Browse Source

Merge branch 'master' of github.com:thingsboard/thingsboard into improvements/dashboard-export

pull/11873/head
ViacheslavKlimov 2 years ago
parent
commit
3124ad360d
  1. 82
      application/src/main/data/json/system/scada_symbols/bottom-right-elbow-connector-hp.svg
  2. 87
      application/src/main/data/json/system/scada_symbols/bottom-tee-connector-hp.svg
  3. 332
      application/src/main/data/json/system/scada_symbols/control-panel-hp.svg
  4. 87
      application/src/main/data/json/system/scada_symbols/cross-connector-hp.svg
  5. 823
      application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg
  6. 823
      application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg
  7. 272
      application/src/main/data/json/system/scada_symbols/filter-hp.svg
  8. 597
      application/src/main/data/json/system/scada_symbols/heat-pump-hp.svg
  9. 174
      application/src/main/data/json/system/scada_symbols/horizontal-connector-hp.svg
  10. 536
      application/src/main/data/json/system/scada_symbols/horizontal-tank-hp.svg
  11. 328
      application/src/main/data/json/system/scada_symbols/horizontal-valve-hp.svg
  12. 82
      application/src/main/data/json/system/scada_symbols/left-bottom-elbow-connector-hp.svg
  13. 87
      application/src/main/data/json/system/scada_symbols/left-tee-connector-hp.svg
  14. 82
      application/src/main/data/json/system/scada_symbols/left-top-elbow-connector-hp.svg
  15. 174
      application/src/main/data/json/system/scada_symbols/long-horizontal-connector-hp.svg
  16. 174
      application/src/main/data/json/system/scada_symbols/long-vertical-connector-hp.svg
  17. 544
      application/src/main/data/json/system/scada_symbols/pool-hp.svg
  18. 352
      application/src/main/data/json/system/scada_symbols/pump-hp.svg
  19. 87
      application/src/main/data/json/system/scada_symbols/right-tee-connector-hp.svg
  20. 636
      application/src/main/data/json/system/scada_symbols/sand-filter-hp.svg
  21. 536
      application/src/main/data/json/system/scada_symbols/short-vertical-tank-hp.svg
  22. 751
      application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg
  23. 82
      application/src/main/data/json/system/scada_symbols/top-right-elbow-connector-hp.svg
  24. 87
      application/src/main/data/json/system/scada_symbols/top-tee-connector-hp.svg
  25. 174
      application/src/main/data/json/system/scada_symbols/vertical-connector-hp.svg
  26. 751
      application/src/main/data/json/system/scada_symbols/vertical-scale-hp.svg
  27. 536
      application/src/main/data/json/system/scada_symbols/vertical-tank-hp.svg
  28. 328
      application/src/main/data/json/system/scada_symbols/vertical-valve-hp.svg
  29. 31
      application/src/main/data/json/system/widget_bundles/general_high_performance_scada_symbols.json
  30. 23
      application/src/main/data/json/system/widget_bundles/high_performance_scada_fluid_system.json
  31. 2
      application/src/main/data/json/system/widget_bundles/scada_fluid_system.json
  32. 153
      application/src/main/data/upgrade/3.8.1/schema_update.sql
  33. 5
      application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
  34. 6
      application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
  35. 52
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  36. 33
      application/src/main/java/org/thingsboard/server/controller/DashboardController.java
  37. 131
      application/src/main/java/org/thingsboard/server/controller/MobileAppBundleController.java
  38. 143
      application/src/main/java/org/thingsboard/server/controller/MobileAppController.java
  39. 29
      application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java
  40. 71
      application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java
  41. 12
      application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java
  42. 83
      application/src/main/java/org/thingsboard/server/service/entitiy/mobile/DefaultTbMobileAppBundleService.java
  43. 24
      application/src/main/java/org/thingsboard/server/service/entitiy/mobile/DefaultTbMobileAppService.java
  44. 32
      application/src/main/java/org/thingsboard/server/service/entitiy/mobile/TbMobileAppBundleService.java
  45. 9
      application/src/main/java/org/thingsboard/server/service/entitiy/mobile/TbMobileAppService.java
  46. 4
      application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
  47. 1
      application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java
  48. 5
      application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java
  49. 4
      application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java
  50. 4
      application/src/main/resources/thingsboard.yml
  51. 169
      application/src/test/java/org/thingsboard/server/controller/MobileAppBundleControllerTest.java
  52. 104
      application/src/test/java/org/thingsboard/server/controller/MobileAppControllerTest.java
  53. 253
      application/src/test/java/org/thingsboard/server/controller/MobileApplicationControllerTest.java
  54. 262
      application/src/test/java/org/thingsboard/server/controller/QrCodeSettingsControllerTest.java
  55. 44
      application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java
  56. 46
      common/dao-api/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleService.java
  57. 19
      common/dao-api/src/main/java/org/thingsboard/server/dao/mobile/MobileAppService.java
  58. 2
      common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java
  59. 3
      common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java
  60. 21
      common/data/src/main/java/org/thingsboard/server/common/data/Views.java
  61. 2
      common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java
  62. 39
      common/data/src/main/java/org/thingsboard/server/common/data/id/MobileAppBundleId.java
  63. 8
      common/data/src/main/java/org/thingsboard/server/common/data/id/QrCodeSettingsId.java
  64. 24
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/LoginMobileInfo.java
  65. 11
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/UserMobileInfo.java
  66. 27
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/UserMobileSessionInfo.java
  67. 24
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/app/MobileApp.java
  68. 25
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/app/MobileAppStatus.java
  69. 49
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/app/MobileAppVersionInfo.java
  70. 13
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/app/StoreInfo.java
  71. 83
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/bundle/MobileAppBundle.java
  72. 63
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/bundle/MobileAppBundleInfo.java
  73. 8
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/bundle/MobileAppBundleOauth2Client.java
  74. 35
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/AbstractMobilePage.java
  75. 42
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/CustomMobilePage.java
  76. 42
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/DashboardPage.java
  77. 43
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/DefaultMobilePage.java
  78. 29
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/DefaultPageId.java
  79. 23
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/MobileLayoutConfig.java
  80. 45
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/MobilePage.java
  81. 24
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/MobilePageType.java
  82. 42
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/WebViewPage.java
  83. 2
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/qrCodeSettings/BadgePosition.java
  84. 3
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/qrCodeSettings/QRCodeConfig.java
  85. 37
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/qrCodeSettings/QrCodeSettings.java
  86. 1
      common/proto/src/main/proto/queue.proto
  87. 13
      common/util/src/main/java/org/thingsboard/common/util/JacksonUtil.java
  88. 120
      dao/src/main/java/org/thingsboard/server/dao/mobile/BaseMobileAppSettingsService.java
  89. 46
      dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleDao.java
  90. 170
      dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleServiceImpl.java
  91. 19
      dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppDao.java
  92. 96
      dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppServiceImpl.java
  93. 12
      dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingService.java
  94. 134
      dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingServiceImpl.java
  95. 10
      dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingsCaffeineCache.java
  96. 6
      dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingsDao.java
  97. 2
      dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingsEvictEvent.java
  98. 10
      dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingsRedisCache.java
  99. 33
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  100. 114
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractMobileAppBundleEntity.java

82
application/src/main/data/json/system/scada_symbols/bottom-right-elbow-connector-hp.svg

@ -0,0 +1,82 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="200" fill="none" version="1.1" viewBox="0 0 200 200"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Bottom right elbow connector",
"description": "Bottom right elbow connector",
"widgetSizeX": 1,
"widgetSizeY": 1,
"tags": [
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"actions": null
}
],
"behavior": [],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": null,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"type": "color",
"default": "#1A1A1A",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<path d="M200 101H131C113.879 101 100 114.879 100 132V201" stroke="#1A1A1A" stroke-width="6" tb:tag="line"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

87
application/src/main/data/json/system/scada_symbols/bottom-tee-connector-hp.svg

@ -0,0 +1,87 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="200" fill="none" version="1.1" viewBox="0 0 200 200"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Bottom tee connector",
"description": "Bottom tee connector",
"widgetSizeX": 1,
"widgetSizeY": 1,
"tags": [
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"actions": null
},
{
"tag": "line-color",
"stateRenderFunction": "element.fill(ctx.properties.lineColor);\nelement.stroke(ctx.properties.lineColor);",
"actions": null
}
],
"behavior": [],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": null,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"type": "color",
"default": "#1A1A1A",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<path d="M200 100H115C106.716 100 100 106.716 100 115V200" stroke="#1A1A1A" stroke-width="6" id="path2" tb:tag="line"/><path d="M0 100H85C93.2843 100 100 106.716 100 115V200" stroke="#1A1A1A" stroke-width="6" id="path4" tb:tag="line"/><path d="M0 100L200 100" stroke="#1A1A1A" stroke-width="6" id="path6" tb:tag="line"/><path d="M87 100H113C113 100 100 100 100 114C100 100 87 100 87 100Z" fill="#1A1A1A" stroke="#1A1A1A" stroke-width="2" id="path8" tb:tag="line-color"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

332
application/src/main/data/json/system/scada_symbols/control-panel-hp.svg

@ -0,0 +1,332 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="400" height="200" fill="none" version="1.1" viewBox="0 0 400 200"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Control panel",
"description": "Control panel",
"searchTags": [
"control"
],
"widgetSizeX": 2,
"widgetSizeY": 1,
"tags": [
{
"tag": "background",
"stateRenderFunction": "element.fill(ctx.properties.background);",
"actions": null
},
{
"tag": "label",
"stateRenderFunction": "if (ctx.properties.label) {\n element.show();\n ctx.api.font(element, ctx.properties.labelFont, ctx.properties.labelColor);\n ctx.api.text(element, ctx.properties.labelText);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "offButton",
"stateRenderFunction": "var offButtonChildren = element.children();\nif(ctx.values.value){\n ctx.api.enable(element);\n offButtonChildren[0].fill(ctx.properties.defaultButtonColor);\n} else {\n ctx.api.disable(element);\n offButtonChildren[0].fill(ctx.properties.activeButtonColor);\n}\n\noffButtonChildren[1].stroke(ctx.properties.borderButtonColor);\n\nif (!ctx.properties.label) {\n element.transform({translateY: -60});\n}",
"actions": {
"click": {
"actionFunction": "ctx.api.disable(element);\nctx.api.callAction(event, 'offUpdateState', undefined, {\n next: () => {\n ctx.api.setValue('value', false);\n },\n error: () => {\n ctx.api.enable(element);\n } \n});"
}
}
},
{
"tag": "offLabel",
"stateRenderFunction": "ctx.api.text(element, ctx.properties.offLabelText);",
"actions": null
},
{
"tag": "onButton",
"stateRenderFunction": "var onButtonChildren = element.children();\nif(ctx.values.value){\n ctx.api.disable(element);\n onButtonChildren[0].fill(ctx.properties.activeButtonColor);\n} else {\n ctx.api.enable(element);\n onButtonChildren[0].fill(ctx.properties.defaultButtonColor);\n}\nonButtonChildren[1].stroke(ctx.properties.borderButtonColor);\nif (!ctx.properties.label) {\n element.transform({translateY: -60});\n}",
"actions": {
"click": {
"actionFunction": "ctx.api.disable(element);\nctx.api.callAction(event, 'onUpdateState', undefined, {\n next: () => {\n ctx.api.setValue('value', true);\n },\n error: () => {\n ctx.api.enable(element);\n } \n});"
}
}
},
{
"tag": "onLabel",
"stateRenderFunction": "ctx.api.text(element, ctx.properties.onLabelText);",
"actions": null
}
],
"behavior": [
{
"id": "value",
"name": "On/Off state",
"hint": null,
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": "On",
"falseLabel": "Off",
"stateLabel": "On",
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "value"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "onUpdateState",
"name": "On update state",
"hint": null,
"group": null,
"type": "action",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": {
"action": "SET_ATTRIBUTE",
"executeRpc": {
"method": "setState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"setAttribute": {
"scope": "SHARED_SCOPE",
"key": "value"
},
"putTimeSeries": {
"key": "state"
},
"valueToData": {
"type": "CONSTANT",
"constantValue": true,
"valueToDataFunction": "/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;"
}
},
"defaultWidgetActionSettings": null
},
{
"id": "offUpdateState",
"name": "Off update state",
"hint": null,
"group": null,
"type": "action",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": {
"action": "SET_ATTRIBUTE",
"executeRpc": {
"method": "setState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"setAttribute": {
"scope": "SHARED_SCOPE",
"key": "value"
},
"putTimeSeries": {
"key": "state"
},
"valueToData": {
"type": "CONSTANT",
"constantValue": false,
"valueToDataFunction": "/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;"
}
},
"defaultWidgetActionSettings": null
}
],
"properties": [
{
"id": "background",
"name": "{i18n:scada.symbol.background-color}",
"type": "color",
"default": "#DEDEDE",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "label",
"name": "{i18n:scada.symbol.label}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "labelText",
"name": "{i18n:scada.symbol.label}",
"type": "text",
"default": "Heat pump",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "flex",
"min": null,
"max": null,
"step": null
},
{
"id": "labelFont",
"name": "{i18n:scada.symbol.label}",
"type": "font",
"default": {
"size": 56,
"sizeUnit": "px",
"family": "Roboto",
"weight": "500",
"style": "normal"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "labelColor",
"name": "{i18n:scada.symbol.label}",
"type": "color",
"default": "#000000DE",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "onLabelText",
"name": "{i18n:scada.symbol.on-label}",
"type": "text",
"default": "On",
"required": true,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "flex",
"min": null,
"max": null,
"step": null
},
{
"id": "offLabelText",
"name": "{i18n:scada.symbol.off-label}",
"type": "text",
"default": "Off",
"required": true,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "flex",
"min": null,
"max": null,
"step": null
},
{
"id": "activeButtonColor",
"name": "{i18n:scada.symbol.button-color}",
"type": "color",
"default": "#999999",
"required": null,
"subLabel": "Active",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "defaultButtonColor",
"name": "{i18n:scada.symbol.button-color}",
"type": "color",
"default": "#DEDEDE",
"required": null,
"subLabel": "Default",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "borderButtonColor",
"name": "{i18n:scada.symbol.button-color}",
"type": "color",
"default": "#999999",
"required": null,
"subLabel": "Border",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<rect width="400" height="200" rx="8" fill="#dedede" tb:tag="background"/><rect x="1" y="1" width="398" height="198" rx="7" stroke="#000" stroke-opacity=".87" stroke-width="2"/><text x="198.7832" y="57.511719" fill="#000000" font-family="Roboto" font-size="56px" font-weight="400" text-anchor="middle" tb:tag="label" xml:space="preserve"><tspan dominant-baseline="middle">Heat pump</tspan></text><g transform="translate(-11 -36.996)" tb:tag="onButton">
<rect x="40" y="137" width="156" height="72" rx="9.8143" fill="#999" style="stroke-width:.95761"/>
<path d="m40.957 146.03c0-4.4589 3.9482-8.0734 8.8189-8.0734h136.45c4.8707 0 8.8189 3.6144 8.8189 8.0734v53.938c0 4.4589-3.9482 8.0743-8.8189 8.0743h-136.45c-4.8707 0-8.8189-3.6153-8.8189-8.0743z" shape-rendering="crispEdges" stroke="#999" stroke-width="1.9147"/>
<text x="118.48975" y="176.07759" fill="#000000" font-family="Roboto" font-size="34px" font-weight="500" text-anchor="middle" tb:tag="onLabel" xml:space="preserve"><tspan dominant-baseline="middle">On</tspan></text>
</g><g transform="translate(-6,-36.996)" tb:tag="offButton">
<rect x="220" y="137" width="156" height="72" rx="9.8143" fill="#dedede" style="stroke-width:.95761"/>
<path d="m220.96 146.03c0-4.4589 3.9482-8.0734 8.8189-8.0734h136.45c4.8707 0 8.8189 3.6144 8.8189 8.0734v53.938c0 4.4589-3.9482 8.0743-8.8189 8.0743h-136.45c-4.8707 0-8.8189-3.6153-8.8189-8.0743z" shape-rendering="crispEdges" stroke="#999" stroke-width="1.9147"/>
<text x="296.72168" y="176.37642" fill="#000000" font-family="Roboto" font-size="34px" font-weight="500" text-anchor="middle" tb:tag="offLabel" xml:space="preserve"><tspan dominant-baseline="middle">Off</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

87
application/src/main/data/json/system/scada_symbols/cross-connector-hp.svg

@ -0,0 +1,87 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="200" fill="none" version="1.1" viewBox="0 0 200 200"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Cross connector",
"description": "Cross connector",
"widgetSizeX": 1,
"widgetSizeY": 1,
"tags": [
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"actions": null
},
{
"tag": "line-color",
"stateRenderFunction": "element.fill(ctx.properties.lineColor);",
"actions": null
}
],
"behavior": [],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": null,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"type": "color",
"default": "#1A1A1A",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<path d="M87 100H113C113 100 100 100 100 86C100 100 87 100 87 100Z" id="path12" fill="#1A1A1A" tb:tag="line-color"/><path d="M87 100H113C113 100 100 100 100 114C100 100 87 100 87 100Z" id="path10" fill="#1A1A1A" tb:tag="line-color"/><path d="M0 100H85C93.2843 100 100 93.2843 100 85V0" stroke-width="6" id="path8" stroke="#1A1A1A" tb:tag="line"/><path d="M200 100H115C106.716 100 100 93.2843 100 85V0" stroke-width="6" id="path6" stroke="#1A1A1A" tb:tag="line"/><path d="M0 100H85C93.2843 100 100 106.716 100 115V200" stroke-width="6" id="path4" stroke="#1A1A1A" tb:tag="line"/><path d="M200 100H115C106.716 100 100 106.716 100 115V200" stroke-width="6" id="path2" stroke="#1A1A1A" tb:tag="line"/>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

823
application/src/main/data/json/system/scada_symbols/dynamic-horizontal-scale-hp.svg

@ -0,0 +1,823 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="800" height="400" fill="none" version="1.1" viewBox="0 0 800 400"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Dynamic horizontal scale",
"description": "Dynamic horizontal scale with current volume value and dynamically configurable warning and critical scale.",
"searchTags": [
"scale"
],
"widgetSizeX": 4,
"widgetSizeY": 2,
"tags": [
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "highCriticalScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalValue = ctx.values.highCriticalState;\nif (showHighCriticalScale && highCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(highCriticalValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(653-offset);\n childrenElement[1].height(653-offset-1);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "highCriticalScaleBackground",
"stateRenderFunction": "var value = ctx.values.value;\nvar showHighCriticalState = ctx.properties.showHighCriticalScale;\nvar highCriticalScale = ctx.values.highCriticalState;\n\nif (showHighCriticalState && value !== null && highCriticalScale !== null) {\n if (value >= highCriticalScale && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"actions": null
},
{
"tag": "highWarningScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningValue = ctx.values.highWarningState;\nif (showHighWarningScale && highWarningValue !== null) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(653-offset);\n childrenElement[1].height(653-offset-1);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "highWarningScaleBackground",
"stateRenderFunction": "var value = ctx.values.value;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\n\nif (showHighWarningScale && value !== null) {\n var highWarningScale = ctx.values.highWarningState;\n var highCriticalScale = ctx.values.highCriticalState;\n if (!showHighCriticalScale) {\n highCriticalScale = ctx.properties.maxValue;\n }\n \n if (highWarningScale !== null && highCriticalScale !== null) {\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}",
"actions": null
},
{
"tag": "label",
"stateRenderFunction": "if (ctx.properties.label) {\n element.show();\n ctx.api.font(element, ctx.properties.labelFont, ctx.properties.labelColor);\n ctx.api.text(element, ctx.properties.labelText);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "lowCriticalScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.values.lowCriticalState;\n\nif (showLowCriticalScale && lowCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(offset);\n childrenElement[1].height(offset-1);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "lowCriticalScaleBackground",
"stateRenderFunction": "var value = ctx.values.value;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\n\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"actions": null
},
{
"tag": "lowWarningScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar lowWarningValue = ctx.values.lowWarningState;\nif (showLowWarningScale && lowWarningValue !== null) {\n element.show();\n var offset = calculateOffset(lowWarningValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(offset);\n childrenElement[1].height(offset-1);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "lowWarningScaleBackground",
"stateRenderFunction": "var value = ctx.values.value;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\n\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n var lowWarningScale = ctx.values.lowWarningState;\n if (!showLowCriticalScale) {\n lowCriticalScale = ctx.properties.minValue;\n }\n if (lowCriticalScale !== null && lowWarningScale !== null) {\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}",
"actions": null
},
{
"tag": "maxValue",
"stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.properties.maxValue);\n}",
"actions": null
},
{
"tag": "minMaxValue",
"stateRenderFunction": "if (ctx.properties.minMaxValue) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "minValue",
"stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.properties.minValue);\n}",
"actions": null
},
{
"tag": "scaleBackground",
"stateRenderFunction": "element.fill(ctx.properties.scaleColor);",
"actions": null
},
{
"tag": "target",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showTarget = ctx.properties.showTarget;\nvar targetValue = ctx.values.targetValue;\n\nif (showTarget && targetValue !== null) {\n element.show();\n var offset = calculateOffset(targetValue, minValue, maxValue);\n element.transform({translateX: -(Math.min(Math.max(offset-17, 0), 617))});\n} else {\n element.hide();\n}\n\n",
"actions": null
},
{
"tag": "targetBackground",
"stateRenderFunction": "element.fill(ctx.properties.targetColor);",
"actions": null
},
{
"tag": "units",
"stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.properties.units);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "value",
"stateRenderFunction": "if (ctx.properties.value !== null) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, null, ctx.properties.valueDecimals));\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "valuePointer",
"stateRenderFunction": "element.fill(ctx.properties.arrowColor);",
"actions": null
},
{
"tag": "valuePosition",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar value = ctx.values.value;\n\nif (value !== null) {\n var offset = calculateOffset(value, minValue, maxValue);\n element.transform({translateX: -offset});\n}\n",
"actions": null
}
],
"behavior": [
{
"id": "value",
"name": "{i18n:scada.symbol.value}",
"hint": "{i18n:scada.symbol.value-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "GET_TIME_SERIES",
"defaultValue": null,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "temperature"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "highCriticalState",
"name": "{i18n:scada.symbol.high-critical-state}",
"hint": "{i18n:scada.symbol.high-critical-state-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 85,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SERVER_SCOPE",
"key": "highCriticalState"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "highWarningState",
"name": "{i18n:scada.symbol.high-warning-state}",
"hint": "{i18n:scada.symbol.high-warning-state-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 70,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SERVER_SCOPE",
"key": "highWarningState"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "lowWarningState",
"name": "{i18n:scada.symbol.low-warning-state}",
"hint": "{i18n:scada.symbol.low-warning-state-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 30,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SERVER_SCOPE",
"key": "lowWarningState"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "lowCriticalState",
"name": "{i18n:scada.symbol.low-critical-state}",
"hint": "{i18n:scada.symbol.low-critical-state-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 15,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SERVER_SCOPE",
"key": "lowCriticalState"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "targetValue",
"name": "{i18n:scada.symbol.target-value}",
"hint": "{i18n:scada.symbol.target-value-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": null,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SERVER_SCOPE",
"key": "target"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
}
],
"properties": [
{
"id": "minMaxValue",
"name": "{i18n:scada.symbol.min-max-value}",
"type": "switch",
"default": false,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "minValue",
"name": "{i18n:scada.symbol.min-max-value}",
"type": "number",
"default": 0,
"required": true,
"subLabel": "{i18n:scada.symbol.min-value}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": 1
},
{
"id": "maxValue",
"name": "{i18n:scada.symbol.min-max-value}",
"type": "number",
"default": 100,
"required": true,
"subLabel": "{i18n:scada.symbol.max-value}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": 1
},
{
"id": "value",
"name": "{i18n:scada.symbol.value}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "valueDecimals",
"name": "{i18n:scada.symbol.value}",
"type": "number",
"default": 0,
"required": true,
"subLabel": "Decimals",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "value",
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 10,
"step": 1
},
{
"id": "valueFont",
"name": "{i18n:scada.symbol.value}",
"type": "font",
"default": {
"size": 60,
"sizeUnit": "px",
"family": "Roboto",
"weight": "400",
"style": "normal"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "value",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "valueColor",
"name": "{i18n:scada.symbol.value}",
"type": "color",
"default": "#002878",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "value",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "label",
"name": "{i18n:scada.symbol.label}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "labelText",
"name": "{i18n:scada.symbol.label}",
"type": "text",
"default": "Outdoor",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "label",
"rowClass": "",
"fieldClass": "flex",
"min": null,
"max": null,
"step": null
},
{
"id": "labelFont",
"name": "{i18n:scada.symbol.label}",
"type": "font",
"default": {
"size": 56,
"sizeUnit": "px",
"family": "Roboto",
"weight": "500",
"style": "normal"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "label",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "labelColor",
"name": "{i18n:scada.symbol.label}",
"type": "color",
"default": "#000000DE",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "label",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "showUnits",
"name": "{i18n:scada.symbol.units}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "units",
"name": "{i18n:scada.symbol.units}",
"type": "units",
"default": "°C",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "showUnits",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "unitsFont",
"name": "{i18n:scada.symbol.units}",
"type": "font",
"default": {
"size": 56,
"sizeUnit": "px",
"family": "Roboto",
"weight": "500",
"style": "normal"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "showUnits",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "unitsColor",
"name": "{i18n:scada.symbol.units}",
"type": "color",
"default": "#000000DE",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "showUnits",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "arrowColor",
"name": "{i18n:scada.symbol.arrow-color}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "showTarget",
"name": "{i18n:scada.symbol.target}",
"type": "switch",
"default": false,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "targetColor",
"name": "{i18n:scada.symbol.target}",
"type": "color",
"default": "#DEDEDE",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "showTarget",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "showHighCriticalScale",
"name": "{i18n:scada.symbol.show-high-critical-scale}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "showHighWarningScale",
"name": "{i18n:scada.symbol.show-high-warning-scale}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "showLowWarningScale",
"name": "{i18n:scada.symbol.show-low-warning-scale}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "showLowCriticalScale",
"name": "{i18n:scada.symbol.show-low-critical-scale}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "scaleColor",
"name": "{i18n:scada.symbol.scale-color}",
"type": "color",
"default": "#C8DFF7",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "defaultWarningScaleColor",
"name": "{i18n:scada.symbol.warning-scale-color}",
"type": "color",
"default": "#EBEBEB",
"required": null,
"subLabel": "Default",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "activeWarningScaleColor",
"name": "{i18n:scada.symbol.warning-scale-color}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": "Active",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "defaultCriticalScaleColor",
"name": "{i18n:scada.symbol.critical-scale-color}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": "Default",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "activeCriticalScaleColor",
"name": "{i18n:scada.symbol.critical-scale-color}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": "Active",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<text x="409.16602" y="113.23438" fill="#000000" font-family="Roboto" font-size="56px" font-weight="400" text-anchor="middle" tb:tag="label" xml:space="preserve"><tspan transform="translate(0,-144)" dominant-baseline="middle">Outdoor</tspan></text><text x="765.21875" y="225.25" fill="#000000" font-family="Roboto" font-size="56px" font-weight="400" text-anchor="middle" tb:tag="units" xml:space="preserve"><tspan transform="translate(0,-144)" dominant-baseline="middle">°C</tspan></text><g fill="#666" font-family="Roboto" font-size="32px" font-weight="500" text-anchor="middle" style="display: none;" tb:tag="minMaxValue">
<text x="100.02344" y="268" tb:tag="maxValue" xml:space="preserve" style=""><tspan dominant-baseline="middle">100</tspan></text>
<text x="715.07031" y="268" tb:tag="minValue" xml:space="preserve" style=""><tspan dominant-baseline="middle">0</tspan></text>
</g><g transform="translate(-350)" tb:tag="valuePosition">
<path d="m724 191 24-42h-48z" fill="#666" tb:tag="valuePointer"/>
<text x="723.7793" y="295.82501" fill="#002878" font-family="Roboto" font-size="60px" font-weight="400" text-anchor="middle" tb:tag="value" xml:space="preserve"><tspan transform="translate(0,-144)" dominant-baseline="middle">26</tspan></text>
</g><g tb:tag="scale">
<rect transform="rotate(-90)" x="-242" y="72" width="42" height="653" fill="#c8dff7" style="stroke-width:.9497" tb:tag="scaleBackground"/>
<rect transform="rotate(-90)" x="-241.52" y="72.475" width="41.05" height="652.05" stroke="#000" stroke-opacity=".87" style="stroke-width:.95024"/>
<g tb:tag="highWarningScale">
<rect transform="rotate(-90)" x="-242" y="72" width="42" height="153.12" fill="#ebebeb" style="stroke-width:.94905" tb:tag="highWarningScaleBackground"/>
<rect transform="rotate(-90)" x="-241.53" y="72.475" width="41.051" height="152.17" stroke="#000" stroke-opacity=".87" style="stroke-width:.94948"/>
</g>
<g tb:tag="highCriticalScale">
<rect transform="rotate(-90)" x="-242" y="72" width="42" height="81.062" fill="#666" style="stroke-width:.94905" tb:tag="highCriticalScaleBackground"/>
<rect transform="rotate(-90)" x="-241.53" y="72.475" width="41.051" height="80.113" stroke="#000" stroke-opacity=".87" style="stroke-width:.94934"/>
</g>
<g transform="rotate(90 700.5 265.5)" tb:tag="lowWarningScale">
<rect transform="scale(-1,1)" x="-677" y="241" width="42" height="153.12" fill="#ebebeb" style="stroke-width:.94905" tb:tag="lowWarningScaleBackground"/>
<rect transform="scale(-1,1)" x="-676.53" y="241.47" width="41.051" height="152.17" stroke="#000" stroke-opacity=".87" style="stroke-width:.94948"/>
</g>
<g transform="rotate(90 700.5 265.5)" tb:tag="lowCriticalScale">
<rect transform="scale(-1,1)" x="-677" y="241" width="42" height="81.062" fill="#666" style="stroke-width:.94905" tb:tag="lowCriticalScaleBackground"/>
<rect transform="scale(-1,1)" x="-676.53" y="241.48" width="41.051" height="80.113" stroke="#000" stroke-opacity=".87" style="stroke-width:.94934"/>
</g>
</g><g transform="translate(-180)" tb:tag="target">
<rect transform="rotate(45)" x="644.15" y="-355.65" width="24" height="24" fill="#dedede" tb:tag="targetBackground"/>
<rect transform="rotate(45)" x="645.15" y="-354.65" width="22" height="22" stroke="#000" stroke-opacity=".87" stroke-width="2"/>
</g><path d="m269.06 0s-269.06 0-269.06 67v328.36c0 2.6512 7.1634 4.6404 16 4.6404h768c8.836 0 16-1.9892 16-4.6404v-328.36c0-67-264.28-67-264.28-67h-135.72zm268.28 81.2c-5.1546 0-9.3334 1.2536-9.3334 2.8v300.4c0 1.5464 4.1786 2.8 9.3334 2.8h58.666c5.1546 0 9.3334-1.2536 9.3334-2.8v-300.4c0-1.5464-4.1786-2.8-9.3334-2.8z" fill="#000" fill-opacity="0" style="stroke-width:.7303" tb:tag="clickArea"/>
</svg>

After

Width:  |  Height:  |  Size: 30 KiB

823
application/src/main/data/json/system/scada_symbols/dynamic-vertical-scale-hp.svg

@ -0,0 +1,823 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="400" height="800" fill="none" version="1.1" viewBox="0 0 400 800"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Dynamic vertical scale",
"description": "Dynamic vertical scale with current volume value and dynamically configurable warning and critical scale.",
"searchTags": [
"scale"
],
"widgetSizeX": 2,
"widgetSizeY": 4,
"tags": [
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "highCriticalScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar highCriticalValue = ctx.values.highCriticalState;\nif (showHighCriticalScale && highCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(highCriticalValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(653-offset);\n childrenElement[1].height(653-offset-1);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "highCriticalScaleBackground",
"stateRenderFunction": "var value = ctx.values.value;\nvar showHighCriticalState = ctx.properties.showHighCriticalScale;\nvar highCriticalScale = ctx.values.highCriticalState;\n\nif (showHighCriticalState && value !== null && highCriticalScale !== null) {\n if (value >= highCriticalScale && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"actions": null
},
{
"tag": "highWarningScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningValue = ctx.values.highWarningState;\nif (showHighWarningScale && highWarningValue !== null) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(653-offset);\n childrenElement[1].height(653-offset-1);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "highWarningScaleBackground",
"stateRenderFunction": "var value = ctx.values.value;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\n\nif (showHighWarningScale && value !== null) {\n var highWarningScale = ctx.values.highWarningState;\n var highCriticalScale = ctx.values.highCriticalState;\n if (!showHighCriticalScale) {\n highCriticalScale = ctx.properties.maxValue;\n }\n \n if (highWarningScale !== null && highCriticalScale !== null) {\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}",
"actions": null
},
{
"tag": "label",
"stateRenderFunction": "if (ctx.properties.label) {\n element.show();\n ctx.api.font(element, ctx.properties.labelFont, ctx.properties.labelColor);\n ctx.api.text(element, ctx.properties.labelText);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "lowCriticalScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.values.lowCriticalState;\n\nif (showLowCriticalScale && lowCriticalValue !== null) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(offset);\n childrenElement[1].height(offset-1);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "lowCriticalScaleBackground",
"stateRenderFunction": "var value = ctx.values.value;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\n\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"actions": null
},
{
"tag": "lowWarningScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar lowWarningValue = ctx.values.lowWarningState;\nif (showLowWarningScale && lowWarningValue !== null) {\n element.show();\n var offset = calculateOffset(lowWarningValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(offset);\n childrenElement[1].height(offset-1);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "lowWarningScaleBackground",
"stateRenderFunction": "var value = ctx.values.value;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\n\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.values.lowCriticalState;\n var lowWarningScale = ctx.values.lowWarningState;\n if (!showLowCriticalScale) {\n lowCriticalScale = ctx.properties.minValue;\n }\n if (lowCriticalScale !== null && lowWarningScale !== null) {\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n }\n}",
"actions": null
},
{
"tag": "maxValue",
"stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.properties.maxValue);\n}",
"actions": null
},
{
"tag": "minMaxValue",
"stateRenderFunction": "if (ctx.properties.minMaxValue) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "minValue",
"stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.properties.minValue);\n}",
"actions": null
},
{
"tag": "scaleBackground",
"stateRenderFunction": "element.fill(ctx.properties.scaleColor);",
"actions": null
},
{
"tag": "target",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showTarget = ctx.properties.showTarget;\nvar targetValue = ctx.values.targetValue;\n\nif (showTarget && targetValue !== null) {\n element.show();\n var offset = calculateOffset(targetValue, minValue, maxValue);\n element.transform({translateY: -(Math.min(Math.max(offset-17, 0), 617))});\n} else {\n element.hide();\n}\n\n",
"actions": null
},
{
"tag": "targetBackground",
"stateRenderFunction": "element.fill(ctx.properties.targetColor);",
"actions": null
},
{
"tag": "units",
"stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.properties.units);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "value",
"stateRenderFunction": "if (ctx.properties.value !== null) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, null, ctx.properties.valueDecimals));\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "valuePointer",
"stateRenderFunction": "element.fill(ctx.properties.arrowColor);",
"actions": null
},
{
"tag": "valuePosition",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar value = ctx.values.value;\n\nif (value !== null) {\n var offset = calculateOffset(value, minValue, maxValue);\n element.transform({translateY: -offset});\n}\n",
"actions": null
}
],
"behavior": [
{
"id": "value",
"name": "{i18n:scada.symbol.value}",
"hint": "{i18n:scada.symbol.value-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "GET_TIME_SERIES",
"defaultValue": null,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "temperature"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "highCriticalState",
"name": "{i18n:scada.symbol.high-critical-state}",
"hint": "{i18n:scada.symbol.high-critical-state-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 85,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SERVER_SCOPE",
"key": "highCriticalState"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "highWarningState",
"name": "{i18n:scada.symbol.high-warning-state}",
"hint": "{i18n:scada.symbol.high-warning-state-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 70,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SERVER_SCOPE",
"key": "highWarningState"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "lowWarningState",
"name": "{i18n:scada.symbol.low-warning-state}",
"hint": "{i18n:scada.symbol.low-warning-state-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 30,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SERVER_SCOPE",
"key": "lowWarningState"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "lowCriticalState",
"name": "{i18n:scada.symbol.low-critical-state}",
"hint": "{i18n:scada.symbol.low-critical-state-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": 15,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SERVER_SCOPE",
"key": "lowCriticalState"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "targetValue",
"name": "{i18n:scada.symbol.target-value}",
"hint": "{i18n:scada.symbol.target-value-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": null,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SERVER_SCOPE",
"key": "target"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
}
],
"properties": [
{
"id": "minMaxValue",
"name": "{i18n:scada.symbol.min-max-value}",
"type": "switch",
"default": false,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "minValue",
"name": "{i18n:scada.symbol.min-max-value}",
"type": "number",
"default": 0,
"required": true,
"subLabel": "{i18n:scada.symbol.min-value}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": 1
},
{
"id": "maxValue",
"name": "{i18n:scada.symbol.min-max-value}",
"type": "number",
"default": 100,
"required": true,
"subLabel": "{i18n:scada.symbol.max-value}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": 1
},
{
"id": "value",
"name": "{i18n:scada.symbol.value}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "valueDecimals",
"name": "{i18n:scada.symbol.value}",
"type": "number",
"default": 0,
"required": true,
"subLabel": "Decimals",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 10,
"step": 1
},
{
"id": "valueFont",
"name": "{i18n:scada.symbol.value}",
"type": "font",
"default": {
"size": 60,
"sizeUnit": "px",
"family": "Roboto",
"weight": "400",
"style": "normal"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "valueColor",
"name": "{i18n:scada.symbol.value}",
"type": "color",
"default": "#002878",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "label",
"name": "{i18n:scada.symbol.label}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "labelText",
"name": "{i18n:scada.symbol.label}",
"type": "text",
"default": "Outdoor",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "flex",
"min": null,
"max": null,
"step": null
},
{
"id": "labelFont",
"name": "{i18n:scada.symbol.label}",
"type": "font",
"default": {
"size": 56,
"sizeUnit": "px",
"family": "Roboto",
"weight": "500",
"style": "normal"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "labelColor",
"name": "{i18n:scada.symbol.label}",
"type": "color",
"default": "#000000DE",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "showUnits",
"name": "{i18n:scada.symbol.units}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "units",
"name": "{i18n:scada.symbol.units}",
"type": "units",
"default": "°C",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "unitsFont",
"name": "{i18n:scada.symbol.units}",
"type": "font",
"default": {
"size": 56,
"sizeUnit": "px",
"family": "Roboto",
"weight": "500",
"style": "normal"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "unitsColor",
"name": "{i18n:scada.symbol.units}",
"type": "color",
"default": "#000000DE",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "arrowColor",
"name": "{i18n:scada.symbol.arrow-color}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "showTarget",
"name": "{i18n:scada.symbol.target}",
"type": "switch",
"default": false,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "targetColor",
"name": "{i18n:scada.symbol.target}",
"type": "color",
"default": "#DEDEDE",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "showTarget",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "showHighCriticalScale",
"name": "{i18n:scada.symbol.show-high-critical-scale}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "showHighWarningScale",
"name": "{i18n:scada.symbol.show-high-warning-scale}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "showLowWarningScale",
"name": "{i18n:scada.symbol.show-low-warning-scale}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "showLowCriticalScale",
"name": "{i18n:scada.symbol.show-low-critical-scale}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "scaleColor",
"name": "{i18n:scada.symbol.scale-color}",
"type": "color",
"default": "#C8DFF7",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "defaultWarningScaleColor",
"name": "{i18n:scada.symbol.warning-scale-color}",
"type": "color",
"default": "#EBEBEB",
"required": null,
"subLabel": "Default",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "activeWarningScaleColor",
"name": "{i18n:scada.symbol.warning-scale-color}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": "Active",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "defaultCriticalScaleColor",
"name": "{i18n:scada.symbol.critical-scale-color}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": "Default",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "activeCriticalScaleColor",
"name": "{i18n:scada.symbol.critical-scale-color}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": "Active",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<text x="206.16602" y="43.234375" fill="#000000" font-family="Roboto" font-size="56px" font-weight="400" text-anchor="middle" tb:tag="label" xml:space="preserve"><tspan transform="translate(0,-144)" dominant-baseline="middle">Outdoor</tspan></text><text x="195.21875" y="774.25" fill="#000000" font-family="Roboto" font-size="56px" font-weight="400" text-anchor="middle" tb:tag="units" xml:space="preserve"><tspan transform="translate(0,-144)" dominant-baseline="middle">°C</tspan></text><g tb:tag="scale">
<rect x="176" y="83" width="42" height="652" fill="#c8dff7" tb:tag="scaleBackground"/>
<rect x="176.5" y="83.5" width="41" height="651" stroke="#000" stroke-opacity=".87"/>
<g tb:tag="lowWarningScale">
<rect transform="scale(1,-1)" x="176" y="-736.5" width="42" height="170" fill="#ebebeb" tb:tag="lowWarningScaleBackground"/>
<rect transform="scale(1,-1)" x="176.5" y="-736" width="41" height="169" stroke="#000" stroke-opacity=".87"/>
</g>
<g tb:tag="highWarningScale">
<rect x="176" y="83" width="42" height="170" fill="#ebebeb" tb:tag="highWarningScaleBackground"/>
<rect x="176.5" y="83.5" width="41" height="169" stroke="#000" stroke-opacity=".87"/>
</g>
<g tb:tag="lowCriticalScale">
<rect transform="scale(1,-1)" x="176" y="-736.5" width="42" height="90" fill="#666" tb:tag="lowCriticalScaleBackground"/>
<rect transform="scale(1,-1)" x="176.5" y="-736" width="41" height="89" stroke="#000" stroke-opacity=".87"/>
</g>
<g tb:tag="highCriticalScale">
<rect x="176" y="83" width="42" height="90" fill="#666" tb:tag="highCriticalScaleBackground"/>
<rect x="176.5" y="83.5" width="41" height="89" stroke="#000" stroke-opacity=".87"/>
</g>
</g><g font-family="Roboto" font-size="32px" font-weight="500" text-anchor="middle" style="display: none;" tb:tag="minMaxValue">
<text x="260.02344" y="97" fill="#666" style="" tb:tag="maxValue" xml:space="preserve"><tspan dominant-baseline="middle">100</tspan></text>
<text x="239.07031" y="727" fill="#666666" style="" tb:tag="minValue" xml:space="preserve"><tspan dominant-baseline="middle">0</tspan></text>
</g><g transform="translate(0,-350)" tb:tag="valuePosition">
<path d="m168 734-42-24v48z" fill="#666" tb:tag="valuePointer"/>
<text x="262.53329" y="738.125" fill="#002878" font-family="Roboto" font-size="60px" font-weight="400" text-anchor="middle" tb:tag="value" xml:space="preserve"><tspan transform="translate(0,-144)" dominant-baseline="middle">26</tspan></text>
</g><g transform="translate(0,-440)" tb:tag="target">
<rect transform="rotate(-45)" x="-379.7" y="634.25" width="24" height="24" fill="#dedede" tb:tag="targetBackground"/>
<rect transform="rotate(-45)" x="-378.7" y="635.25" width="22" height="22" stroke="#000" stroke-opacity=".87" stroke-width="2"/>
</g><path d="m134.53 0s-134.53 0-134.53 134v656.72c0 5.3024 3.5817 9.2808 8 9.2808h384c4.418 0 8-3.9784 8-9.2808v-656.72c0-134-132.14-134-132.14-134h-67.86zm134.14 162.4c-2.5773 0-4.6667 2.5072-4.6667 5.6v600.8c0 3.0928 2.0893 5.6 4.6667 5.6h29.333c2.5773 0 4.6667-2.5072 4.6667-5.6v-600.8c0-3.0928-2.0893-5.6-4.6667-5.6z" fill="#000" fill-opacity="0" style="stroke-width:.7303" tb:tag="clickArea"/>
</svg>

After

Width:  |  Height:  |  Size: 30 KiB

272
application/src/main/data/json/system/scada_symbols/filter-hp.svg

@ -0,0 +1,272 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="400" fill="none" version="1.1" viewBox="0 0 200 400"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Filter",
"description": "Filter",
"searchTags": [
"filter"
],
"widgetSizeX": 1,
"widgetSizeY": 2,
"tags": [
{
"tag": "background",
"stateRenderFunction": "element.attr({fill: ctx.properties.runningColor});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "warning",
"name": "{i18n:scada.symbol.warning-state}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "critical",
"name": "{i18n:scada.symbol.critical-state}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "criticalAnimation",
"name": "{i18n:scada.symbol.critical-state-animation}",
"hint": "{i18n:scada.symbol.critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warningClick",
"name": "{i18n:scada.symbol.warning-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
},
{
"id": "criticalClick",
"name": "{i18n:scada.symbol.critical-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
}
],
"properties": [
{
"id": "runningColor",
"name": "{i18n:scada.symbol.running-color}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.warning-color}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.critical-color}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<path d="m18.383 177.21c-0.0635-1.09-0.5693-2.106-1.4002-2.815l-9.0297-7.696c-4.2584-3.63-6.8043-8.844-6.9791-14.436-0.65043-20.804-1.9643-74.241 0.29843-110.76 0.77895-12.572 2.4293-30.36 3.1494-37.9 0.19581-2.0502 1.9178-3.6002 3.9773-3.6002h183.2c2.059 0 3.781 1.5501 3.977 3.6002 0.72 7.5392 2.371 25.328 3.15 37.9 2.262 36.518 0.948 89.955 0.298 110.76-0.175 5.592-2.721 10.806-6.979 14.436l-9.03 7.696c-0.831 0.709-1.336 1.725-1.4 2.815l-8.57 147.01c-0.605 10.385-5.508 20.049-13.531 26.67-34.554 28.516-84.478 28.516-119.03 0-8.0233-6.621-12.926-16.285-13.532-26.67l-8.5695-147.01z" fill="#fff" tb:tag="background"/><path d="m19.381 177.15c-0.0794-1.362-0.7114-2.633-1.7498-3.518l-9.0297-7.697c-4.0475-3.45-6.4624-8.4-6.6283-13.706-0.65057-20.807-1.9624-74.2 0.297-110.67 0.77778-12.553 2.4266-30.326 3.1467-37.866 0.1463-1.5318 1.4324-2.6953 2.9818-2.6953h183.2c1.549 0 2.835 1.1635 2.982 2.6953 0.72 7.5402 2.369 25.314 3.146 37.866 2.26 36.465 0.948 89.858 0.297 110.67-0.165 5.306-2.58 10.256-6.628 13.706l-9.029 7.697c-1.039 0.885-1.671 2.156-1.75 3.518l-8.57 147.01c-0.589 10.108-5.361 19.513-13.17 25.958-34.184 28.21-83.574 28.21-117.76 0-7.8088-6.445-12.581-15.85-13.17-25.958l-8.5695-147.01z" stroke="#000" stroke-opacity=".87" stroke-width="2"/><path d="m67.264 0s-67.264 0-67.264 67v328.36c0 2.6512 1.7909 4.6404 4 4.6404h192c2.209 0 4-1.9892 4-4.6404v-328.36c0-67-66.07-67-66.07-67h-33.929zm67.07 81.2c-1.2887 0-2.3333 1.2536-2.3333 2.8v300.4c0 1.5464 1.0447 2.8 2.3333 2.8h14.666c1.2887 0 2.3333-1.2536 2.3333-2.8v-300.4c0-1.5464-1.0447-2.8-2.3333-2.8z" fill="#000" fill-opacity="0" style="stroke-width:1.4142" tb:tag="clickArea"/><g transform="translate(0,316)" fill="#d12730" style="display: none;" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><g transform="translate(0,316)" fill="#faa405" style="display: none;" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

597
application/src/main/data/json/system/scada_symbols/heat-pump-hp.svg

@ -0,0 +1,597 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="800" height="600" fill="none" version="1.1" viewBox="0 0 800 600"><tb:metadata xmlns=""><![CDATA[{
"title": "Heat pump",
"description": "Heat pump",
"searchTags": [
"pump"
],
"widgetSizeX": 4,
"widgetSizeY": 3,
"stateRenderFunction": "var levelUpButton = ctx.tags.levelUpButton;\nvar levelDownButton = ctx.tags.levelDownButton;\n\nvar enabledColor = ctx.properties.runningColor;\nvar disabledColor = ctx.properties.stoppedColor;\n\nvar temperature = ctx.values.temperature;\nvar running = ctx.values.running;\nvar minTemperature = ctx.properties.minTemperature;\nvar maxTemperature = ctx.properties.maxTemperature;\n\nvar levelUpEnabled = running && temperature < maxTemperature;\nvar levelDownEnabled = running && temperature > minTemperature;\n\nif (levelUpEnabled) {\n ctx.api.enable(levelUpButton);\n levelUpButton[0].attr({fill: enabledColor});\n} else {\n ctx.api.disable(levelUpButton);\n levelUpButton[0].attr({fill: disabledColor});\n}\n \nif (levelDownEnabled) {\n ctx.api.enable(levelDownButton);\n levelDownButton[0].attr({fill: enabledColor});\n} else {\n ctx.api.disable(levelDownButton);\n levelDownButton[0].attr({fill: disabledColor});\n}",
"tags": [
{
"tag": "background",
"stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "circle-background",
"stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = '#cccccc';\n}\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "levelDownButton",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "if (ctx.values.running) {\n var temperature = ctx.values.temperature; \n var minTemperature = ctx.properties.minTemperature;\n var step = ctx.properties.temperatureStep;\n \n var newTemperature = Math.max(minTemperature, temperature - step);\n ctx.api.setValue('temperature', newTemperature);\n ctx.api.callAction(event, 'updateTemperatureState', newTemperature, {\n error: () => {\n ctx.api.setValue('temperature', temperature);\n }\n });\n}"
}
}
},
{
"tag": "levelUpButton",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "if (ctx.values.running) {\n var temperature = ctx.values.temperature; \n var temperature = ctx.values.temperature; \n var maxTemperature = ctx.properties.maxTemperature;\n var minTemperature = ctx.properties.minTemperature;\n var step = ctx.properties.temperatureStep;\n var newTemperature = temperature || minTemperature === 0 ? Math.min(maxTemperature, temperature + step) : minTemperature;\n ctx.api.setValue('temperature', newTemperature);\n ctx.api.callAction(event, 'updateTemperatureState', newTemperature, {\n error: () => {\n ctx.api.setValue('temperature', temperature);\n }\n });\n}"
}
}
},
{
"tag": "value-box-background",
"stateRenderFunction": "if (ctx.values.running) {\n var color = ctx.properties.valueBoxBackground;\n element.attr({fill: color});\n}\n",
"actions": null
},
{
"tag": "value-text",
"stateRenderFunction": "var valueTextFont = ctx.properties.valueTextFont;\nvar valueTextColor = ctx.properties.valueTextColor;\nvar units = ctx.properties.valueUnits ? ctx.properties.units : null;\nvar currentVolume = ctx.values.temperature;\nvar decimals = Math.floor(ctx.properties.temperatureStep) === ctx.properties.temperatureStep;\nvar valueText = ctx.api.formatValue(currentVolume, decimals ? 0 : 1, units, !decimals);\nctx.api.font(element, valueTextFont, valueTextColor);\nctx.api.text(element, valueText);",
"actions": null
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "running",
"name": "{i18n:scada.symbol.running}",
"hint": "{i18n:scada.symbol.running-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.running}",
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "running"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "temperature",
"name": "{i18n:scada.symbol.temperature}",
"hint": "{i18n:scada.symbol.temperature-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "GET_TIME_SERIES",
"defaultValue": null,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "temperature"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "updateTemperatureState",
"name": "{i18n:scada.symbol.update-temperature}",
"hint": "{i18n:scada.symbol.update-temperature-hint}",
"group": null,
"type": "action",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": {
"action": "ADD_TIME_SERIES",
"executeRpc": {
"method": "setState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"setAttribute": {
"scope": "SERVER_SCOPE",
"key": "state"
},
"putTimeSeries": {
"key": "temperature"
},
"valueToData": {
"type": "VALUE",
"constantValue": false,
"valueToDataFunction": "/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;"
}
},
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning-state}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "critical",
"name": "{i18n:scada.symbol.critical-state}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "criticalAnimation",
"name": "{i18n:scada.symbol.critical-state-animation}",
"hint": "{i18n:scada.symbol.critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warningClick",
"name": "{i18n:scada.symbol.warning-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
},
{
"id": "criticalClick",
"name": "{i18n:scada.symbol.critical-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
}
],
"properties": [
{
"id": "runningColor",
"name": "{i18n:scada.symbol.running-color}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "stoppedColor",
"name": "{i18n:scada.symbol.stopped-color}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.warning-color}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.critical-color}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "minTemperature",
"name": "{i18n:scada.symbol.temperature}",
"type": "number",
"default": 10,
"required": null,
"subLabel": "Min",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "column-xs",
"fieldClass": "",
"min": null,
"max": null,
"step": 1
},
{
"id": "maxTemperature",
"name": "{i18n:scada.symbol.temperature}",
"type": "number",
"default": 45,
"required": true,
"subLabel": "Max",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": 1
},
{
"id": "temperatureStep",
"name": "{i18n:scada.symbol.temperature-step}",
"type": "number",
"default": 1,
"required": true,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": 0.5
},
{
"id": "valueTextColor",
"name": "{i18n:scada.symbol.value-text}",
"type": "color",
"default": "#002878",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "valueTextFont",
"name": "{i18n:scada.symbol.value-text}",
"type": "font",
"default": {
"size": 40,
"sizeUnit": "px",
"family": "Roboto",
"weight": "500",
"style": "normal"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "valueUnits",
"name": "{i18n:scada.symbol.value-units}",
"type": "switch",
"default": false,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "units",
"name": "{i18n:scada.symbol.value-units}",
"type": "units",
"default": "°C",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "valueBoxBackground",
"name": "{i18n:scada.symbol.value-box-background}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<g fill="#fff" tb:tag="background">
<rect x="250" y="571" width="300" height="29" rx="6.9964"/>
<rect x="251" y="572" width="298" height="27" rx="5.9964" stroke="#000" stroke-opacity=".87" stroke-width="2"/>
<path d="m303 542h194l32 31h-258l32-31z"/>
<path d="m273.47 572 29.936-29h193.19l29.936 29h-253.06z" stroke="#000" stroke-opacity=".87" stroke-width="2"/>
<rect width="800" height="544" rx="32"/>
<rect x="1" y="1" width="798" height="542" rx="31" stroke="#000" stroke-opacity=".87" stroke-width="2"/>
<rect x="80" y="72.001" width="180" height="128" rx="8"/>
<rect x="81" y="73.001" width="178" height="126" rx="7" stroke="#000" stroke-opacity=".87" stroke-width="2" tb:tag="value-box-background"/>
<text x="170.51953" y="139.75" dominant-baseline="middle" fill="#002878" font-family="Roboto" font-size="40px" font-weight="500" text-anchor="middle" tb:tag="value-text" xml:space="preserve"><tspan>27</tspan></text>
<ellipse cx="542" cy="284" rx="180" ry="180" fill="#fff"/>
<path d="m720.5 284c0 98.583-79.918 178.5-178.5 178.5s-178.5-79.918-178.5-178.5 79.918-178.5 178.5-178.5 178.5 79.918 178.5 178.5z" stroke="#000" stroke-opacity=".12" stroke-width="2.9985"/>
<g clip-path="url(#clip1_3215_7169)">
<path d="m536.11 171.46c-13.585-34.892-30.132-50.233-39.494-62.425l-51.598 1.357-36.438 43.78c2.989-3.245 18.831-0.165 58.282 38.122 39.45 38.287 53.558 73.174 55.681 85.832l27.713-18.525c0-27.204-0.561-53.25-14.146-88.141z" stroke="#727171" stroke-width="3.2645"/>
<path d="m643.04 226.16c24.624-28.208 30.551-49.979 37.016-63.926l-25.114-45.094-55.681-12.001c4.261 1.145 8.873 16.611-6.773 69.312s-40.023 81.37-50.256 89.117l29.258 15.973c24.107-12.607 46.927-25.174 71.55-53.381z" stroke="#727171" stroke-width="3.2645"/>
<path d="m660.17 341.98c37.249 3.809 58.446-3.928 73.565-6.705l22.307-46.547-22.363-52.386c1.526 4.14-8.436 16.837-60.494 34.504-52.059 17.666-89.542 14.325-101.78 10.446l-6.364 30.727c24.215 12.398 57.877 26.152 95.125 29.961z" stroke="#727171" stroke-width="3.2645"/>
<path d="m445.68 341.28c-26.207 26.744-42.114 48.43-49.371 61.981l22.476 46.466 54.898 15.186c-4.188-1.389-7.902-17.095 10.752-68.808 18.653-51.713 44.64-78.931 55.302-86.076l-22.551-16.939c-24.793 11.199-45.3 21.447-71.506 48.19z" stroke="#727171" stroke-width="3.2645"/>
<path d="m547.19 393.22c12.328 35.356 26.698 53.295 35.619 65.814l51.613 0.49 37.982-42.448c-3.104 3.137-25.248-0.317-63.303-39.991s-48.728-61.258-50.396-73.984l-21.661 6.423c-0.974 27.187-2.182 48.341 10.146 83.696z" stroke="#727171" stroke-width="3.2645"/>
<path d="m442.58 235.02c-36.583-7.982-58.516-2.683-73.851-1.627l-27.411 43.736 16.318 54.573c-1.05-4.286 10.279-15.78 63.997-27.467 53.718-11.688 97.403-14.454 109.12-9.221l-3.176-23.132c-22.664-15.048-48.417-28.88-84.999-36.862z" stroke="#727171" stroke-width="3.2645"/>
<circle cx="542" cy="284" r="37.241" stroke="#000" stroke-opacity=".87" stroke-width="2"/>
<circle cx="542" cy="284" r="18.621" fill="#727171"/>
</g>
<mask id="path-29-inside-1_3215_7169" fill="white">
<path d="m722 284c0 99.411-80.589 180-180 180s-180-80.589-180-180 80.589-180 180-180 180 80.589 180 180zm-331.96 0c0 83.926 68.035 151.96 151.96 151.96s151.96-68.035 151.96-151.96-68.035-151.96-151.96-151.96-151.96 68.035-151.96 151.96z"/>
</mask>
<path d="m722 284c0 99.411-80.589 180-180 180s-180-80.589-180-180 80.589-180 180-180 180 80.589 180 180zm-331.96 0c0 83.926 68.035 151.96 151.96 151.96s151.96-68.035 151.96-151.96-68.035-151.96-151.96-151.96-151.96 68.035-151.96 151.96z" fill="#DEDEDE" mask="url(#path-29-inside-1_3215_7169)" stroke="#000" stroke-opacity=".87" stroke-width="4" tb:tag="circle-background"/>
</g><path d="m269.06 0s-269.06 0-269.06 100.5v492.54c0 3.9768 7.1636 6.9606 16 6.9606h768c8.836 0 16-2.9838 16-6.9606v-492.54c0-100.5-264.28-100.5-264.28-100.5h-135.72zm268.28 121.8c-5.1548 0-9.3332 1.8804-9.3332 4.2v450.6c0 2.3196 4.1788 4.2 9.3332 4.2h58.664c5.1548 0 9.3332-1.8804 9.3332-4.2v-450.6c0-2.3196-4.1788-4.2-9.3332-4.2z" fill="#000" fill-opacity="0" style="stroke-width:2.4495" tb:tag="clickArea"/><g tb:tag="levelUpButton">
<path d="m164 290.19c0 5.42-4.394 9.814-9.815 9.814h-68.371c-5.4203 0-9.8143-4.394-9.8143-9.814v-68.373c0-5.421 4.394-9.815 9.8143-9.815h68.371c5.421 0 9.815 4.394 9.815 9.815z"/>
<path d="m163 290.19c0 4.868-3.947 8.814-8.815 8.814h-68.371c-4.868 0-8.8143-3.946-8.8143-8.814v-68.373c0-4.868 3.9463-8.815 8.8143-8.815h68.371c4.868 0 8.815 3.947 8.815 8.815z" stroke="#000" stroke-opacity=".87" stroke-width="2"/>
<path d="m135.92 263.33c0.89 0 1.336-1.077 0.707-1.706l-15.921-15.921c-0.39-0.39-1.023-0.39-1.413 0l-15.921 15.921c-0.629 0.629-0.183 1.706 0.707 1.706z" fill="#000" fill-opacity=".87"/>
</g><g tb:tag="levelDownButton">
<path d="m264.96 221.82c0-5.42-4.394-9.814-9.814-9.814h-69.327c-5.42 0-9.814 4.394-9.814 9.814v68.372c0 5.42 4.394 9.814 9.814 9.814h69.327c5.42 0 9.814-4.394 9.814-9.814z"/>
<path d="m263.96 221.82c0-4.868-3.946-8.814-8.814-8.814h-69.327c-4.868 0-8.814 3.946-8.814 8.814v68.372c0 4.868 3.946 8.814 8.814 8.814h69.327c4.868 0 8.814-3.946 8.814-8.814z" stroke="#000" stroke-opacity=".87" stroke-width="2"/>
<path d="m236.58 248.67c0.893 0 1.338 1.082 0.703 1.711l-16.101 15.927c-0.389 0.386-1.016 0.386-1.406 0l-16.1-15.927c-0.635-0.629-0.191-1.711 0.703-1.711z" fill="#000" fill-opacity=".87"/>
</g><g transform="translate(0,516)" fill="#d12730" style="display: none;" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><g transform="translate(0,516)" fill="#faa405" style="display: none;" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><defs>
<clipPath id="clip1_3215_7169">
<rect x="362" y="104" width="360" height="360" rx="180" fill="#fff"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 25 KiB

174
application/src/main/data/json/system/scada_symbols/horizontal-connector-hp.svg

@ -0,0 +1,174 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="200" fill="none" version="1.1" viewBox="0 0 200 200"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Horizontal connector",
"description": "Horizontal connector",
"widgetSizeX": 1,
"widgetSizeY": 1,
"tags": [
{
"tag": "arrow",
"stateRenderFunction": "var arrow = ctx.values.arrow;\nif (arrow) {\n element.show();\n var arrowDirection = ctx.values.arrowDirection;\n var direticon = element.remember('direticon');\n var angle = arrowDirection ? 0 : 180;\n if (!direticon) {\n element.transform({rotate: angle});\n } else {\n ctx.api.transform({rotate: angle, originY: 100});\n element.remember('direticon', false);\n }\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"actions": null
}
],
"behavior": [
{
"id": "arrow",
"name": "{i18n:scada.symbol.arrow-presence}",
"hint": "{i18n:scada.symbol.arrow-presence-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.arrow-present}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "arrowDirection",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
}
],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": null,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"type": "color",
"default": "#1A1A1A",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "arrowColor",
"name": "{i18n:scada.symbol.arrow-color}",
"type": "color",
"default": "#1A1A1A",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<path d="M0 100H200" stroke="#1A1A1A" stroke-width="6" tb:tag="line"/><path d="M129 100L71 129V71L129 100Z" fill="#1A1A1A" tb:tag="arrow"/>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

536
application/src/main/data/json/system/scada_symbols/horizontal-tank-hp.svg

@ -0,0 +1,536 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="1e3" height="600" fill="none" version="1.1" viewBox="0 0 1e3 600"><tb:metadata xmlns=""><![CDATA[{
"title": "Horizontal tank",
"description": "Horizontal tank",
"searchTags": [
"horizontal tank"
],
"widgetSizeX": 5,
"widgetSizeY": 3,
"tags": [
{
"tag": "background",
"stateRenderFunction": "element.attr({fill: ctx.properties.tankColor});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "fluid-background",
"stateRenderFunction": " var color = ctx.properties.fluidColor;\n element.attr({fill: color, 'fill-opacity': 1});\n \n var valueSet = element.remember('valueSet');\n if (!valueSet) {\n element.remember('valueSet', true);\n element.attr({height: 0});\n }\n \n var currentVolume = ctx.values.currentVolume; \n var tankCapacity = ctx.values.tankCapacity; \n\n var height = currentVolume / tankCapacity;\n height = Math.max(0, Math.min(1, height))*570; \n \n var elementHeight = element.remember('height');\n if (height !== elementHeight) {\n element.remember('height', height);\n element.attr({height: height});\n }",
"actions": null
},
{
"tag": "scale",
"stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 15;\n var majorIntervalLength = 570 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(208, y, 240, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 198, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(220, minorY, 240, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}",
"actions": null
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "tankCapacity",
"name": "{i18n:scada.symbol.tank-capacity}",
"hint": "{i18n:scada.symbol.tank-capacity-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": null,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SERVER_SCOPE",
"key": "tankCapacity"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "currentVolume",
"name": "{i18n:scada.symbol.current-volume}",
"hint": "{i18n:scada.symbol.current-volume-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "GET_TIME_SERIES",
"defaultValue": null,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "liquidVolume"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning-state}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "critical",
"name": "{i18n:scada.symbol.critical-state}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "criticalAnimation",
"name": "{i18n:scada.symbol.critical-state-animation}",
"hint": "{i18n:scada.symbol.critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warningClick",
"name": "{i18n:scada.symbol.warning-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
},
{
"id": "criticalClick",
"name": "{i18n:scada.symbol.critical-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
}
],
"properties": [
{
"id": "tankColor",
"name": "{i18n:scada.symbol.tank-color}",
"type": "color",
"default": "#EBEBEB",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "fluidColor",
"name": "{i18n:scada.symbol.fluid-color}",
"type": "color",
"default": "#C8DFF7",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "scale",
"name": "{i18n:scada.symbol.scale}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "majorIntervals",
"name": "{i18n:scada.symbol.major-ticks}",
"type": "number",
"default": 10,
"required": null,
"subLabel": "{i18n:scada.symbol.intervals}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": "scale",
"rowClass": "",
"fieldClass": "",
"min": 1,
"max": null,
"step": 1
},
{
"id": "majorFont",
"name": "{i18n:scada.symbol.major-ticks}",
"type": "font",
"default": {
"size": 24,
"sizeUnit": "px",
"family": "Roboto",
"weight": "500",
"style": "normal"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "scale",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "majorColor",
"name": "{i18n:scada.symbol.major-ticks-color}",
"type": "color",
"default": "#00000061",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "scale",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "minorIntervals",
"name": "{i18n:scada.symbol.minor-ticks}",
"type": "number",
"default": 5,
"required": null,
"subLabel": "{i18n:scada.symbol.intervals}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "scale",
"rowClass": "",
"fieldClass": "",
"min": 1,
"max": null,
"step": null
},
{
"id": "minorColor",
"name": "{i18n:scada.symbol.minor-ticks-color}",
"type": "color",
"default": "#0000001F",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "scale",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.warning-color}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.critical-color}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<path d="m174 600c-174 0-174-151.63-174-239 4.9551e-6 -1.386 9.6938e-6 -2.741 1.4223e-5 -4.065 1.18e-8 -4e-3 -1.4223e-5 -113.93-1.4223e-5 -113.94 0-87.373-8.3346e-7 -243 174-243h653.5c173 0 172.5 156.06 172.5 243v57 61c0 86.939 0 239-172.5 239h-653.5z" fill="#EBEBEB" tb:tag="background"/><path d="m174 599c-43.353 0-75.782-9.444-100.06-24.9-24.272-15.456-40.469-36.971-51.28-61.242-21.649-48.601-21.664-108.13-21.663-151.86l2e-5 -4.065-2e-5 -113.94c0-43.727 0.0143-104.26 21.666-153.86 10.814-24.774 27.013-46.788 51.286-62.616 24.274-15.828 56.7-25.52 100.05-25.52h653.5c43.097 0 75.302 9.7177 99.388 25.578 24.085 15.86 40.129 37.914 50.819 62.716 21.294 49.406 21.294 109.58 21.293 153.04v118.67c0 43.512-0.015 103.04-21.477 151.7-10.719 24.299-26.776 45.854-50.838 61.342-24.063 15.489-56.209 24.959-99.185 24.959h-653.5z" stroke="#000" stroke-opacity=".87" stroke-width="2"/><mask id="mask0_3610_178965" x="2" y="2" width="996" height="596" style="mask-type:alpha" maskUnits="userSpaceOnUse">
<path d="m175.3 598c-173.3 0-173.3-150.62-173.3-237.41 1e-5 -1.377 1e-5 -2.723 1e-5 -4.038 1e-5 -3e-3 -1e-5 -113.17-1e-5 -113.18 0-86.79 0-241.38 173.3-241.38h650.89c172.31 0 171.81 155.02 171.81 241.38v56.62 60.593c0 86.36 0 237.41-171.81 237.41h-650.89z" fill="#EBEBEB"/>
<path d="m175.3 597c-43.18 0-75.478-9.381-99.655-24.734-24.175-15.352-40.305-36.723-51.073-60.831-21.561-48.274-21.576-107.4-21.576-150.84l2e-5 -4.038-2e-5 -113.18c0-43.436 0.01436-103.56 21.579-152.84 10.77-24.608 26.904-46.474 51.079-62.196 24.176-15.722 56.471-25.349 99.646-25.349h650.89c42.925 0 75 9.653 98.989 25.407 23.988 15.754 39.967 37.659 50.614 62.295 21.208 49.075 21.208 108.84 21.207 152.01v117.88c0 43.222-0.015 102.35-21.391 150.68-10.675 24.136-26.667 45.547-50.631 60.931-23.967 15.385-55.984 24.792-98.788 24.792h-650.89z" stroke="#000" stroke-opacity=".87" stroke-width="2"/>
</mask><g mask="url(#mask0_3610_178965)">
<rect transform="scale(1,-1)" x="2" y="-585" width="996" height="419" fill="#c8dff7" tb:tag="fluid-background"/>
</g><g tb:tag="scale">
<line x1="208" x2="240" y1="8.9727" y2="8.9727" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="220" x2="240" y1="20.613" y2="20.613" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="32.254" y2="32.254" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="43.894" y2="43.894" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="55.537" y2="55.537" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="208" x2="240" y1="67.178" y2="67.178" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="220" x2="240" y1="78.818" y2="78.818" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="90.459" y2="90.459" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="102.1" y2="102.1" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="113.74" y2="113.74" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="208" x2="240" y1="125.38" y2="125.38" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="220" x2="240" y1="137.02" y2="137.02" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="148.66" y2="148.66" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="160.3" y2="160.3" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="171.94" y2="171.94" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="208" x2="240" y1="183.59" y2="183.59" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="220" x2="240" y1="195.23" y2="195.23" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="206.87" y2="206.87" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="218.51" y2="218.51" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="230.15" y2="230.15" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="208" x2="240" y1="241.79" y2="241.79" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="220" x2="240" y1="253.43" y2="253.43" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="265.07" y2="265.07" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="276.71" y2="276.71" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="288.35" y2="288.35" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="208" x2="240" y1="299.99" y2="299.99" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="220" x2="240" y1="311.64" y2="311.64" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="323.28" y2="323.28" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="334.92" y2="334.92" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="346.56" y2="346.56" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="208" x2="240" y1="358.2" y2="358.2" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="220" x2="240" y1="369.84" y2="369.84" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="381.48" y2="381.48" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="393.12" y2="393.12" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="404.76" y2="404.76" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="208" x2="240" y1="416.4" y2="416.4" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="220" x2="240" y1="428.04" y2="428.04" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="439.69" y2="439.69" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="451.33" y2="451.33" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="462.97" y2="462.97" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="208" x2="240" y1="474.61" y2="474.61" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="220" x2="240" y1="486.25" y2="486.25" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="497.89" y2="497.89" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="509.53" y2="509.53" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="521.17" y2="521.17" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="208" x2="240" y1="532.81" y2="532.81" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="220" x2="240" y1="544.45" y2="544.45" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="556.1" y2="556.1" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="567.74" y2="567.74" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="220" x2="240" y1="579.38" y2="579.38" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="208" x2="240" y1="591.02" y2="591.02" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<path d="m167.58 2.8789v17.121h-2.824v-13.77l-4.184 1.418v-2.332l6.668-2.4375h0.34zm17.301 7.1602v2.789c0 1.336-0.133 2.4766-0.399 3.4219-0.257 0.9375-0.632 1.6992-1.125 2.2852-0.492 0.5859-1.082 1.0156-1.769 1.289-0.68 0.2735-1.441 0.4102-2.285 0.4102-0.672 0-1.297-0.086-1.875-0.2578-0.571-0.1719-1.086-0.4414-1.547-0.8086s-0.856-0.8399-1.184-1.418c-0.32-0.5859-0.57-1.2852-0.75-2.0977-0.172-0.8125-0.257-1.7539-0.257-2.8242v-2.789c0-1.3438 0.132-2.4766 0.398-3.3985 0.266-0.92968 0.644-1.6836 1.137-2.2617 0.492-0.58594 1.078-1.0117 1.757-1.2774 0.688-0.26562 1.454-0.39844 2.297-0.39844 0.68 0 1.305 0.08594 1.875 0.25782 0.578 0.16406 1.094 0.42578 1.547 0.78515 0.461 0.35938 0.852 0.82813 1.172 1.4062 0.328 0.57032 0.578 1.2617 0.75 2.0742 0.172 0.80469 0.258 1.7422 0.258 2.8125zm-2.824 3.1875v-3.6094c0-0.67969-0.039-1.2774-0.117-1.793-0.079-0.52344-0.196-0.96484-0.352-1.3242-0.148-0.36719-0.336-0.66406-0.563-0.89062-0.226-0.23438-0.484-0.40235-0.773-0.50391-0.289-0.10938-0.613-0.16406-0.973-0.16406-0.437 0-0.828 0.08593-1.172 0.25781-0.343 0.16406-0.632 0.42969-0.867 0.79687-0.234 0.36719-0.414 0.85157-0.539 1.4531-0.117 0.59375-0.176 1.3164-0.176 2.168v3.6094c0 0.6875 0.039 1.2929 0.118 1.8164 0.078 0.5234 0.195 0.9726 0.351 1.3476 0.156 0.3672 0.344 0.6719 0.563 0.9141 0.226 0.2344 0.484 0.4062 0.773 0.5156 0.297 0.1094 0.621 0.1641 0.973 0.1641 0.445 0 0.84-0.086 1.183-0.2578 0.344-0.1719 0.633-0.4454 0.868-0.8204 0.234-0.3828 0.41-0.8789 0.527-1.4882 0.117-0.6094 0.176-1.3399 0.176-2.1914zm16.715-3.1875v2.789c0 1.336-0.133 2.4766-0.399 3.4219-0.258 0.9375-0.633 1.6992-1.125 2.2852-0.492 0.5859-1.082 1.0156-1.769 1.289-0.68 0.2735-1.442 0.4102-2.286 0.4102-0.671 0-1.296-0.086-1.875-0.2578-0.57-0.1719-1.086-0.4414-1.546-0.8086-0.461-0.3672-0.856-0.8399-1.184-1.418-0.32-0.5859-0.57-1.2852-0.75-2.0977-0.172-0.8125-0.258-1.7539-0.258-2.8242v-2.789c0-1.3438 0.133-2.4766 0.399-3.3985 0.265-0.92968 0.644-1.6836 1.136-2.2617 0.492-0.58594 1.078-1.0117 1.758-1.2774 0.688-0.26562 1.453-0.39844 2.297-0.39844 0.68 0 1.305 0.08594 1.875 0.25782 0.578 0.16406 1.094 0.42578 1.547 0.78515 0.461 0.35938 0.851 0.82813 1.172 1.4062 0.328 0.57032 0.578 1.2617 0.75 2.0742 0.172 0.80469 0.258 1.7422 0.258 2.8125zm-2.825 3.1875v-3.6094c0-0.67969-0.039-1.2774-0.117-1.793-0.078-0.52344-0.195-0.96484-0.351-1.3242-0.149-0.36719-0.336-0.66406-0.563-0.89062-0.226-0.23438-0.484-0.40235-0.773-0.50391-0.289-0.10938-0.614-0.16406-0.973-0.16406-0.438 0-0.828 0.08593-1.172 0.25781-0.344 0.16406-0.633 0.42969-0.867 0.79687-0.234 0.36719-0.414 0.85157-0.539 1.4531-0.117 0.59375-0.176 1.3164-0.176 2.168v3.6094c0 0.6875 0.039 1.2929 0.117 1.8164 0.078 0.5234 0.196 0.9726 0.352 1.3476 0.156 0.3672 0.344 0.6719 0.562 0.9141 0.227 0.2344 0.485 0.4062 0.774 0.5156 0.297 0.1094 0.621 0.1641 0.972 0.1641 0.446 0 0.84-0.086 1.184-0.2578 0.344-0.1719 0.633-0.4454 0.867-0.8204 0.235-0.3828 0.41-0.8789 0.528-1.4882 0.117-0.6094 0.175-1.3399 0.175-2.1914z" fill="#000" fill-opacity=".38"/>
<path d="m176.03 75.373h0.235c1.078 0 1.976-0.1406 2.695-0.4218 0.727-0.2891 1.305-0.6875 1.734-1.1953 0.43-0.5079 0.739-1.1055 0.926-1.793 0.188-0.6875 0.281-1.4336 0.281-2.2383v-2.9414c0-0.6953-0.074-1.3047-0.222-1.8281-0.141-0.5313-0.344-0.9727-0.61-1.3242-0.258-0.3594-0.558-0.6289-0.902-0.8086-0.336-0.1797-0.699-0.2696-1.09-0.2696-0.43 0-0.816 0.0977-1.16 0.293-0.336 0.1875-0.621 0.4492-0.856 0.7852-0.226 0.3281-0.402 0.7148-0.527 1.1601-0.117 0.4375-0.176 0.9063-0.176 1.4063 0 0.4687 0.055 0.9218 0.164 1.3593 0.118 0.4297 0.289 0.8125 0.516 1.1485 0.227 0.3359 0.512 0.6015 0.856 0.7969 0.343 0.1953 0.746 0.2929 1.207 0.2929 0.437 0 0.839-0.082 1.207-0.2461 0.367-0.1718 0.687-0.4023 0.961-0.6914 0.273-0.289 0.488-0.6133 0.644-0.9726 0.156-0.3594 0.242-0.7266 0.258-1.1016l1.078 0.3281c0 0.5938-0.125 1.1797-0.375 1.7578-0.242 0.5704-0.582 1.0938-1.02 1.5704-0.429 0.4687-0.933 0.8437-1.511 1.125-0.571 0.2812-1.192 0.4218-1.864 0.4218-0.812 0-1.531-0.1523-2.156-0.457-0.617-0.3125-1.133-0.7344-1.547-1.2656-0.406-0.5313-0.711-1.1407-0.914-1.8282s-0.304-1.4101-0.304-2.1679c0-0.8203 0.125-1.5899 0.375-2.3086 0.25-0.7188 0.613-1.3516 1.089-1.8985 0.477-0.5546 1.055-0.9843 1.735-1.289 0.687-0.3125 1.465-0.4688 2.332-0.4688 0.922 0 1.73 0.1797 2.426 0.5391 0.695 0.3594 1.281 0.8555 1.757 1.4883 0.477 0.6328 0.836 1.3672 1.079 2.2031 0.242 0.8359 0.363 1.7344 0.363 2.6953v0.9961c0 1.0078-0.09 1.9805-0.27 2.918-0.179 0.9297-0.472 1.7929-0.879 2.5898-0.398 0.7891-0.925 1.4844-1.582 2.086-0.648 0.5937-1.445 1.0586-2.39 1.3945-0.938 0.3281-2.039 0.4922-3.305 0.4922h-0.258v-2.3321zm22.739-7.7343v2.789c0 1.336-0.133 2.4766-0.399 3.4219-0.258 0.9375-0.633 1.6992-1.125 2.2852-0.492 0.5859-1.082 1.0156-1.769 1.289-0.68 0.2735-1.442 0.4102-2.286 0.4102-0.671 0-1.296-0.086-1.875-0.2578-0.57-0.1719-1.086-0.4414-1.546-0.8086-0.461-0.3672-0.856-0.8399-1.184-1.418-0.32-0.5859-0.57-1.2851-0.75-2.0976-0.172-0.8125-0.258-1.754-0.258-2.8243v-2.789c0-1.3438 0.133-2.4766 0.399-3.3985 0.265-0.9297 0.644-1.6836 1.136-2.2617 0.492-0.5859 1.078-1.0117 1.758-1.2773 0.688-0.2657 1.453-0.3985 2.297-0.3985 0.68 0 1.305 0.086 1.875 0.2578 0.578 0.1641 1.094 0.4258 1.547 0.7852 0.461 0.3594 0.851 0.8281 1.172 1.4063 0.328 0.5703 0.578 1.2617 0.75 2.0742 0.172 0.8047 0.258 1.7422 0.258 2.8125zm-2.825 3.1875v-3.6094c0-0.6797-0.039-1.2773-0.117-1.793-0.078-0.5234-0.195-0.9648-0.351-1.3242-0.149-0.3672-0.336-0.6641-0.563-0.8906-0.226-0.2344-0.484-0.4024-0.773-0.5039-0.289-0.1094-0.614-0.1641-0.973-0.1641-0.438 0-0.828 0.086-1.172 0.2578-0.344 0.1641-0.633 0.4297-0.867 0.7969s-0.414 0.8516-0.539 1.4531c-0.117 0.5938-0.176 1.3164-0.176 2.168v3.6094c0 0.6875 0.039 1.2929 0.117 1.8164 0.078 0.5234 0.196 0.9726 0.352 1.3476 0.156 0.3672 0.344 0.6719 0.562 0.9141 0.227 0.2344 0.485 0.4062 0.774 0.5156 0.297 0.1094 0.621 0.1641 0.972 0.1641 0.446 0 0.84-0.086 1.184-0.2578 0.344-0.1719 0.633-0.4453 0.867-0.8203 0.235-0.3829 0.41-0.8789 0.528-1.4883 0.117-0.6094 0.175-1.3399 0.175-2.1914z" fill="#000" fill-opacity=".38"/>
<path d="m184.91 130.55c0 1.062-0.246 1.957-0.738 2.683-0.492 0.727-1.164 1.278-2.016 1.653-0.844 0.367-1.797 0.551-2.859 0.551-1.063 0-2.02-0.184-2.871-0.551-0.852-0.375-1.524-0.926-2.016-1.653-0.492-0.726-0.738-1.621-0.738-2.683 0-0.703 0.136-1.34 0.41-1.91 0.273-0.578 0.66-1.075 1.16-1.489 0.508-0.421 1.102-0.746 1.781-0.972 0.688-0.227 1.438-0.34 2.25-0.34 1.078 0 2.043 0.199 2.895 0.598 0.851 0.398 1.519 0.949 2.004 1.652 0.492 0.703 0.738 1.523 0.738 2.461zm-2.836-0.141c0-0.57-0.117-1.07-0.351-1.5-0.235-0.429-0.563-0.761-0.985-0.996-0.422-0.234-0.91-0.351-1.465-0.351-0.562 0-1.05 0.117-1.465 0.351-0.414 0.235-0.738 0.567-0.972 0.996-0.227 0.43-0.34 0.93-0.34 1.5 0 0.578 0.113 1.078 0.34 1.5 0.226 0.414 0.551 0.731 0.972 0.949 0.422 0.219 0.918 0.329 1.489 0.329 0.57 0 1.062-0.11 1.476-0.329 0.414-0.218 0.735-0.535 0.961-0.949 0.227-0.422 0.34-0.922 0.34-1.5zm2.449-7.781c0 0.852-0.226 1.609-0.679 2.273-0.446 0.664-1.063 1.188-1.852 1.571-0.789 0.375-1.687 0.562-2.695 0.562-1.016 0-1.922-0.187-2.719-0.562-0.789-0.383-1.41-0.907-1.863-1.571-0.446-0.664-0.668-1.421-0.668-2.273 0-1.016 0.222-1.871 0.668-2.566 0.453-0.704 1.074-1.239 1.863-1.606s1.691-0.551 2.707-0.551 1.918 0.184 2.707 0.551 1.406 0.902 1.852 1.606c0.453 0.695 0.679 1.55 0.679 2.566zm-2.824 0.094c0-0.508-0.101-0.953-0.305-1.336-0.195-0.391-0.472-0.695-0.832-0.914-0.359-0.219-0.785-0.328-1.277-0.328s-0.918 0.105-1.277 0.316c-0.36 0.211-0.637 0.508-0.832 0.891-0.196 0.382-0.293 0.84-0.293 1.371 0 0.523 0.097 0.98 0.293 1.371 0.195 0.383 0.472 0.683 0.832 0.902 0.367 0.219 0.797 0.328 1.289 0.328s0.918-0.109 1.277-0.328c0.36-0.219 0.637-0.519 0.832-0.902 0.195-0.391 0.293-0.848 0.293-1.371zm17.067 2.519v2.789c0 1.336-0.133 2.477-0.399 3.422-0.258 0.938-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.274-1.442 0.411-2.286 0.411-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.442-1.546-0.809-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.097-0.172-0.813-0.258-1.754-0.258-2.825v-2.789c0-1.343 0.133-2.476 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.012 1.758-1.277 0.688-0.266 1.453-0.399 2.297-0.399 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.36 0.851 0.828 1.172 1.407 0.328 0.57 0.578 1.261 0.75 2.074 0.172 0.804 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.61c0-0.679-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.964-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.89-0.226-0.235-0.484-0.403-0.773-0.504-0.289-0.11-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.257-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.852-0.539 1.453c-0.117 0.594-0.176 1.317-0.176 2.168v3.61c0 0.687 0.039 1.293 0.117 1.816 0.078 0.524 0.196 0.973 0.352 1.348 0.156 0.367 0.344 0.672 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.446 0.867-0.821c0.235-0.382 0.41-0.878 0.528-1.488 0.117-0.609 0.175-1.34 0.175-2.191z" fill="#000" fill-opacity=".38"/>
<path d="m185.09 175.74v1.547l-6.82 15.516h-2.977l6.809-14.813h-8.836v-2.25h11.824zm13.68 7.102v2.789c0 1.336-0.133 2.476-0.399 3.422-0.258 0.937-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.273-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.441-1.546-0.808-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.098-0.172-0.812-0.258-1.754-0.258-2.824v-2.789c0-1.344 0.133-2.477 0.399-3.399 0.265-0.929 0.644-1.683 1.136-2.261 0.492-0.586 1.078-1.012 1.758-1.278 0.688-0.265 1.453-0.398 2.297-0.398 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.359 0.851 0.828 1.172 1.406 0.328 0.57 0.578 1.262 0.75 2.074 0.172 0.805 0.258 1.743 0.258 2.813zm-2.825 3.187v-3.609c0-0.68-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.965-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.891-0.226-0.234-0.484-0.402-0.773-0.504-0.289-0.109-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.851-0.539 1.453c-0.117 0.594-0.176 1.316-0.176 2.168v3.609c0 0.688 0.039 1.293 0.117 1.817 0.078 0.523 0.196 0.972 0.352 1.347 0.156 0.368 0.344 0.672 0.562 0.914 0.227 0.235 0.485 0.407 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.445 0.867-0.82c0.235-0.383 0.41-0.879 0.528-1.488 0.117-0.61 0.175-1.34 0.175-2.192z" fill="#000" fill-opacity=".38"/>
<path d="m182.29 233.23h0.352v2.309h-0.2c-1.007 0-1.879 0.156-2.613 0.469-0.726 0.312-1.324 0.742-1.793 1.289s-0.82 1.187-1.055 1.922c-0.226 0.726-0.339 1.5-0.339 2.32v2.684c0 0.679 0.074 1.281 0.222 1.804 0.149 0.516 0.356 0.95 0.621 1.301 0.274 0.344 0.586 0.606 0.938 0.785 0.351 0.18 0.73 0.27 1.137 0.27 0.421 0 0.804-0.086 1.148-0.258 0.344-0.18 0.637-0.426 0.879-0.738 0.242-0.313 0.426-0.684 0.551-1.114 0.125-0.429 0.187-0.898 0.187-1.406 0-0.484-0.062-0.937-0.187-1.359-0.117-0.43-0.293-0.805-0.528-1.125-0.234-0.328-0.527-0.582-0.879-0.762-0.343-0.187-0.742-0.281-1.195-0.281-0.562 0-1.074 0.133-1.535 0.398-0.453 0.266-0.82 0.614-1.102 1.043-0.273 0.422-0.421 0.871-0.445 1.348l-1.078-0.352c0.063-0.726 0.223-1.379 0.48-1.957 0.266-0.578 0.614-1.07 1.043-1.476 0.43-0.407 0.926-0.715 1.489-0.926 0.57-0.219 1.195-0.328 1.875-0.328 0.828 0 1.547 0.156 2.156 0.469 0.609 0.312 1.113 0.738 1.512 1.277 0.406 0.531 0.707 1.141 0.902 1.828 0.203 0.68 0.305 1.391 0.305 2.133 0 0.82-0.125 1.586-0.375 2.297-0.25 0.703-0.617 1.32-1.102 1.851-0.476 0.532-1.058 0.946-1.746 1.242-0.68 0.297-1.453 0.446-2.32 0.446-0.914 0-1.731-0.176-2.449-0.528-0.711-0.351-1.317-0.836-1.817-1.453-0.492-0.617-0.867-1.328-1.125-2.133-0.258-0.804-0.387-1.656-0.387-2.554v-1.172c0-1.297 0.164-2.52 0.493-3.668 0.328-1.156 0.832-2.176 1.511-3.059 0.688-0.882 1.567-1.574 2.637-2.074 1.07-0.508 2.348-0.762 3.832-0.762zm16.481 7.207v2.79c0 1.335-0.133 2.476-0.399 3.421-0.258 0.938-0.633 1.7-1.125 2.286-0.492 0.585-1.082 1.015-1.769 1.289-0.68 0.273-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.441-1.546-0.809-0.461-0.367-0.856-0.839-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.097-0.172-0.813-0.258-1.754-0.258-2.824v-2.79c0-1.343 0.133-2.476 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.011 1.758-1.277 0.688-0.266 1.453-0.398 2.297-0.398 0.68 0 1.305 0.085 1.875 0.257 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.36 0.851 0.829 1.172 1.407 0.328 0.57 0.578 1.261 0.75 2.074 0.172 0.805 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.609c0-0.68-0.039-1.278-0.117-1.793-0.078-0.524-0.195-0.965-0.351-1.325-0.149-0.367-0.336-0.664-0.563-0.89-0.226-0.235-0.484-0.403-0.773-0.504-0.289-0.11-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.429-0.867 0.796-0.234 0.368-0.414 0.852-0.539 1.454-0.117 0.593-0.176 1.316-0.176 2.168v3.609c0 0.687 0.039 1.293 0.117 1.816 0.078 0.524 0.196 0.973 0.352 1.348 0.156 0.367 0.344 0.672 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.445 0.867-0.82c0.235-0.383 0.41-0.879 0.528-1.489 0.117-0.609 0.175-1.339 0.175-2.191z" fill="#000" fill-opacity=".38"/>
<path d="m176.72 300.07-2.25-0.55 0.925-8.578h9.2v2.39h-6.856l-0.469 4.184c0.266-0.157 0.633-0.309 1.102-0.457 0.469-0.157 1.004-0.235 1.605-0.235 0.805 0 1.524 0.133 2.157 0.399 0.64 0.258 1.183 0.636 1.629 1.136 0.445 0.493 0.785 1.094 1.019 1.805 0.235 0.703 0.352 1.496 0.352 2.379 0 0.789-0.117 1.527-0.352 2.215-0.226 0.687-0.57 1.293-1.031 1.816-0.461 0.524-1.043 0.934-1.746 1.231-0.696 0.289-1.52 0.433-2.473 0.433-0.711 0-1.394-0.101-2.051-0.304-0.648-0.211-1.23-0.52-1.746-0.926-0.515-0.414-0.929-0.922-1.242-1.524-0.312-0.609-0.492-1.308-0.539-2.097h2.766c0.07 0.555 0.222 1.027 0.457 1.418 0.242 0.383 0.562 0.676 0.961 0.879 0.398 0.203 0.859 0.304 1.382 0.304 0.477 0 0.887-0.082 1.231-0.246 0.344-0.172 0.629-0.414 0.855-0.726 0.235-0.321 0.407-0.696 0.516-1.125 0.117-0.43 0.176-0.907 0.176-1.43 0-0.5-0.067-0.957-0.199-1.371-0.125-0.414-0.317-0.774-0.575-1.078-0.25-0.305-0.566-0.539-0.949-0.703-0.383-0.172-0.824-0.258-1.324-0.258-0.672 0-1.188 0.098-1.547 0.293-0.352 0.195-0.68 0.437-0.984 0.726zm22.047-2.027v2.789c0 1.336-0.133 2.477-0.399 3.422-0.258 0.938-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.274-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.257-0.57-0.172-1.086-0.442-1.546-0.809-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.098-0.172-0.812-0.258-1.754-0.258-2.824v-2.789c0-1.344 0.133-2.477 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.012 1.758-1.277 0.688-0.266 1.453-0.399 2.297-0.399 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.359 0.851 0.828 1.172 1.406 0.328 0.571 0.578 1.262 0.75 2.075 0.172 0.804 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.61c0-0.679-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.965-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.891-0.226-0.234-0.484-0.402-0.773-0.504-0.289-0.109-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.852-0.539 1.453c-0.117 0.594-0.176 1.317-0.176 2.168v3.61c0 0.687 0.039 1.293 0.117 1.816s0.196 0.973 0.352 1.348c0.156 0.367 0.344 0.671 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.515 0.297 0.11 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.257 0.344-0.172 0.633-0.446 0.867-0.821 0.235-0.383 0.41-0.879 0.528-1.488 0.117-0.609 0.175-1.34 0.175-2.191z" fill="#000" fill-opacity=".38"/>
<path d="m185.51 359.52v2.25h-12.305l-0.094-1.7 7.36-11.531h2.261l-2.449 4.195-4.23 6.786h9.457zm-2.133-10.981v17.063h-2.824v-17.063h2.824zm15.391 7.102v2.789c0 1.336-0.133 2.476-0.399 3.422-0.258 0.937-0.633 1.699-1.125 2.285s-1.082 1.015-1.769 1.289c-0.68 0.273-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.441-1.546-0.808-0.461-0.368-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.286-0.75-2.098-0.172-0.813-0.258-1.754-0.258-2.824v-2.789c0-1.344 0.133-2.477 0.399-3.399 0.265-0.929 0.644-1.683 1.136-2.261 0.492-0.586 1.078-1.012 1.758-1.278 0.688-0.265 1.453-0.398 2.297-0.398 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.425 1.547 0.785 0.461 0.359 0.851 0.828 1.172 1.406 0.328 0.57 0.578 1.262 0.75 2.074 0.172 0.805 0.258 1.742 0.258 2.813zm-2.825 3.187v-3.609c0-0.68-0.039-1.278-0.117-1.793-0.078-0.524-0.195-0.965-0.351-1.324-0.149-0.368-0.336-0.664-0.563-0.891-0.226-0.234-0.484-0.402-0.773-0.504-0.289-0.109-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.851-0.539 1.453c-0.117 0.594-0.176 1.316-0.176 2.168v3.609c0 0.688 0.039 1.293 0.117 1.817 0.078 0.523 0.196 0.972 0.352 1.347 0.156 0.367 0.344 0.672 0.562 0.914 0.227 0.235 0.485 0.407 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.445 0.867-0.82c0.235-0.383 0.41-0.879 0.528-1.488 0.117-0.61 0.175-1.34 0.175-2.192z" fill="#000" fill-opacity=".38"/>
<path d="m177.07 413.39h1.688c0.656 0 1.199-0.114 1.629-0.34 0.437-0.227 0.761-0.539 0.972-0.938 0.211-0.398 0.317-0.855 0.317-1.371 0-0.539-0.098-1-0.293-1.383-0.188-0.39-0.477-0.691-0.867-0.902-0.383-0.211-0.872-0.316-1.465-0.316-0.5 0-0.953 0.101-1.36 0.304-0.398 0.196-0.715 0.477-0.949 0.844-0.234 0.359-0.351 0.789-0.351 1.289h-2.836c0-0.906 0.238-1.711 0.714-2.414 0.477-0.703 1.125-1.254 1.946-1.652 0.828-0.407 1.758-0.61 2.789-0.61 1.101 0 2.062 0.184 2.883 0.551 0.828 0.359 1.472 0.899 1.933 1.617 0.461 0.719 0.692 1.61 0.692 2.672 0 0.485-0.114 0.977-0.34 1.477-0.227 0.5-0.563 0.957-1.008 1.371-0.445 0.406-1 0.738-1.664 0.996-0.664 0.25-1.434 0.375-2.309 0.375h-2.121v-1.57zm0 2.203v-1.547h2.121c1 0 1.852 0.117 2.555 0.351 0.711 0.235 1.289 0.559 1.734 0.973 0.446 0.406 0.77 0.871 0.973 1.395 0.211 0.523 0.316 1.078 0.316 1.664 0 0.797-0.144 1.508-0.433 2.132-0.281 0.618-0.684 1.141-1.207 1.571-0.524 0.429-1.137 0.754-1.84 0.972-0.695 0.219-1.453 0.329-2.273 0.329-0.735 0-1.438-0.102-2.11-0.305s-1.273-0.504-1.805-0.902c-0.531-0.407-0.953-0.911-1.265-1.512-0.305-0.61-0.457-1.313-0.457-2.11h2.824c0 0.508 0.117 0.957 0.352 1.348 0.242 0.383 0.578 0.684 1.007 0.902 0.438 0.219 0.938 0.329 1.5 0.329 0.594 0 1.106-0.106 1.536-0.317 0.429-0.211 0.757-0.523 0.984-0.937 0.234-0.414 0.352-0.914 0.352-1.5 0-0.664-0.129-1.203-0.387-1.618-0.258-0.414-0.625-0.718-1.102-0.914-0.476-0.203-1.039-0.304-1.687-0.304h-1.688zm21.696-2.356v2.789c0 1.336-0.133 2.477-0.399 3.422-0.258 0.938-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.274-1.442 0.411-2.286 0.411-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.442-1.546-0.809-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.097-0.172-0.813-0.258-1.754-0.258-2.825v-2.789c0-1.343 0.133-2.476 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.012 1.758-1.277 0.688-0.266 1.453-0.399 2.297-0.399 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.36 0.851 0.828 1.172 1.407 0.328 0.57 0.578 1.261 0.75 2.074 0.172 0.804 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.61c0-0.679-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.964-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.89-0.226-0.235-0.484-0.403-0.773-0.504-0.289-0.11-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.257-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.852-0.539 1.453c-0.117 0.594-0.176 1.317-0.176 2.168v3.61c0 0.687 0.039 1.293 0.117 1.816 0.078 0.524 0.196 0.973 0.352 1.348 0.156 0.367 0.344 0.672 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.446 0.867-0.821c0.235-0.382 0.41-0.878 0.528-1.488 0.117-0.609 0.175-1.34 0.175-2.191z" fill="#000" fill-opacity=".38"/>
<path d="m185.2 478.55v2.25h-11.437v-1.934l5.554-6.058c0.61-0.688 1.09-1.282 1.442-1.782 0.351-0.5 0.598-0.949 0.738-1.347 0.149-0.407 0.223-0.801 0.223-1.184 0-0.539-0.102-1.012-0.305-1.418-0.195-0.414-0.484-0.738-0.867-0.973-0.383-0.242-0.848-0.363-1.395-0.363-0.632 0-1.164 0.137-1.593 0.41-0.43 0.274-0.754 0.653-0.973 1.137-0.219 0.477-0.328 1.023-0.328 1.641h-2.824c0-0.992 0.226-1.899 0.679-2.719 0.453-0.828 1.11-1.484 1.969-1.969 0.86-0.492 1.895-0.738 3.106-0.738 1.14 0 2.109 0.191 2.906 0.574s1.402 0.926 1.816 1.629c0.422 0.703 0.633 1.535 0.633 2.496 0 0.531-0.086 1.059-0.258 1.582-0.172 0.524-0.418 1.047-0.738 1.57-0.313 0.516-0.684 1.036-1.113 1.559-0.43 0.516-0.903 1.039-1.418 1.57l-3.692 4.067h7.875zm13.575-7.711v2.789c0 1.336-0.133 2.476-0.399 3.422-0.258 0.937-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.273-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.441-1.546-0.808-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.098-0.172-0.812-0.258-1.754-0.258-2.824v-2.789c0-1.344 0.133-2.477 0.399-3.399 0.265-0.929 0.644-1.683 1.136-2.261 0.492-0.586 1.078-1.012 1.758-1.278 0.688-0.265 1.453-0.398 2.297-0.398 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.359 0.851 0.828 1.172 1.406 0.328 0.57 0.578 1.262 0.75 2.074 0.172 0.805 0.258 1.743 0.258 2.813zm-2.825 3.187v-3.609c0-0.68-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.965-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.891-0.226-0.234-0.484-0.402-0.773-0.504-0.289-0.109-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.851-0.539 1.453c-0.117 0.594-0.176 1.316-0.176 2.168v3.609c0 0.688 0.039 1.293 0.117 1.817 0.078 0.523 0.196 0.972 0.352 1.347 0.156 0.368 0.344 0.672 0.562 0.914 0.227 0.235 0.485 0.407 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.445 0.867-0.82c0.235-0.383 0.41-0.879 0.528-1.488 0.117-0.61 0.175-1.34 0.175-2.192z" fill="#000" fill-opacity=".38"/>
<path d="m181.47 521.28v17.121h-2.824v-13.769l-4.184 1.418v-2.332l6.668-2.438h0.34zm17.301 7.16v2.79c0 1.335-0.133 2.476-0.399 3.421-0.258 0.938-0.633 1.7-1.125 2.286-0.492 0.585-1.082 1.015-1.769 1.289-0.68 0.273-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.441-1.546-0.809-0.461-0.367-0.856-0.839-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.097-0.172-0.813-0.258-1.754-0.258-2.824v-2.79c0-1.343 0.133-2.476 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.011 1.758-1.277 0.688-0.266 1.453-0.398 2.297-0.398 0.68 0 1.305 0.085 1.875 0.257 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.36 0.851 0.829 1.172 1.407 0.328 0.57 0.578 1.261 0.75 2.074 0.172 0.805 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.609c0-0.68-0.039-1.278-0.117-1.793-0.078-0.524-0.195-0.965-0.351-1.325-0.149-0.367-0.336-0.664-0.563-0.89-0.226-0.235-0.484-0.403-0.773-0.504-0.289-0.11-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.429-0.867 0.796-0.234 0.368-0.414 0.852-0.539 1.454-0.117 0.593-0.176 1.316-0.176 2.168v3.609c0 0.687 0.039 1.293 0.117 1.816 0.078 0.524 0.196 0.973 0.352 1.348 0.156 0.367 0.344 0.672 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.445 0.867-0.82c0.235-0.383 0.41-0.879 0.528-1.489 0.117-0.609 0.175-1.339 0.175-2.191z" fill="#000" fill-opacity=".38"/>
<path d="m198.77 586.04v2.789c0 1.336-0.133 2.477-0.399 3.422-0.258 0.938-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.274-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.257-0.57-0.172-1.086-0.442-1.546-0.809-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.098-0.172-0.812-0.258-1.754-0.258-2.824v-2.789c0-1.344 0.133-2.477 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.012 1.758-1.277 0.688-0.266 1.453-0.399 2.297-0.399 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.359 0.851 0.828 1.172 1.406 0.328 0.571 0.578 1.262 0.75 2.075 0.172 0.804 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.61c0-0.679-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.965-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.891-0.226-0.234-0.484-0.402-0.773-0.504-0.289-0.109-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.852-0.539 1.453c-0.117 0.594-0.176 1.317-0.176 2.168v3.61c0 0.687 0.039 1.293 0.117 1.816s0.196 0.973 0.352 1.348c0.156 0.367 0.344 0.671 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.515 0.297 0.11 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.257 0.344-0.172 0.633-0.446 0.867-0.821 0.235-0.383 0.41-0.879 0.528-1.488 0.117-0.609 0.175-1.34 0.175-2.191z" fill="#000" fill-opacity=".38"/>
</g><path d="m336.32 0s-336.32 0-336.32 100.5v492.54c0 3.9768 8.9546 6.9606 20 6.9606h960c11.045 0 20-2.9838 20-6.9606v-492.54c0-100.5-330.35-100.5-330.35-100.5h-169.65zm335.35 121.8c-6.4433 0-11.667 1.8804-11.667 4.2v450.6c0 2.3196 5.2233 4.2 11.667 4.2h73.329c6.4433 0 11.667-1.8804 11.667-4.2v-450.6c0-2.3196-5.2233-4.2-11.667-4.2z" fill="#000" fill-opacity="0" style="stroke-width:.55902" tb:tag="clickArea"/><g fill="#d12730" style="display: none;" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><g transform="translate(0,-4.9442)" fill="#faa405" style="display: none;" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 49 KiB

328
application/src/main/data/json/system/scada_symbols/horizontal-valve-hp.svg

@ -0,0 +1,328 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="200" fill="none" version="1.1" viewBox="0 0 200 200"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Horizontal valve",
"description": "Horizontal valve with various states.",
"searchTags": [
"valve"
],
"widgetSizeX": 1,
"widgetSizeY": 1,
"tags": [
{
"tag": "background",
"stateRenderFunction": "var color = ctx.properties.closedColor;\nif (ctx.values.opened) {\n color = ctx.properties.openedColor;\n}\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "opened",
"name": "{i18n:scada.symbol.opened}",
"hint": "{i18n:scada.symbol.opened-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.opened}",
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SERVER_SCOPE",
"key": "open"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning-state}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "critical",
"name": "{i18n:scada.symbol.critical-state}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "criticalAnimation",
"name": "{i18n:scada.symbol.critical-state-animation}",
"hint": "{i18n:scada.symbol.critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warningClick",
"name": "{i18n:scada.symbol.warning-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
},
{
"id": "criticalClick",
"name": "{i18n:scada.symbol.critical-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
}
],
"properties": [
{
"id": "openedColor",
"name": "{i18n:scada.symbol.running-color}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "closedColor",
"name": "{i18n:scada.symbol.closed-color}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.warning-color}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.critical-color}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<path d="M100 100V47" stroke="#000" stroke-opacity=".87" stroke-width="2"/><g fill="#fff" tb:tag="background">
<path d="m101 100-101-58v116l101-58z"/>
<path d="m0.99999 156.27 97.992-56.273-97.992-56.273v112.55z" stroke="#000" stroke-opacity=".87" stroke-width="2"/>
<path d="M99 100L200 42V158L99 100Z"/>
<path d="M199 156.273L101.008 100L199 43.7274V156.273Z" stroke="#000" stroke-opacity=".87" stroke-width="2"/>
</g><rect transform="rotate(-90 52 48)" x="52" y="48" width="48" height="96" fill="#DEDEDE"/><rect transform="rotate(-90 53 47)" x="53" y="47" width="46" height="94" stroke="#000" stroke-opacity=".87" stroke-width="2"/><path d="m67.264 0s-67.264 0-67.264 33.5v164.18c0 1.3256 1.7909 2.3202 4 2.3202h192c2.209 0 4-0.99459 4-2.3202v-164.18c0-33.5-66.07-33.5-66.07-33.5h-33.929zm67.07 40.6c-1.2887 0-2.3333 0.6268-2.3333 1.4v150.2c0 0.7732 1.0447 1.4 2.3333 1.4h14.666c1.2887 0 2.3333-0.6268 2.3333-1.4v-150.2c0-0.7732-1.0447-1.4-2.3333-1.4z" fill="#000" fill-opacity="0" tb:tag="clickArea"/><g transform="translate(0,116)" fill="#d12730" style="display: none;" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><g transform="translate(0,116)" fill="#faa405" style="display: none;" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

82
application/src/main/data/json/system/scada_symbols/left-bottom-elbow-connector-hp.svg

@ -0,0 +1,82 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="200" fill="none" version="1.1" viewBox="0 0 200 200"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Left bottom elbow connector",
"description": "Left bottom elbow connector",
"widgetSizeX": 1,
"widgetSizeY": 1,
"tags": [
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"actions": null
}
],
"behavior": [],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": null,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"type": "color",
"default": "#1A1A1A",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<path d="M100 200L100 131C100 113.879 86.1208 100 69 100L0 100" stroke="#1A1A1A" stroke-width="6" tb:tag="line"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

87
application/src/main/data/json/system/scada_symbols/left-tee-connector-hp.svg

@ -0,0 +1,87 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="200" fill="none" version="1.1" viewBox="0 0 200 200"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Left tee connector",
"description": "Left tee connector",
"widgetSizeX": 1,
"widgetSizeY": 1,
"tags": [
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"actions": null
},
{
"tag": "line-color",
"stateRenderFunction": "element.fill(ctx.properties.lineColor);\nelement.stroke(ctx.properties.lineColor);",
"actions": null
}
],
"behavior": [],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": null,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"type": "color",
"default": "#1A1A1A",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<path d="M100 0V200" stroke="#1A1A1A" stroke-width="6" id="path6" tb:tag="line"/><path d="M100 113V87C100 87 100 100 86 100C100 100 100 113 100 113Z" fill="#1A1A1A" stroke="#1A1A1A" stroke-width="2" id="path8" tb:tag="line-color"/><path d="M0 100H85C93.2843 100 100 93.2843 100 85V0" stroke="#1A1A1A" stroke-width="6" id="path4" tb:tag="line"/><path d="M0 100H85C93.2843 100 100 106.716 100 115V200" stroke="#1A1A1A" stroke-width="6" id="path2" tb:tag="line"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

82
application/src/main/data/json/system/scada_symbols/left-top-elbow-connector-hp.svg

@ -0,0 +1,82 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="200" fill="none" version="1.1" viewBox="0 0 200 200"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Left top elbow connector",
"description": "Left top elbow connector",
"widgetSizeX": 1,
"widgetSizeY": 1,
"tags": [
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"actions": null
}
],
"behavior": [],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": null,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"type": "color",
"default": "#1A1A1A",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<path d="M0 100H69C86.1208 100 100 86.1208 100 69V0" stroke="#1A1A1A" stroke-width="6" tb:tag="line"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

174
application/src/main/data/json/system/scada_symbols/long-horizontal-connector-hp.svg

@ -0,0 +1,174 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="400" height="200" fill="none" version="1.1" viewBox="0 0 400 200"><tb:metadata xmlns=""><![CDATA[{
"title": "Long horizontal connector",
"description": "Long horizontal connector",
"widgetSizeX": 2,
"widgetSizeY": 1,
"tags": [
{
"tag": "arrow",
"stateRenderFunction": "var arrow = ctx.values.arrow;\nif (arrow) {\n element.show();\n var arrowDirection = ctx.values.arrowDirection;\n var direticon = element.remember('direticon');\n var angle = arrowDirection ? 0 : 180;\n if (!direticon) {\n element.transform({rotate: angle});\n } else {\n ctx.api.transform({rotate: angle, originY: 100});\n element.remember('direticon', false);\n }\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"actions": null
}
],
"behavior": [
{
"id": "arrow",
"name": "{i18n:scada.symbol.arrow-presence}",
"hint": "{i18n:scada.symbol.arrow-presence-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.arrow-present}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "arrowDirection",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
}
],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": null,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"type": "color",
"default": "#1A1A1A",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "arrowColor",
"name": "{i18n:scada.symbol.arrow-color}",
"type": "color",
"default": "#1A1A1A",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<path d="m0 100h400" stroke="#1A1A1A" stroke-width="6" tb:tag="line"/><path d="m229 100-58 29v-58z" fill="#1a1a1a" tb:tag="arrow"/>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

174
application/src/main/data/json/system/scada_symbols/long-vertical-connector-hp.svg

@ -0,0 +1,174 @@
<svg xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="400" fill="none" version="1.1" viewBox="0 0 200 400" id="svg4" sodipodi:docname="long-vertical-connector.svg" inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Long vertical connector",
"description": "Long vertical connector",
"widgetSizeX": 1,
"widgetSizeY": 2,
"tags": [
{
"tag": "arrow",
"stateRenderFunction": "var arrow = ctx.values.arrow;\nif (arrow) {\n element.show();\n var arrowDirection = ctx.values.arrowDirection;\n var direticon = element.remember('direticon');\n var angle = arrowDirection ? 0 : 180;\n if (!direticon) {\n element.transform({rotate: angle});\n } else {\n ctx.api.transform({rotate: angle, originY: 100});\n element.remember('direticon', false);\n }\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"actions": null
}
],
"behavior": [
{
"id": "arrow",
"name": "{i18n:scada.symbol.arrow-presence}",
"hint": "{i18n:scada.symbol.arrow-presence-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.arrow-present}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "arrowDirection",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
}
],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": null,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"type": "color",
"default": "#1A1A1A",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "arrowColor",
"name": "{i18n:scada.symbol.arrow-color}",
"type": "color",
"default": "#1A1A1A",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<defs id="defs8"/><sodipodi:namedview id="namedview6" pagecolor="#505050" bordercolor="#eeeeee" borderopacity="1" inkscape:pageshadow="0" inkscape:pageopacity="0" inkscape:pagecheckerboard="0" showgrid="false" inkscape:zoom="2.2475" inkscape:cx="99.888765" inkscape:cy="200" inkscape:window-width="1920" inkscape:window-height="1136" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="svg4"/><path d="M100 400L100 0" stroke="#1A1A1A" stroke-width="6" id="path4" tb:tag="line"/><path d="M100 171L129 229H71L100 171Z" fill="#1A1A1A" id="path2" tb:tag="arrow"/>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

544
application/src/main/data/json/system/scada_symbols/pool-hp.svg

@ -0,0 +1,544 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="2400" height="800" fill="none" version="1.1" viewBox="0 0 2400 800" xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg">
<tb:metadata><![CDATA[{
"title": "Pool",
"description": "Pool",
"searchTags": [
"pool"
],
"widgetSizeX": 12,
"widgetSizeY": 4,
"tags": [
{
"tag": "background",
"stateRenderFunction": "element.attr({fill: ctx.properties.tankColor});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "fluid-background",
"stateRenderFunction": " var color = ctx.properties.fluidColor;\n element.attr({fill: color, 'fill-opacity': 1});\n \n var valueSet = element.remember('valueSet');\n if (!valueSet) {\n element.remember('valueSet', true);\n element.attr({height: 0});\n }\n \n var currentVolume = ctx.values.currentVolume; \n var tankCapacity = ctx.values.tankCapacity; \n\n var height = currentVolume / tankCapacity;\n height = Math.max(0, Math.min(1, height))*770; \n \n var elementHeight = element.remember('height');\n if (height !== elementHeight) {\n element.remember('height', height);\n element.attr({height: height});\n }",
"actions": null
},
{
"tag": "scale",
"stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 20;\n var majorIntervalLength = 770 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(298, y, 330, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 288, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(310, minorY, 330, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}",
"actions": null
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "tankCapacity",
"name": "{i18n:scada.symbol.tank-capacity}",
"hint": "{i18n:scada.symbol.tank-capacity-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": null,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SERVER_SCOPE",
"key": "tankCapacity"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "currentVolume",
"name": "{i18n:scada.symbol.current-volume}",
"hint": "{i18n:scada.symbol.current-volume-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "GET_TIME_SERIES",
"defaultValue": null,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "liquidVolume"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning-state}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "critical",
"name": "{i18n:scada.symbol.critical-state}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "criticalAnimation",
"name": "{i18n:scada.symbol.critical-state-animation}",
"hint": "{i18n:scada.symbol.critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warningClick",
"name": "{i18n:scada.symbol.warning-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
},
{
"id": "criticalClick",
"name": "{i18n:scada.symbol.critical-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
}
],
"properties": [
{
"id": "tankColor",
"name": "{i18n:scada.symbol.tank-color}",
"type": "color",
"default": "#EBEBEB",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "fluidColor",
"name": "{i18n:scada.symbol.fluid-color}",
"type": "color",
"default": "#C8DFF7",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "scale",
"name": "{i18n:scada.symbol.scale}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "majorIntervals",
"name": "{i18n:scada.symbol.major-ticks}",
"type": "number",
"default": 10,
"required": null,
"subLabel": "{i18n:scada.symbol.intervals}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": "scale",
"rowClass": "",
"fieldClass": "",
"min": 1,
"max": null,
"step": 1
},
{
"id": "majorFont",
"name": "{i18n:scada.symbol.major-ticks}",
"type": "font",
"default": {
"size": 24,
"sizeUnit": "px",
"family": "Roboto",
"weight": "500",
"style": "normal"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "scale",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "majorColor",
"name": "{i18n:scada.symbol.major-ticks-color}",
"type": "color",
"default": "#00000061",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "scale",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "minorIntervals",
"name": "{i18n:scada.symbol.minor-ticks}",
"type": "number",
"default": 5,
"required": null,
"subLabel": "{i18n:scada.symbol.intervals}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "scale",
"rowClass": "",
"fieldClass": "",
"min": 1,
"max": null,
"step": null
},
{
"id": "minorColor",
"name": "{i18n:scada.symbol.minor-ticks-color}",
"type": "color",
"default": "#0000001F",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "scale",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.warning-color}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.critical-color}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<path d="m0 14.714c0-8.1263 6.5877-14.714 14.714-14.714h2370.6c8.12 0 14.71 6.5877 14.71 14.714v770.57c0 8.126-6.59 14.714-14.71 14.714h-2370.6c-8.1263 0-14.714-6.588-14.714-14.714v-770.57z" fill="#EBEBEB" tb:tag="background"/>
<path d="m1 14.714c0-7.574 6.14-13.714 13.714-13.714h2370.6c7.57 0 13.71 6.14 13.71 13.714v770.57c0 7.574-6.14 13.714-13.71 13.714h-2370.6c-7.574 0-13.714-6.14-13.714-13.714v-770.57z" stroke="#000" stroke-opacity=".87" stroke-width="2"/>
<mask id="mask0_3600_177361" x="2" y="2" width="2396" height="796" style="mask-type:alpha" maskUnits="userSpaceOnUse">
<path d="m2386 2c6.63 0 12 5.3726 12 12v772c0 6.627-5.37 12-12 12h-2372c-6.6274 0-12-5.373-12-12v-772c0-6.6274 5.3726-12 12-12h2372z" fill="#C8DFF7"/>
</mask>
<g mask="url(#mask0_3600_177361)">
<rect transform="scale(1,-1)" x="2" y="-790" width="2396" height="615" fill="#c8dff7" style="stroke-width:3.1235" tb:tag="fluid-background"/>
</g>
<g tb:tag="scale">
<line x1="298" x2="330" y1="6.582" y2="6.582" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="310" x2="330" y1="22.319" y2="22.319" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="38.057" y2="38.057" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="53.793" y2="53.793" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="69.53" y2="69.53" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="298" x2="330" y1="85.268" y2="85.268" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="310" x2="330" y1="101" y2="101" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="116.74" y2="116.74" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="132.48" y2="132.48" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="148.22" y2="148.22" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="298" x2="330" y1="163.95" y2="163.95" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="310" x2="330" y1="179.69" y2="179.69" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="195.43" y2="195.43" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="211.16" y2="211.16" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="226.9" y2="226.9" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="298" x2="330" y1="242.64" y2="242.64" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="310" x2="330" y1="258.38" y2="258.38" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="274.11" y2="274.11" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="289.85" y2="289.85" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="305.59" y2="305.59" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="298" x2="330" y1="321.32" y2="321.32" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="310" x2="330" y1="337.06" y2="337.06" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="352.8" y2="352.8" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="368.53" y2="368.53" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="384.27" y2="384.27" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="298" x2="330" y1="400.01" y2="400.01" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="310" x2="330" y1="415.75" y2="415.75" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="431.48" y2="431.48" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="447.22" y2="447.22" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="462.96" y2="462.96" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="298" x2="330" y1="478.69" y2="478.69" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="310" x2="330" y1="494.43" y2="494.43" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="510.17" y2="510.17" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="525.9" y2="525.9" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="541.64" y2="541.64" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="298" x2="330" y1="557.38" y2="557.38" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="310" x2="330" y1="573.12" y2="573.12" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="588.85" y2="588.85" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="604.59" y2="604.59" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="620.33" y2="620.33" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="298" x2="330" y1="636.06" y2="636.06" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="310" x2="330" y1="651.8" y2="651.8" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="667.54" y2="667.54" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="683.28" y2="683.28" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="699.01" y2="699.01" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="298" x2="330" y1="714.75" y2="714.75" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="310" x2="330" y1="730.49" y2="730.49" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="746.22" y2="746.22" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="761.96" y2="761.96" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="310" x2="330" y1="777.7" y2="777.7" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="298" x2="330" y1="793.44" y2="793.44" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<path d="m257.58 2.8789v17.121h-2.824v-13.77l-4.184 1.418v-2.332l6.668-2.4375h0.34zm17.301 7.1602v2.789c0 1.336-0.133 2.4766-0.399 3.4219-0.257 0.9375-0.632 1.6992-1.125 2.2852-0.492 0.5859-1.082 1.0156-1.769 1.289-0.68 0.2735-1.441 0.4102-2.285 0.4102-0.672 0-1.297-0.086-1.875-0.2578-0.571-0.1719-1.086-0.4414-1.547-0.8086s-0.856-0.8399-1.184-1.418c-0.32-0.5859-0.57-1.2852-0.75-2.0977-0.172-0.8125-0.257-1.7539-0.257-2.8242v-2.789c0-1.3438 0.132-2.4766 0.398-3.3985 0.266-0.92968 0.644-1.6836 1.137-2.2617 0.492-0.58594 1.078-1.0117 1.757-1.2774 0.688-0.26562 1.454-0.39844 2.297-0.39844 0.68 0 1.305 0.08594 1.875 0.25782 0.578 0.16406 1.094 0.42578 1.547 0.78515 0.461 0.35938 0.852 0.82813 1.172 1.4062 0.328 0.57032 0.578 1.2617 0.75 2.0742 0.172 0.80469 0.258 1.7422 0.258 2.8125zm-2.824 3.1875v-3.6094c0-0.67969-0.039-1.2774-0.117-1.793-0.079-0.52344-0.196-0.96484-0.352-1.3242-0.148-0.36719-0.336-0.66406-0.563-0.89062-0.226-0.23438-0.484-0.40235-0.773-0.50391-0.289-0.10938-0.613-0.16406-0.973-0.16406-0.437 0-0.828 0.08593-1.172 0.25781-0.343 0.16406-0.632 0.42969-0.867 0.79687-0.234 0.36719-0.414 0.85157-0.539 1.4531-0.117 0.59375-0.176 1.3164-0.176 2.168v3.6094c0 0.6875 0.039 1.2929 0.118 1.8164 0.078 0.5234 0.195 0.9726 0.351 1.3476 0.156 0.3672 0.344 0.6719 0.563 0.9141 0.226 0.2344 0.484 0.4062 0.773 0.5156 0.297 0.1094 0.621 0.1641 0.973 0.1641 0.445 0 0.84-0.086 1.183-0.2578 0.344-0.1719 0.633-0.4454 0.868-0.8204 0.234-0.3828 0.41-0.8789 0.527-1.4882 0.117-0.6094 0.176-1.3399 0.176-2.1914zm16.715-3.1875v2.789c0 1.336-0.133 2.4766-0.399 3.4219-0.258 0.9375-0.633 1.6992-1.125 2.2852-0.492 0.5859-1.082 1.0156-1.769 1.289-0.68 0.2735-1.442 0.4102-2.286 0.4102-0.671 0-1.296-0.086-1.875-0.2578-0.57-0.1719-1.086-0.4414-1.546-0.8086-0.461-0.3672-0.856-0.8399-1.184-1.418-0.32-0.5859-0.57-1.2852-0.75-2.0977-0.172-0.8125-0.258-1.7539-0.258-2.8242v-2.789c0-1.3438 0.133-2.4766 0.399-3.3985 0.265-0.92968 0.644-1.6836 1.136-2.2617 0.492-0.58594 1.078-1.0117 1.758-1.2774 0.688-0.26562 1.453-0.39844 2.297-0.39844 0.68 0 1.305 0.08594 1.875 0.25782 0.578 0.16406 1.094 0.42578 1.547 0.78515 0.461 0.35938 0.851 0.82813 1.172 1.4062 0.328 0.57032 0.578 1.2617 0.75 2.0742 0.172 0.80469 0.258 1.7422 0.258 2.8125zm-2.825 3.1875v-3.6094c0-0.67969-0.039-1.2774-0.117-1.793-0.078-0.52344-0.195-0.96484-0.351-1.3242-0.149-0.36719-0.336-0.66406-0.563-0.89062-0.226-0.23438-0.484-0.40235-0.773-0.50391-0.289-0.10938-0.614-0.16406-0.973-0.16406-0.438 0-0.828 0.08593-1.172 0.25781-0.344 0.16406-0.633 0.42969-0.867 0.79687-0.234 0.36719-0.414 0.85157-0.539 1.4531-0.117 0.59375-0.176 1.3164-0.176 2.168v3.6094c0 0.6875 0.039 1.2929 0.117 1.8164 0.078 0.5234 0.196 0.9726 0.352 1.3476 0.156 0.3672 0.344 0.6719 0.562 0.9141 0.227 0.2344 0.485 0.4062 0.774 0.5156 0.297 0.1094 0.621 0.1641 0.972 0.1641 0.446 0 0.84-0.086 1.184-0.2578 0.344-0.1719 0.633-0.4454 0.867-0.8204 0.235-0.3828 0.41-0.8789 0.528-1.4882 0.117-0.6094 0.175-1.3399 0.175-2.1914z" fill="#000" fill-opacity=".38"/>
<path d="m266.03 95.474h0.235c1.078 0 1.976-0.1406 2.695-0.4218 0.727-0.2891 1.305-0.6875 1.734-1.1954 0.43-0.5078 0.739-1.1054 0.926-1.7929 0.188-0.6875 0.281-1.4336 0.281-2.2383v-2.9414c0-0.6953-0.074-1.3047-0.222-1.8281-0.141-0.5313-0.344-0.9727-0.61-1.3243-0.258-0.3593-0.558-0.6289-0.902-0.8085-0.336-0.1797-0.699-0.2696-1.09-0.2696-0.43 0-0.816 0.0977-1.16 0.293-0.336 0.1875-0.621 0.4492-0.856 0.7851-0.226 0.3282-0.402 0.7149-0.527 1.1602-0.117 0.4375-0.176 0.9063-0.176 1.4063 0 0.4687 0.055 0.9218 0.164 1.3593 0.118 0.4297 0.289 0.8125 0.516 1.1485 0.227 0.3359 0.512 0.6015 0.856 0.7968 0.343 0.1954 0.746 0.293 1.207 0.293 0.437 0 0.839-0.082 1.207-0.2461 0.367-0.1719 0.687-0.4023 0.961-0.6914 0.273-0.2891 0.488-0.6133 0.644-0.9726 0.156-0.3594 0.242-0.7266 0.258-1.1016l1.078 0.3281c0 0.5938-0.125 1.1797-0.375 1.7578-0.242 0.5703-0.582 1.0938-1.02 1.5703-0.429 0.4688-0.933 0.8438-1.511 1.125-0.571 0.2813-1.192 0.4219-1.864 0.4219-0.812 0-1.531-0.1523-2.156-0.457-0.617-0.3125-1.133-0.7344-1.547-1.2656-0.406-0.5313-0.711-1.1407-0.914-1.8282s-0.304-1.4101-0.304-2.1679c0-0.8203 0.125-1.5899 0.375-2.3086 0.25-0.7188 0.613-1.3516 1.089-1.8985 0.477-0.5547 1.055-0.9843 1.735-1.289 0.687-0.3125 1.465-0.4688 2.332-0.4688 0.922 0 1.73 0.1797 2.426 0.5391 0.695 0.3594 1.281 0.8555 1.757 1.4883 0.477 0.6328 0.836 1.3672 1.079 2.2031 0.242 0.8359 0.363 1.7344 0.363 2.6953v0.9961c0 1.0078-0.09 1.9805-0.27 2.918-0.179 0.9297-0.472 1.7929-0.879 2.5898-0.398 0.7891-0.925 1.4844-1.582 2.0859-0.648 0.5938-1.445 1.0586-2.39 1.3946-0.938 0.3281-2.039 0.4922-3.305 0.4922h-0.258v-2.3321zm22.739-7.7343v2.789c0 1.336-0.133 2.4766-0.399 3.4219-0.258 0.9375-0.633 1.6992-1.125 2.2852-0.492 0.5859-1.082 1.0156-1.769 1.289-0.68 0.2735-1.442 0.4102-2.286 0.4102-0.671 0-1.296-0.086-1.875-0.2578-0.57-0.1719-1.086-0.4414-1.546-0.8086-0.461-0.3672-0.856-0.8399-1.184-1.418-0.32-0.5859-0.57-1.2852-0.75-2.0977-0.172-0.8125-0.258-1.7539-0.258-2.8242v-2.789c0-1.3438 0.133-2.4766 0.399-3.3985 0.265-0.9297 0.644-1.6836 1.136-2.2617 0.492-0.5859 1.078-1.0117 1.758-1.2773 0.688-0.2657 1.453-0.3985 2.297-0.3985 0.68 0 1.305 0.086 1.875 0.2578 0.578 0.1641 1.094 0.4258 1.547 0.7852 0.461 0.3594 0.851 0.8281 1.172 1.4062 0.328 0.5704 0.578 1.2618 0.75 2.0743 0.172 0.8046 0.258 1.7421 0.258 2.8125zm-2.825 3.1875v-3.6094c0-0.6797-0.039-1.2774-0.117-1.793-0.078-0.5234-0.195-0.9648-0.351-1.3242-0.149-0.3672-0.336-0.6641-0.563-0.8906-0.226-0.2344-0.484-0.4024-0.773-0.5039-0.289-0.1094-0.614-0.1641-0.973-0.1641-0.438 0-0.828 0.0859-1.172 0.2578-0.344 0.1641-0.633 0.4297-0.867 0.7969s-0.414 0.8516-0.539 1.4531c-0.117 0.5938-0.176 1.3164-0.176 2.168v3.6094c0 0.6875 0.039 1.2929 0.117 1.8164 0.078 0.5234 0.196 0.9726 0.352 1.3476 0.156 0.3672 0.344 0.6719 0.562 0.9141 0.227 0.2344 0.485 0.4062 0.774 0.5156 0.297 0.1094 0.621 0.1641 0.972 0.1641 0.446 0 0.84-0.086 1.184-0.2578 0.344-0.1719 0.633-0.4454 0.867-0.8204 0.235-0.3828 0.41-0.8789 0.528-1.4882 0.117-0.6094 0.175-1.3399 0.175-2.1914z" fill="#000" fill-opacity=".38"/>
<path d="m274.91 170.75c0 1.063-0.246 1.957-0.738 2.684-0.492 0.726-1.164 1.277-2.016 1.652-0.844 0.367-1.797 0.551-2.859 0.551-1.063 0-2.02-0.184-2.871-0.551-0.852-0.375-1.524-0.926-2.016-1.652-0.492-0.727-0.738-1.621-0.738-2.684 0-0.703 0.136-1.34 0.41-1.91 0.273-0.578 0.66-1.074 1.16-1.488 0.508-0.422 1.102-0.746 1.781-0.973 0.688-0.227 1.438-0.34 2.25-0.34 1.078 0 2.043 0.199 2.895 0.598 0.851 0.398 1.519 0.949 2.004 1.652 0.492 0.703 0.738 1.524 0.738 2.461zm-2.836-0.141c0-0.57-0.117-1.07-0.351-1.5-0.235-0.429-0.563-0.761-0.985-0.996-0.422-0.234-0.91-0.351-1.465-0.351-0.562 0-1.05 0.117-1.465 0.351-0.414 0.235-0.738 0.567-0.972 0.996-0.227 0.43-0.34 0.93-0.34 1.5 0 0.579 0.113 1.079 0.34 1.5 0.226 0.414 0.551 0.731 0.972 0.95 0.422 0.218 0.918 0.328 1.489 0.328 0.57 0 1.062-0.11 1.476-0.328 0.414-0.219 0.735-0.536 0.961-0.95 0.227-0.421 0.34-0.921 0.34-1.5zm2.449-7.781c0 0.852-0.226 1.61-0.679 2.274-0.446 0.664-1.063 1.187-1.852 1.57-0.789 0.375-1.687 0.562-2.695 0.562-1.016 0-1.922-0.187-2.719-0.562-0.789-0.383-1.41-0.906-1.863-1.57-0.446-0.664-0.668-1.422-0.668-2.274 0-1.015 0.222-1.871 0.668-2.566 0.453-0.703 1.074-1.239 1.863-1.606s1.691-0.55 2.707-0.55 1.918 0.183 2.707 0.55 1.406 0.903 1.852 1.606c0.453 0.695 0.679 1.551 0.679 2.566zm-2.824 0.094c0-0.508-0.101-0.953-0.305-1.336-0.195-0.391-0.472-0.695-0.832-0.914-0.359-0.219-0.785-0.328-1.277-0.328s-0.918 0.105-1.277 0.316c-0.36 0.211-0.637 0.508-0.832 0.891-0.196 0.383-0.293 0.84-0.293 1.371 0 0.523 0.097 0.98 0.293 1.371 0.195 0.383 0.472 0.684 0.832 0.902 0.367 0.219 0.797 0.328 1.289 0.328s0.918-0.109 1.277-0.328c0.36-0.218 0.637-0.519 0.832-0.902 0.195-0.391 0.293-0.848 0.293-1.371zm17.067 2.519v2.79c0 1.335-0.133 2.476-0.399 3.421-0.258 0.938-0.633 1.7-1.125 2.286-0.492 0.585-1.082 1.015-1.769 1.289-0.68 0.273-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.441-1.546-0.809-0.461-0.367-0.856-0.839-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.097-0.172-0.813-0.258-1.754-0.258-2.824v-2.79c0-1.343 0.133-2.476 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.011 1.758-1.277 0.688-0.266 1.453-0.398 2.297-0.398 0.68 0 1.305 0.085 1.875 0.257 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.36 0.851 0.829 1.172 1.407 0.328 0.57 0.578 1.261 0.75 2.074 0.172 0.805 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.609c0-0.68-0.039-1.278-0.117-1.793-0.078-0.524-0.195-0.965-0.351-1.325-0.149-0.367-0.336-0.664-0.563-0.89-0.226-0.235-0.484-0.403-0.773-0.504-0.289-0.11-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.429-0.867 0.796-0.234 0.368-0.414 0.852-0.539 1.454-0.117 0.593-0.176 1.316-0.176 2.168v3.609c0 0.687 0.039 1.293 0.117 1.816 0.078 0.524 0.196 0.973 0.352 1.348 0.156 0.367 0.344 0.672 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.445 0.867-0.82c0.235-0.383 0.41-0.879 0.528-1.489 0.117-0.609 0.175-1.339 0.175-2.191z" fill="#000" fill-opacity=".38"/>
<path d="m275.09 236.04v1.547l-6.82 15.516h-2.977l6.809-14.813h-8.836v-2.25h11.824zm13.68 7.102v2.789c0 1.336-0.133 2.476-0.399 3.422-0.258 0.937-0.633 1.699-1.125 2.285s-1.082 1.015-1.769 1.289c-0.68 0.273-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.441-1.546-0.808-0.461-0.368-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.286-0.75-2.098-0.172-0.813-0.258-1.754-0.258-2.824v-2.789c0-1.344 0.133-2.477 0.399-3.399 0.265-0.929 0.644-1.683 1.136-2.261 0.492-0.586 1.078-1.012 1.758-1.278 0.688-0.265 1.453-0.398 2.297-0.398 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.425 1.547 0.785 0.461 0.359 0.851 0.828 1.172 1.406 0.328 0.57 0.578 1.262 0.75 2.074 0.172 0.805 0.258 1.742 0.258 2.813zm-2.825 3.187v-3.609c0-0.68-0.039-1.278-0.117-1.793-0.078-0.524-0.195-0.965-0.351-1.324-0.149-0.368-0.336-0.664-0.563-0.891-0.226-0.234-0.484-0.402-0.773-0.504-0.289-0.109-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.851-0.539 1.453c-0.117 0.594-0.176 1.316-0.176 2.168v3.609c0 0.688 0.039 1.293 0.117 1.817 0.078 0.523 0.196 0.972 0.352 1.347 0.156 0.367 0.344 0.672 0.562 0.914 0.227 0.235 0.485 0.407 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.445 0.867-0.82c0.235-0.383 0.41-0.879 0.528-1.488 0.117-0.61 0.175-1.34 0.175-2.192z" fill="#000" fill-opacity=".38"/>
<path d="m272.29 313.63h0.352v2.308h-0.2c-1.007 0-1.879 0.157-2.613 0.469-0.726 0.313-1.324 0.742-1.793 1.289s-0.82 1.188-1.055 1.922c-0.226 0.727-0.339 1.5-0.339 2.32v2.684c0 0.68 0.074 1.281 0.222 1.805 0.149 0.515 0.356 0.949 0.621 1.3 0.274 0.344 0.586 0.606 0.938 0.786 0.351 0.179 0.73 0.269 1.137 0.269 0.421 0 0.804-0.086 1.148-0.258 0.344-0.179 0.637-0.425 0.879-0.738 0.242-0.312 0.426-0.684 0.551-1.113 0.125-0.43 0.187-0.899 0.187-1.406 0-0.485-0.062-0.938-0.187-1.36-0.117-0.43-0.293-0.805-0.528-1.125-0.234-0.328-0.527-0.582-0.879-0.762-0.343-0.187-0.742-0.281-1.195-0.281-0.562 0-1.074 0.133-1.535 0.399-0.453 0.265-0.82 0.613-1.102 1.043-0.273 0.421-0.421 0.871-0.445 1.347l-1.078-0.351c0.063-0.727 0.223-1.379 0.48-1.957 0.266-0.578 0.614-1.071 1.043-1.477 0.43-0.406 0.926-0.715 1.489-0.926 0.57-0.218 1.195-0.328 1.875-0.328 0.828 0 1.547 0.156 2.156 0.469 0.609 0.312 1.113 0.738 1.512 1.277 0.406 0.532 0.707 1.141 0.902 1.828 0.203 0.68 0.305 1.391 0.305 2.133 0 0.821-0.125 1.586-0.375 2.297-0.25 0.703-0.617 1.32-1.102 1.852-0.476 0.531-1.058 0.945-1.746 1.242-0.68 0.297-1.453 0.445-2.32 0.445-0.914 0-1.731-0.176-2.449-0.527-0.711-0.352-1.317-0.836-1.817-1.453-0.492-0.617-0.867-1.328-1.125-2.133s-0.387-1.656-0.387-2.555v-1.172c0-1.297 0.164-2.519 0.493-3.668 0.328-1.156 0.832-2.175 1.511-3.058 0.688-0.883 1.567-1.574 2.637-2.074 1.07-0.508 2.348-0.762 3.832-0.762zm16.481 7.207v2.789c0 1.336-0.133 2.476-0.399 3.422-0.258 0.937-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.273-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.441-1.546-0.808-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.098-0.172-0.812-0.258-1.754-0.258-2.824v-2.789c0-1.344 0.133-2.477 0.399-3.399 0.265-0.929 0.644-1.683 1.136-2.261 0.492-0.586 1.078-1.012 1.758-1.278 0.688-0.265 1.453-0.398 2.297-0.398 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.359 0.851 0.828 1.172 1.406 0.328 0.57 0.578 1.262 0.75 2.074 0.172 0.805 0.258 1.743 0.258 2.813zm-2.825 3.187v-3.609c0-0.68-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.965-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.891-0.226-0.234-0.484-0.402-0.773-0.504-0.289-0.109-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.851-0.539 1.453c-0.117 0.594-0.176 1.316-0.176 2.168v3.609c0 0.688 0.039 1.293 0.117 1.817 0.078 0.523 0.196 0.972 0.352 1.347 0.156 0.368 0.344 0.672 0.562 0.914 0.227 0.235 0.485 0.407 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.445 0.867-0.82c0.235-0.383 0.41-0.879 0.528-1.488 0.117-0.61 0.175-1.34 0.175-2.192z" fill="#000" fill-opacity=".38"/>
<path d="m266.72 400.57-2.25-0.55 0.925-8.578h9.2v2.39h-6.856l-0.469 4.184c0.266-0.157 0.633-0.309 1.102-0.457 0.469-0.157 1.004-0.235 1.605-0.235 0.805 0 1.524 0.133 2.157 0.399 0.64 0.258 1.183 0.636 1.629 1.136 0.445 0.493 0.785 1.094 1.019 1.805 0.235 0.703 0.352 1.496 0.352 2.379 0 0.789-0.117 1.527-0.352 2.215-0.226 0.687-0.57 1.293-1.031 1.816-0.461 0.524-1.043 0.934-1.746 1.231-0.696 0.289-1.52 0.433-2.473 0.433-0.711 0-1.394-0.101-2.051-0.304-0.648-0.211-1.23-0.52-1.746-0.926-0.515-0.414-0.929-0.922-1.242-1.524-0.312-0.609-0.492-1.308-0.539-2.097h2.766c0.07 0.555 0.222 1.027 0.457 1.418 0.242 0.383 0.562 0.676 0.961 0.879 0.398 0.203 0.859 0.304 1.382 0.304 0.477 0 0.887-0.082 1.231-0.246 0.344-0.172 0.629-0.414 0.855-0.726 0.235-0.321 0.407-0.696 0.516-1.125 0.117-0.43 0.176-0.907 0.176-1.43 0-0.5-0.067-0.957-0.199-1.371-0.125-0.414-0.317-0.774-0.575-1.078-0.25-0.305-0.566-0.539-0.949-0.703-0.383-0.172-0.824-0.258-1.324-0.258-0.672 0-1.188 0.098-1.547 0.293-0.352 0.195-0.68 0.437-0.984 0.726zm22.047-2.027v2.789c0 1.336-0.133 2.477-0.399 3.422-0.258 0.938-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.274-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.257-0.57-0.172-1.086-0.442-1.546-0.809-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.098-0.172-0.812-0.258-1.754-0.258-2.824v-2.789c0-1.344 0.133-2.477 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.012 1.758-1.277 0.688-0.266 1.453-0.399 2.297-0.399 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.359 0.851 0.828 1.172 1.406 0.328 0.571 0.578 1.262 0.75 2.075 0.172 0.804 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.61c0-0.679-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.965-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.891-0.226-0.234-0.484-0.402-0.773-0.504-0.289-0.109-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.852-0.539 1.453c-0.117 0.594-0.176 1.317-0.176 2.168v3.61c0 0.687 0.039 1.293 0.117 1.816s0.196 0.973 0.352 1.348c0.156 0.367 0.344 0.671 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.515 0.297 0.11 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.257 0.344-0.172 0.633-0.446 0.867-0.821 0.235-0.383 0.41-0.879 0.528-1.488 0.117-0.609 0.175-1.34 0.175-2.191z" fill="#000" fill-opacity=".38"/>
<path d="m275.51 480.12v2.25h-12.305l-0.094-1.699 7.36-11.531h2.261l-2.449 4.195-4.23 6.785h9.457zm-2.133-10.98v17.062h-2.824v-17.062h2.824zm15.391 7.101v2.789c0 1.336-0.133 2.477-0.399 3.422-0.258 0.938-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.274-1.442 0.411-2.286 0.411-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.442-1.546-0.809-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.097-0.172-0.813-0.258-1.754-0.258-2.825v-2.789c0-1.343 0.133-2.476 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.012 1.758-1.277 0.688-0.266 1.453-0.399 2.297-0.399 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.36 0.851 0.828 1.172 1.407 0.328 0.57 0.578 1.261 0.75 2.074 0.172 0.804 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.61c0-0.679-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.964-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.89-0.226-0.235-0.484-0.403-0.773-0.504-0.289-0.11-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.257-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.852-0.539 1.453c-0.117 0.594-0.176 1.317-0.176 2.168v3.61c0 0.687 0.039 1.293 0.117 1.816 0.078 0.524 0.196 0.973 0.352 1.348 0.156 0.367 0.344 0.672 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.446 0.867-0.821c0.235-0.382 0.41-0.878 0.528-1.488 0.117-0.609 0.175-1.34 0.175-2.191z" fill="#000" fill-opacity=".38"/>
<path d="m267.07 554.09h1.688c0.656 0 1.199-0.113 1.629-0.34 0.437-0.227 0.761-0.539 0.972-0.938 0.211-0.398 0.317-0.855 0.317-1.371 0-0.539-0.098-1-0.293-1.382-0.188-0.391-0.477-0.692-0.867-0.903-0.383-0.211-0.872-0.316-1.465-0.316-0.5 0-0.953 0.101-1.36 0.304-0.398 0.196-0.715 0.477-0.949 0.844-0.234 0.36-0.351 0.789-0.351 1.289h-2.836c0-0.906 0.238-1.711 0.714-2.414 0.477-0.703 1.125-1.254 1.946-1.652 0.828-0.406 1.758-0.609 2.789-0.609 1.101 0 2.062 0.183 2.883 0.55 0.828 0.36 1.472 0.899 1.933 1.617 0.461 0.719 0.692 1.61 0.692 2.672 0 0.485-0.114 0.977-0.34 1.477-0.227 0.5-0.563 0.957-1.008 1.371-0.445 0.406-1 0.738-1.664 0.996-0.664 0.25-1.434 0.375-2.309 0.375h-2.121v-1.57zm0 2.203v-1.547h2.121c1 0 1.852 0.117 2.555 0.352 0.711 0.234 1.289 0.558 1.734 0.972 0.446 0.407 0.77 0.871 0.973 1.395 0.211 0.523 0.316 1.078 0.316 1.664 0 0.797-0.144 1.508-0.433 2.133-0.281 0.617-0.684 1.14-1.207 1.57-0.524 0.43-1.137 0.754-1.84 0.973-0.695 0.218-1.453 0.328-2.273 0.328-0.735 0-1.438-0.102-2.11-0.305s-1.273-0.504-1.805-0.902c-0.531-0.407-0.953-0.91-1.265-1.512-0.305-0.609-0.457-1.312-0.457-2.109h2.824c0 0.507 0.117 0.957 0.352 1.347 0.242 0.383 0.578 0.684 1.007 0.903 0.438 0.218 0.938 0.328 1.5 0.328 0.594 0 1.106-0.106 1.536-0.317 0.429-0.211 0.757-0.523 0.984-0.937 0.234-0.414 0.352-0.914 0.352-1.5 0-0.664-0.129-1.203-0.387-1.617s-0.625-0.719-1.102-0.914c-0.476-0.204-1.039-0.305-1.687-0.305h-1.688zm21.696-2.356v2.79c0 1.335-0.133 2.476-0.399 3.421-0.258 0.938-0.633 1.7-1.125 2.286-0.492 0.585-1.082 1.015-1.769 1.289-0.68 0.273-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.441-1.546-0.809-0.461-0.367-0.856-0.839-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.097-0.172-0.813-0.258-1.754-0.258-2.824v-2.79c0-1.343 0.133-2.476 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.011 1.758-1.277 0.688-0.266 1.453-0.398 2.297-0.398 0.68 0 1.305 0.085 1.875 0.257 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.36 0.851 0.829 1.172 1.407 0.328 0.57 0.578 1.261 0.75 2.074 0.172 0.805 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.609c0-0.68-0.039-1.278-0.117-1.793-0.078-0.524-0.195-0.965-0.351-1.325-0.149-0.367-0.336-0.664-0.563-0.89-0.226-0.235-0.484-0.403-0.773-0.504-0.289-0.11-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.429-0.867 0.796-0.234 0.368-0.414 0.852-0.539 1.454-0.117 0.593-0.176 1.316-0.176 2.168v3.609c0 0.687 0.039 1.293 0.117 1.816 0.078 0.524 0.196 0.973 0.352 1.348 0.156 0.367 0.344 0.672 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.445 0.867-0.82c0.235-0.383 0.41-0.879 0.528-1.489 0.117-0.609 0.175-1.339 0.175-2.191z" fill="#000" fill-opacity=".38"/>
<path d="m275.2 639.35v2.25h-11.437v-1.934l5.554-6.059c0.61-0.687 1.09-1.281 1.442-1.781 0.351-0.5 0.598-0.949 0.738-1.347 0.149-0.407 0.223-0.801 0.223-1.184 0-0.539-0.102-1.012-0.305-1.418-0.195-0.414-0.484-0.738-0.867-0.973-0.383-0.242-0.848-0.363-1.395-0.363-0.632 0-1.164 0.137-1.593 0.41-0.43 0.274-0.754 0.653-0.973 1.137-0.219 0.476-0.328 1.023-0.328 1.641h-2.824c0-0.993 0.226-1.899 0.679-2.719 0.453-0.828 1.11-1.485 1.969-1.969 0.86-0.492 1.895-0.738 3.106-0.738 1.14 0 2.109 0.191 2.906 0.574s1.402 0.926 1.816 1.629c0.422 0.703 0.633 1.535 0.633 2.496 0 0.531-0.086 1.059-0.258 1.582s-0.418 1.047-0.738 1.57c-0.313 0.516-0.684 1.035-1.113 1.559-0.43 0.516-0.903 1.039-1.418 1.57l-3.692 4.067h7.875zm13.575-7.711v2.789c0 1.336-0.133 2.476-0.399 3.422-0.258 0.937-0.633 1.699-1.125 2.285s-1.082 1.015-1.769 1.289c-0.68 0.273-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.441-1.546-0.808-0.461-0.368-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.286-0.75-2.098-0.172-0.813-0.258-1.754-0.258-2.824v-2.789c0-1.344 0.133-2.477 0.399-3.399 0.265-0.929 0.644-1.683 1.136-2.261 0.492-0.586 1.078-1.012 1.758-1.278 0.688-0.265 1.453-0.398 2.297-0.398 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.425 1.547 0.785 0.461 0.359 0.851 0.828 1.172 1.406 0.328 0.57 0.578 1.262 0.75 2.074 0.172 0.805 0.258 1.742 0.258 2.813zm-2.825 3.187v-3.609c0-0.68-0.039-1.278-0.117-1.793-0.078-0.524-0.195-0.965-0.351-1.324-0.149-0.368-0.336-0.664-0.563-0.891-0.226-0.234-0.484-0.402-0.773-0.504-0.289-0.109-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.851-0.539 1.453c-0.117 0.594-0.176 1.316-0.176 2.168v3.609c0 0.688 0.039 1.293 0.117 1.817 0.078 0.523 0.196 0.972 0.352 1.347 0.156 0.367 0.344 0.672 0.562 0.914 0.227 0.235 0.485 0.407 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.445 0.867-0.82c0.235-0.383 0.41-0.879 0.528-1.488 0.117-0.61 0.175-1.34 0.175-2.192z" fill="#000" fill-opacity=".38"/>
<path d="m271.47 702.18v17.121h-2.824v-13.77l-4.184 1.418v-2.332l6.668-2.437h0.34zm17.301 7.16v2.789c0 1.336-0.133 2.476-0.399 3.422-0.258 0.937-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.273-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.441-1.546-0.808-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.098-0.172-0.812-0.258-1.754-0.258-2.824v-2.789c0-1.344 0.133-2.477 0.399-3.399 0.265-0.929 0.644-1.683 1.136-2.261 0.492-0.586 1.078-1.012 1.758-1.278 0.688-0.265 1.453-0.398 2.297-0.398 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.359 0.851 0.828 1.172 1.406 0.328 0.57 0.578 1.262 0.75 2.074 0.172 0.805 0.258 1.743 0.258 2.813zm-2.825 3.187v-3.609c0-0.68-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.965-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.891-0.226-0.234-0.484-0.402-0.773-0.504-0.289-0.109-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.851-0.539 1.453c-0.117 0.594-0.176 1.316-0.176 2.168v3.609c0 0.688 0.039 1.293 0.117 1.817 0.078 0.523 0.196 0.972 0.352 1.347 0.156 0.368 0.344 0.672 0.562 0.914 0.227 0.235 0.485 0.407 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.445 0.867-0.82c0.235-0.383 0.41-0.879 0.528-1.488 0.117-0.61 0.175-1.34 0.175-2.192z" fill="#000" fill-opacity=".38"/>
<path d="m288.77 787.04v2.789c0 1.336-0.133 2.477-0.399 3.422-0.258 0.938-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.274-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.257-0.57-0.172-1.086-0.442-1.546-0.809-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.098-0.172-0.812-0.258-1.754-0.258-2.824v-2.789c0-1.344 0.133-2.477 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.012 1.758-1.277 0.688-0.266 1.453-0.399 2.297-0.399 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.359 0.851 0.828 1.172 1.406 0.328 0.571 0.578 1.262 0.75 2.075 0.172 0.804 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.61c0-0.679-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.965-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.891-0.226-0.234-0.484-0.402-0.773-0.504-0.289-0.109-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.852-0.539 1.453c-0.117 0.594-0.176 1.317-0.176 2.168v3.61c0 0.687 0.039 1.293 0.117 1.816s0.196 0.973 0.352 1.348c0.156 0.367 0.344 0.671 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.515 0.297 0.11 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.257 0.344-0.172 0.633-0.446 0.867-0.821 0.235-0.383 0.41-0.879 0.528-1.488 0.117-0.609 0.175-1.34 0.175-2.191z" fill="#000" fill-opacity=".38"/>
</g>
<path d="m807.17 0s-807.17 0-807.17 134v656.72c0 5.3024 21.491 9.2808 48 9.2808h2304c26.508 0 48-3.9784 48-9.2808v-656.72c0-134-792.84-134-792.84-134h-407.15zm804.84 162.4c-15.464 0-28 2.5072-28 5.6v600.8c0 3.0928 12.536 5.6 28 5.6h175.99c15.464 0 28-2.5072 28-5.6v-600.8c0-3.0928-12.536-5.6-28-5.6z" fill="#000" fill-opacity="0" style="stroke-width:6.9281" tb:tag="clickArea"/>
<g fill="#d12730" style="display:none" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g>
<g transform="translate(0 -4.9442)" fill="#faa405" style="display:none" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 48 KiB

352
application/src/main/data/json/system/scada_symbols/pump-hp.svg

@ -0,0 +1,352 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="400" height="400" fill="none" version="1.1" viewBox="0 0 400 400"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Centrifugal pump",
"description": "Centrifugal pump with configurable connectors and various states.",
"searchTags": [
"pump",
"centrifugal"
],
"widgetSizeX": 2,
"widgetSizeY": 2,
"stateRenderFunction": "\n",
"tags": [
{
"tag": "background",
"stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "circle-background",
"stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = '#cccccc';\n}\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "running",
"name": "{i18n:scada.symbol.running}",
"hint": "{i18n:scada.symbol.running-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.running}",
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "running"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning-state}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "critical",
"name": "{i18n:scada.symbol.critical-state}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "criticalAnimation",
"name": "{i18n:scada.symbol.critical-state-animation}",
"hint": "{i18n:scada.symbol.critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warningClick",
"name": "{i18n:scada.symbol.warning-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
},
{
"id": "criticalClick",
"name": "{i18n:scada.symbol.critical-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
}
],
"properties": [
{
"id": "runningColor",
"name": "{i18n:scada.symbol.running-color}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "stoppedColor",
"name": "{i18n:scada.symbol.stopped-color}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.warning-color}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.critical-color}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<g fill="#fff" tb:tag="background">
<path d="m114.5 309h144.33l51.173 66h-247l51.497-66z"/>
<path d="m65.041 374 49.942-64.008h143.36l49.628 64.008h-242.93z" stroke="#000" stroke-opacity=".87" stroke-width="1.9925"/>
<rect x="23.77" y="373.35" width="325.82" height="26.648" rx="4.6492"/>
<rect x="24.766" y="374.35" width="323.82" height="24.656" rx="3.6529" stroke="#000" stroke-opacity=".87" stroke-width="1.9925"/>
<rect x="175" width="225" height="133.67"/>
<rect x="176.34" y="1.3357" width="222.33" height="130.99" stroke="#000" stroke-opacity=".87" stroke-width="2.6715"/>
<circle cx="186.5" cy="186.5" r="186.5"/>
<circle cx="186.5" cy="186.5" r="185.16" stroke="#000" stroke-opacity=".87" stroke-width="2.6715"/>
<ellipse cx="186.69" cy="186.82" rx="120.18" ry="119.92"/>
<path d="m305.86 186.82c0 65.672-53.356 118.91-119.18 118.91-65.821 0-119.18-53.242-119.18-118.91 0-65.673 53.356-118.92 119.18-118.92 65.822 0 119.18 53.242 119.18 118.92z" stroke="#000" stroke-opacity=".12" stroke-width="2.0026"/>
<g clip-path="url(#clip0_3214_60796)">
<path d="m182.79 111.96c-9.073-23.303-20.123-33.549-26.376-41.692l-34.461 0.9063-24.336 29.24c1.9967-2.1678 12.577-0.11 38.925 25.46 26.347 25.571 35.77 48.871 37.187 57.324l18.509-12.372c0-18.168-0.375-35.563-9.448-58.866z" stroke="#727171" stroke-width="2.1802"/>
<path d="m254.2 148.49c16.446-18.838 20.404-33.379 24.722-42.693l-16.773-30.117-37.188-8.0152c2.847 0.765 5.926 11.094-4.523 46.292-10.449 35.197-26.73 54.344-33.564 59.518l19.54 10.668c16.101-8.42 31.341-16.813 47.786-35.652z" stroke="#727171" stroke-width="2.1802"/>
<path d="m265.64 225.84c24.877 2.544 39.033-2.623 49.131-4.478l14.898-31.087-14.935-34.987c1.019 2.765-5.634 11.245-40.402 23.044-34.769 11.799-59.802 9.567-67.973 6.976l-4.25 20.522c16.172 8.28 38.654 17.466 63.531 20.01z" stroke="#727171" stroke-width="2.1802"/>
<path d="m122.4 225.38c-17.502 17.861-28.126 32.344-32.973 41.394l15.011 31.033 36.665 10.143c-2.797-0.928-5.278-11.417 7.18-45.955 12.459-34.537 29.814-52.715 36.935-57.487l-15.061-11.313c-16.558 7.479-30.255 14.324-47.757 32.185z" stroke="#727171" stroke-width="2.1802"/>
<path d="m190.19 260.06c8.234 23.613 17.831 35.594 23.789 43.955l34.471 0.327 25.366-28.349c-2.073 2.094-16.862-0.212-42.278-26.709-25.415-26.497-32.543-40.913-33.657-49.412l-14.467 4.29c-0.65 18.157-1.457 32.286 6.776 55.898z" stroke="#727171" stroke-width="2.1802"/>
<path d="m120.32 154.41c-24.432-5.331-39.08-1.792-49.323-1.087l-18.306 29.211 10.898 36.447c-0.7013-2.863 6.8652-10.539 42.741-18.345 35.877-7.805 65.052-9.653 72.879-6.158l-2.121-15.449c-15.136-10.05-32.336-19.288-56.768-24.619z" stroke="#727171" stroke-width="2.1802"/>
<circle cx="186.72" cy="187.12" r="24.872" fill="#ccc" stroke="#000" stroke-opacity=".87" stroke-width="2.1802" tb:tag="circle-background"/>
<circle cx="186.72" cy="187.12" r="12.436" fill="#727171"/>
</g>
</g><mask id="path-20-inside-1_3214_60796" fill="white">
<path d="m306.86 186.82c0 66.227-53.806 119.92-120.18 119.92-66.372 0-120.18-53.689-120.18-119.92 0-66.228 53.806-119.92 120.18-119.92 66.373 0 120.18 53.689 120.18 119.92zm-221.64 0c0 55.911 45.424 101.24 101.46 101.24 56.034 0 101.46-45.325 101.46-101.24 0-55.912-45.424-101.24-101.46-101.24-56.033 0-101.46 45.325-101.46 101.24z"/>
</mask><path d="m306.86 186.82c0 66.227-53.806 119.92-120.18 119.92-66.372 0-120.18-53.689-120.18-119.92 0-66.228 53.806-119.92 120.18-119.92 66.373 0 120.18 53.689 120.18 119.92zm-221.64 0c0 55.911 45.424 101.24 101.46 101.24 56.034 0 101.46-45.325 101.46-101.24 0-55.912-45.424-101.24-101.46-101.24-56.033 0-101.46 45.325-101.46 101.24z" fill="#ccc" mask="url(#path-20-inside-1_3214_60796)" stroke="#000" stroke-width="5.3402" tb:tag="circle-background"/><g fill="#D12730" style="display: none;" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059h4.9805zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><g fill="#faa405" style="display: none;" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><defs>
<clipPath id="clip0_3214_60796">
<rect x="66.508" y="66.9" width="240.36" height="239.83" rx="119.92" fill="#fff"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

87
application/src/main/data/json/system/scada_symbols/right-tee-connector-hp.svg

@ -0,0 +1,87 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="200" fill="none" version="1.1" viewBox="0 0 200 200"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Right tee connector",
"description": "Right tee connector",
"widgetSizeX": 1,
"widgetSizeY": 1,
"tags": [
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"actions": null
},
{
"tag": "line-color",
"stateRenderFunction": "element.fill(ctx.properties.lineColor);\nelement.stroke(ctx.properties.lineColor);",
"actions": null
}
],
"behavior": [],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": null,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"type": "color",
"default": "#1A1A1A",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<path d="M200 100H115C106.716 100 100 106.716 100 115V200" stroke="#1A1A1A" stroke-width="6" id="path2" tb:tag="line"/><path d="M200 100H115C106.716 100 100 93.2843 100 85V0" stroke="#1A1A1A" stroke-width="6" id="path4" tb:tag="line"/><path d="M100 0V200" stroke="#1A1A1A" stroke-width="6" id="path6" tb:tag="line"/><path d="M100 113V87C100 87 100 100 114 100C100 100 100 113 100 113Z" fill="#1A1A1A" stroke="#1A1A1A" stroke-width="2" id="path8" tb:tag="line-color"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

636
application/src/main/data/json/system/scada_symbols/sand-filter-hp.svg

@ -0,0 +1,636 @@
<svg xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="600" height="1e3" fill="none" version="1.1" viewBox="0 0 600 1e3" id="svg100" sodipodi:docname="sand-filter.svg" inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Sand filter",
"description": "Sand filter with configurable filtration mode option and various states.",
"searchTags": [
"filter",
"sand"
],
"widgetSizeX": 3,
"widgetSizeY": 5,
"stateRenderFunction": "var running = ctx.values.running;\nif (running) {\n ctx.api.enable(ctx.tags.filterMode);\n} else {\n ctx.api.disable(ctx.tags.filterMode);\n}",
"tags": [
{
"tag": "background",
"stateRenderFunction": "var color = ctx.properties.stoppedColor;\nif (ctx.values.running) {\n color = ctx.properties.runningColor;\n}\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "filterMode",
"stateRenderFunction": "var defaultBorderColor = ctx.properties.defaultBorderColor;\nvar activeBorderColor = ctx.properties.activeBorderColor;\nvar defaultLabelColor = ctx.properties.defaultLabelColor;\nvar activeLabelColor = ctx.properties.activeLabelColor;\nvar activeBoxBackground = ctx.properties.activeBoxBackground;\nvar defaultBoxBackground = ctx.properties.defaultBoxBackground;\n\nvar running = ctx.values.running;\nvar filtrationMode = ctx.values.filtrationMode;\n\nvar runningColor = ctx.properties.stoppedColor;\n\nif (running) {\n runningColor = ctx.properties.runningColor;\n}\nelement.children().forEach((value) => {\n value.children()[0].fill(runningColor);\n});\n\nvar filtrationMap = {};\nvar bottomShift = 104;\nvar rightShift = 226;\nvar middleShift = 121;\n\nvar filterModeSet = element.remember('filterModeSet');\n\nvar i = 0;\nif (ctx.properties.filtrationMode) {\n i++;\n filtrationMap[i] = 'filter';\n}\nif (ctx.properties.wasteMode) {\n i++;\n filtrationMap[i] = 'waste';\n}\nif (ctx.properties.backwashMode) {\n i++;\n filtrationMap[i] = 'backwash';\n}\nif (ctx.properties.recirculateMode) {\n i++;\n filtrationMap[i] = 'recirculate';\n}\nif (ctx.properties.rinseMode) {\n i++;\n filtrationMap[i] = 'rinse';\n}\nif (ctx.properties.closedMode) {\n i++;\n filtrationMap[i] = 'closed';\n}\n\nif (!filterModeSet) {\n element.remember('filterModeSet', true);\n var clone = element.children()[0];\n setFilterModeColors(clone);\n element.clear();\n \n var filterMode = Object.values(filtrationMap);\n var lastToMiddle = filterMode.length % 2;\n \n filterMode.forEach((mode, index, arr) => {\n var template = clone.clone();\n var x = (index % 2) * rightShift;\n var y = Math.floor((index % filterMode.length) / 2) * bottomShift;\n if (index === filterMode.length-1 && lastToMiddle) {\n x = middleShift;\n }\n template.attr({'class': mode}).css('cursor', 'pointer').translate(x, y);\n ctx.api.text(template.findOne('text'), capitalizeFirstLetter(mode));\n template.click((event) => click(event, getFilterModeKey(mode)));\n element.add(template);\n })\n}\n\nif (isFinite(filtrationMode)) {\n if (element.findOne('.active')) {\n setFilterModeColors(element.findOne('.active'));\n }\n setFilterModeColorsByMap(filtrationMode, running);\n}\n\nfunction click(event, index) {\n var filtrationMode = ctx.values.filtrationMode;\n if (ctx.values.running && isFinite(filtrationMode)) {\n ctx.api.disable(element.children());\n var newValue = +index;\n if (newValue === filtrationMode) {\n newValue = 0;\n } else {\n setFilterModeColorsByMap(filtrationMode);\n }\n ctx.api.setValue('filtrationMode', newValue);\n ctx.api.callAction(event, 'filtrationModeUpdateState', newValue, {\n next: () => {\n setFilterModeColorsByMap(newValue ? newValue: filtrationMode, newValue);\n ctx.api.enable(element.children());\n },\n error: () => {\n setFilterModeColorsByMap(newValue);\n ctx.api.setValue('filtrationMode', filtrationMode);\n ctx.api.enable(element.children());\n }\n });\n }\n}\n\nfunction getFilterModeKey(value) {\n return Object.keys(filtrationMap).find(key => filtrationMap[key] === value);\n}\n\nfunction setFilterModeColorsByMap(mode, active = false) {\n var filterBox = element.findOne('g.'+filtrationMap[mode])\n if (filterBox) {\n return setFilterModeColors(filterBox, active);\n }\n}\n\nfunction setFilterModeColors(filterBox, active = false) {\n if (filterBox) {\n if (active) {\n filterBox.addClass('active');\n } else {\n filterBox.removeClass('active');\n }\n var borderColor = active ? activeBorderColor : defaultBorderColor;\n var labelColor = active ? activeLabelColor : defaultLabelColor;\n var boxColor = active ? activeBoxBackground : defaultBoxBackground;\n if (running) {\n filterBox.children()[0].fill(boxColor);\n }\n filterBox.children()[1].stroke(borderColor);\n ctx.api.font(filterBox.findOne('text'), null, labelColor);\n }\n}\n\nfunction capitalizeFirstLetter(string) {\n return string.charAt(0).toUpperCase() + string.slice(1);\n}",
"actions": null
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "running",
"name": "{i18n:scada.symbol.running}",
"hint": "{i18n:scada.symbol.running-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.running}",
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SHARED_SCOPE",
"key": "running"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "filtrationMode",
"name": "{i18n:scada.symbol.filtration-mode}",
"hint": "{i18n:scada.symbol.filtration-mode-hint}",
"group": null,
"type": "value",
"valueType": "INTEGER",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": 0,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SERVER_SCOPE",
"key": "filtrationMode"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "filtrationModeUpdateState",
"name": "{i18n:scada.symbol.filtration-mode-update}",
"hint": "{i18n:scada.symbol.filtration-mode-update-hint}",
"group": null,
"type": "action",
"valueType": "INTEGER",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": {
"action": "SET_ATTRIBUTE",
"executeRpc": {
"method": "setState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"setAttribute": {
"scope": "SHARED_SCOPE",
"key": "filtrationMode"
},
"putTimeSeries": {
"key": "state"
},
"valueToData": {
"type": "VALUE",
"constantValue": false,
"valueToDataFunction": "/* Convert input boolean value to RPC parameters or attribute/time-series value */\nreturn value;"
}
},
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning-state}",
"hint": "{i18n:scada.symbol.warning-state}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "critical",
"name": "{i18n:scada.symbol.critical-state}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "criticalAnimation",
"name": "{i18n:scada.symbol.critical-state-animation}",
"hint": "{i18n:scada.symbol.critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warningClick",
"name": "{i18n:scada.symbol.warning-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
},
{
"id": "criticalClick",
"name": "{i18n:scada.symbol.critical-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
}
],
"properties": [
{
"id": "filtrationMode",
"name": "{i18n:scada.symbol.filter-mode}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "wasteMode",
"name": "{i18n:scada.symbol.waste-mode}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "backwashMode",
"name": "{i18n:scada.symbol.backwash-mode}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "recirculateMode",
"name": "{i18n:scada.symbol.recirculate-mode}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "rinseMode",
"name": "{i18n:scada.symbol.rinse-mode}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "closedMode",
"name": "{i18n:scada.symbol.closed-mode}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "runningColor",
"name": "runningColor",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "stoppedColor",
"name": "{i18n:scada.symbol.stopped-color}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.warning-color}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.critical-color}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "activeBoxBackground",
"name": "{i18n:scada.symbol.mode-box-background}",
"type": "color",
"default": "#999999",
"required": null,
"subLabel": "Active",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "column-xs",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "defaultBoxBackground",
"name": "{i18n:scada.symbol.mode-box-background}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": "Default",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "column-xs",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "activeBorderColor",
"name": "{i18n:scada.symbol.border-color}",
"type": "color",
"default": "#999999",
"required": null,
"subLabel": "Active",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "column-xs",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "defaultBorderColor",
"name": "{i18n:scada.symbol.border-color}",
"type": "color",
"default": "#0000001F",
"required": null,
"subLabel": "Default",
"divider": false,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "activeLabelColor",
"name": "{i18n:scada.symbol.label-color}",
"type": "color",
"default": "#000000C2",
"required": null,
"subLabel": "Active",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "column-xs",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "defaultLabelColor",
"name": "{i18n:scada.symbol.label-color}",
"type": "color",
"default": "#00000061",
"required": null,
"subLabel": "Default",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<defs id="defs104"/><sodipodi:namedview id="namedview102" pagecolor="#505050" bordercolor="#eeeeee" borderopacity="1" inkscape:pageshadow="0" inkscape:pageopacity="0" inkscape:pagecheckerboard="0" showgrid="false" inkscape:zoom="3.0311311" inkscape:cx="241.65896" inkscape:cy="429.37766" inkscape:window-width="1920" inkscape:window-height="1136" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:current-layer="svg100"/><g fill="#fff" tb:tag="background" id="g30">
<path d="m137.58 946h324.84l53.581 24h-432l53.581-24z" id="path2"/>
<path d="m88.679 969 49.116-22h324.41l49.116 22h-422.64z" stroke="#000" stroke-opacity=".87" stroke-width="2" id="path4"/>
<path d="m0 388c0-99.411 80.588-180 180-180h120 120c99.411 0 180 80.589 180 180v412c0 81.738-66.262 148-148 148h-304c-81.738 0-148-66.262-148-148v-412z" id="path6"/>
<path d="m1 388c0-98.859 80.14-179 179-179h120 120c98.858 0 179 80.141 179 179v412c0 81.186-65.814 147-147 147h-304c-81.186 0-147-65.814-147-147v-412z" stroke="#000" stroke-opacity=".87" stroke-width="2" id="path8"/>
<rect x="80" y="968" width="440" height="32" rx="6.9621" id="rect10"/>
<rect x="81" y="969" width="438" height="30" rx="5.9621" stroke="#000" stroke-opacity=".87" stroke-width="2" id="rect12"/>
<path d="m216 209c0 0.552 0.448 1 1 1h166c0.552 0 1-0.448 1-1v-75c0-0.552-0.448-1-1-1h-166c-0.552 0-1 0.448-1 1v75z" id="path14"/>
<path d="m217 209h166v-75h-166v75z" stroke="#000" stroke-opacity=".87" stroke-width="2" id="path16"/>
<path d="m216 66c0 0.5523 0.448 1 1 1l166 2e-4c0.552 0 1-0.4477 1-1v-64c0-1.1046-0.895-2-2-2h-164c-1.105 0-2 0.89543-2 2v64z" id="path18"/>
<path d="m217 66 166 2e-4v-64c0-0.55228-0.448-1-1-1h-164c-0.552 0-1 0.44772-1 1v64z" stroke="#000" stroke-opacity=".87" stroke-width="2" id="path20"/>
<path d="m428.05 136.02h-46.554v72.008h42.554c2.209 0 4 1.791 4 4v60.008s0 30.002 16.001 46.004c18.004 18.004 44.006 18.004 44.006 18.004h112.01v-72.009h-108.01c-2.209 0-4-1.791-4-4v-56.007s0-30.004-12.002-46.006c-16.502-22.002-48.005-22.002-48.005-22.002z" id="path22"/>
<path d="m429.05 272.03v-60.008c0-2.761-2.239-5-5-5h-41.554v-70.008h45.553 1e-3 3e-3 0.014 0.062c0.057 1e-3 0.142 2e-3 0.255 5e-3 0.227 5e-3 0.565 0.015 1.003 0.036 0.877 0.043 2.152 0.127 3.736 0.297 3.17 0.339 7.569 1.018 12.475 2.376 9.827 2.719 21.591 8.133 29.657 18.888 5.849 7.798 8.82 19.097 10.314 28.56 0.744 4.712 1.116 8.93 1.302 11.969 0.093 1.519 0.14 2.742 0.163 3.584 0.011 0.421 0.017 0.746 0.02 0.965 1e-3 0.11 2e-3 0.193 3e-3 0.248v0.061 0.015 3e-3 56.008c0 2.762 2.238 5 5 5h107.01v70.009h-111.02-0.012-0.05c-0.046-1e-3 -0.116-2e-3 -0.21-4e-3 -0.187-4e-3 -0.467-0.013-0.831-0.03-0.73-0.034-1.797-0.103-3.136-0.242-2.68-0.277-6.443-0.832-10.761-1.943-8.647-2.223-19.463-6.659-28.296-15.492-7.797-7.797-11.745-19.07-13.731-28.501-0.989-4.701-1.484-8.908-1.732-11.939-0.123-1.515-0.185-2.735-0.216-3.573-0.015-0.419-0.023-0.743-0.027-0.96-2e-3 -0.109-3e-3 -0.191-3e-3 -0.246v-0.06-0.014-3e-3 -1e-3z" stroke="#000" stroke-opacity=".87" stroke-width="2" id="path24"/>
<rect y="64" width="600" height="72" id="rect26"/>
<rect x="1" y="65" width="598" height="70" stroke="#000" stroke-opacity=".87" stroke-width="2" id="rect28"/>
</g><path d="m201.79 0s-201.79 0-201.79 167.5v820.9c0 6.628 5.3726 11.601 12 11.601h576c6.6269 0 12-4.9729 12-11.601v-820.9c0-167.5-198.21-167.5-198.21-167.5h-101.79zm201.21 203c-3.866 0-7 3.134-7 7v751.01c0 3.866 3.134 7 7 7h43.999c3.866 0 7-3.134 7-7v-751.01c0-3.866-3.134-7-7-7z" fill="#000" fill-opacity="0" tb:tag="clickArea" id="path32"/><g fill="#d12730" style="display: none;" tb:tag="critical" id="g42">
<rect width="84" height="84" rx="4" fill="#fff" style="" id="rect34"/>
<rect width="84" height="84" rx="4" style="" id="rect36"/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style="" id="rect38"/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style="" id="path40"/>
</g><g fill="#faa405" style="display: none;" tb:tag="warning" id="g52">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style="" id="path44"/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style="" id="path46"/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style="" id="path48"/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style="" id="path50"/>
</g><g id="g152" tb:tag="filterMode">
<g id="g107">
<rect x="75" y="410" width="208.86" height="96.012" rx="9.8143" fill="#999" id="rect54"/>
<path d="M 76,419.814 C 76,414.946 79.9463,411 84.8143,411 h 189.24 c 4.868,0 8.814,3.946 8.814,8.814 v 76.383 c 0,4.868 -3.946,8.815 -8.814,8.815 H 84.8143 C 79.9463,505.012 76,501.065 76,496.197 Z" stroke="#999" stroke-width="2" id="path62-3"/>
<text x="180.79984" y="460.00018" fill="#000000" font-family="Roboto" font-size="28px" font-weight="500" text-anchor="middle" xml:space="preserve" id="text58"><tspan dominant-baseline="middle" id="tspan56">Filter</tspan></text>
</g>
<g id="g112">
<path d="m75 539.7c0-5.42 4.394-9.814 9.8143-9.814h189.24c5.42 0 9.814 4.394 9.814 9.814v76.383c0 5.421-4.394 9.815-9.814 9.815h-189.24c-5.4203 0-9.8143-4.394-9.8143-9.815v-76.383z" fill="#fff" id="path60"/>
<path d="m76 539.7c0-4.868 3.9463-8.814 8.8143-8.814h189.24c4.868 0 8.814 3.946 8.814 8.814v76.383c0 4.868-3.946 8.815-8.814 8.815h-189.24c-4.868 0-8.8143-3.947-8.8143-8.815v-76.383z" stroke="#999" stroke-width="2" id="path62"/>
<text x="183.43652" y="579.61719" fill="#000000" font-family="Roboto" font-size="28px" font-weight="500" text-anchor="middle" xml:space="preserve" id="text66"><tspan dominant-baseline="middle" id="tspan64">Backwash</tspan></text>
</g>
<g id="g121">
<path d="m75 659.58c0-5.42 4.394-9.814 9.8143-9.814h189.24c5.42 0 9.814 4.394 9.814 9.814v76.384c0 5.42-4.394 9.814-9.814 9.814h-189.24c-5.4203 0-9.8143-4.394-9.8143-9.814v-76.384z" fill="#fff" id="path68"/>
<path d="m76 659.58c0-4.868 3.9463-8.814 8.8143-8.814h189.24c4.868 0 8.814 3.946 8.814 8.814v76.384c0 4.868-3.946 8.814-8.814 8.814h-189.24c-4.868 0-8.8143-3.946-8.8143-8.814v-76.384z" stroke="#999" stroke-width="2" id="path70"/>
<text x="181.64941" y="699.61719" fill="#000000" font-family="Roboto" font-size="28px" font-weight="500" text-anchor="middle" xml:space="preserve" id="text74"><tspan dominant-baseline="middle" id="tspan72">Rinse</tspan></text>
</g>
<g id="g127">
<path d="m315.69 419.81c0-5.42 4.394-9.814 9.814-9.814h189.24c5.42 0 9.815 4.394 9.815 9.814v76.383c0 5.421-4.395 9.815-9.815 9.815h-189.24c-5.42 0-9.814-4.394-9.814-9.814v-76.384z" fill="#fff" id="path76"/>
<path d="m316.69 419.81c0-4.868 3.946-8.814 8.814-8.814h189.24c4.868 0 8.815 3.946 8.815 8.814v76.383c0 4.869-3.947 8.815-8.815 8.815h-189.24c-4.868 0-8.814-3.946-8.814-8.814v-76.384z" stroke="#999" stroke-width="2" id="path78"/>
<text x="421.99316" y="459.75586" fill="#000000" font-family="Roboto" font-size="28px" font-weight="500" text-anchor="middle" xml:space="preserve" id="text82"><tspan dominant-baseline="middle" id="tspan80">Waste</tspan></text>
</g>
<g id="g137">
<path d="m315.69 539.7c0-5.42 4.394-9.814 9.815-9.814h189.24c5.42 0 9.814 4.394 9.814 9.814v76.383c0 5.421-4.394 9.815-9.814 9.815h-189.24c-5.421 0-9.815-4.394-9.815-9.815v-76.383z" fill="#fff" id="path84"/>
<path d="m316.69 539.7c0-4.868 3.947-8.814 8.815-8.814h189.24c4.868 0 8.814 3.946 8.814 8.814v76.383c0 4.868-3.946 8.815-8.814 8.815h-189.24c-4.868 0-8.815-3.947-8.815-8.815v-76.383z" stroke="#999" stroke-width="2" id="path86"/>
<text x="425.14062" y="579.61719" fill="#000000" font-family="Roboto" font-size="28px" font-weight="500" text-anchor="middle" xml:space="preserve" id="text90"><tspan dominant-baseline="middle" id="tspan88">Recirculate</tspan></text>
</g>
<g id="g143">
<path d="m315.69 659.58c0-5.42 4.394-9.814 9.815-9.814h189.24c5.42 0 9.814 4.394 9.814 9.814v76.383c0 5.42-4.394 9.814-9.814 9.814h-189.24c-5.421 0-9.815-4.394-9.815-9.814v-76.383z" fill="#fff" id="path92"/>
<path d="m316.69 659.58c0-4.868 3.947-8.814 8.815-8.814h189.24c4.868 0 8.814 3.946 8.814 8.814v76.383c0 4.868-3.946 8.814-8.814 8.814h-189.24c-4.868 0-8.815-3.946-8.815-8.814v-76.383z" stroke="#999" stroke-width="2" id="path94"/>
<text x="422.45215" y="699.61719" fill="black" font-family="Roboto" font-size="28px" font-weight="500" text-anchor="middle" xml:space="preserve" id="text98"><tspan dominant-baseline="middle" id="tspan96">Closed</tspan></text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 31 KiB

536
application/src/main/data/json/system/scada_symbols/short-vertical-tank-hp.svg

@ -0,0 +1,536 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="600" height="600" fill="none" version="1.1" viewBox="0 0 600 600"><tb:metadata xmlns=""><![CDATA[{
"title": "Short vertical tank",
"description": "Short vertical tank",
"searchTags": [
"short tank"
],
"widgetSizeX": 3,
"widgetSizeY": 3,
"tags": [
{
"tag": "background",
"stateRenderFunction": "element.attr({fill: ctx.properties.tankColor});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "fluid-background",
"stateRenderFunction": " var color = ctx.properties.fluidColor;\n element.attr({fill: color, 'fill-opacity': 1});\n \n var valueSet = element.remember('valueSet');\n if (!valueSet) {\n element.remember('valueSet', true);\n element.attr({height: 0});\n }\n \n var currentVolume = ctx.values.currentVolume; \n var tankCapacity = ctx.values.tankCapacity; \n\n var height = currentVolume / tankCapacity;\n height = Math.max(0, Math.min(1, height))*570; \n \n var elementHeight = element.remember('height');\n if (height !== elementHeight) {\n element.remember('height', height);\n element.attr({height: height});\n }",
"actions": null
},
{
"tag": "scale",
"stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 15;\n var majorIntervalLength = 570 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(170, y, 202, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 160, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(182, minorY, 202, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}",
"actions": null
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "tankCapacity",
"name": "{i18n:scada.symbol.tank-capacity}",
"hint": "{i18n:scada.symbol.tank-capacity-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": null,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SERVER_SCOPE",
"key": "tankCapacity"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "currentVolume",
"name": "{i18n:scada.symbol.current-volume}",
"hint": "{i18n:scada.symbol.current-volume-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "GET_TIME_SERIES",
"defaultValue": null,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "liquidVolume"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning-state}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "critical",
"name": "{i18n:scada.symbol.critical-state}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "criticalAnimation",
"name": "{i18n:scada.symbol.critical-state-animation}",
"hint": "{i18n:scada.symbol.critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warningClick",
"name": "{i18n:scada.symbol.warning-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
},
{
"id": "criticalClick",
"name": "{i18n:scada.symbol.critical-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
}
],
"properties": [
{
"id": "tankColor",
"name": "{i18n:scada.symbol.tank-color}",
"type": "color",
"default": "#EBEBEB",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "fluidColor",
"name": "{i18n:scada.symbol.fluid-color}",
"type": "color",
"default": "#C8DFF7",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "scale",
"name": "{i18n:scada.symbol.scale}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "majorIntervals",
"name": "{i18n:scada.symbol.major-ticks}",
"type": "number",
"default": 10,
"required": null,
"subLabel": "{i18n:scada.symbol.intervals}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": "scale",
"rowClass": "",
"fieldClass": "",
"min": 1,
"max": null,
"step": 1
},
{
"id": "majorFont",
"name": "{i18n:scada.symbol.major-ticks}",
"type": "font",
"default": {
"size": 24,
"sizeUnit": "px",
"family": "Roboto",
"weight": "500",
"style": "normal"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "scale",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "majorColor",
"name": "{i18n:scada.symbol.major-ticks-color}",
"type": "color",
"default": "#00000061",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "scale",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "minorIntervals",
"name": "{i18n:scada.symbol.minor-ticks}",
"type": "number",
"default": 5,
"required": null,
"subLabel": "{i18n:scada.symbol.intervals}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "scale",
"rowClass": "",
"fieldClass": "",
"min": 1,
"max": null,
"step": null
},
{
"id": "minorColor",
"name": "{i18n:scada.symbol.minor-ticks-color}",
"type": "color",
"default": "#0000001F",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "scale",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.warning-color}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.critical-color}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<path d="m0 120s0-120 140-120c124.5-5.6458e-4 194.5 2.7873e-4 320 0 140-3.1093e-4 140 120 140 120v360s0 120-140 120h-320c-140 0-140-120-140-120v-360z" fill="#EBEBEB" tb:tag="background"/><path d="m0.936 120h0.064l1e-5 -3e-3 5e-5 -0.014 4.3e-4 -0.061c5.1e-4 -0.054 0.00153-0.137 0.00361-0.247 0.00415-0.221 0.0125-0.551 0.02932-0.984 0.03365-0.865 0.10115-2.139 0.23656-3.764 0.27086-3.25 0.81325-7.901 1.8991-13.486 2.1725-11.173 6.5161-26.059 15.195-40.937 17.319-29.69 51.988-59.504 121.64-59.504 68.61-3.13e-4 120.67-2e-4 174.59-8e-5 43.92 1e-4 89.074 2e-4 145.41 8e-5 69.648-1.6e-4 104.32 29.814 121.64 59.504 8.679 14.878 13.022 29.764 15.195 40.937 1.086 5.585 1.628 10.236 1.899 13.486 0.135 1.625 0.203 2.899 0.237 3.764 6e-3 0.169 0.011 0.321 0.016 0.458 6e-3 0.215 0.01 0.391 0.013 0.526 2e-3 0.11 3e-3 0.193 4e-3 0.247v0.061 0.014 3e-3h0.051-0.051v360h0.064-0.064v3e-3 0.014 0.061c-1e-3 0.054-2e-3 0.137-4e-3 0.248-4e-3 0.22-0.013 0.55-0.029 0.983-0.034 0.865-0.102 2.139-0.237 3.764-0.271 3.251-0.813 7.902-1.899 13.486-2.173 11.173-6.516 26.06-15.195 40.937-17.319 29.69-51.988 59.504-121.64 59.504h-320c-69.648 0-104.32-29.814-121.64-59.504-8.6786-14.877-13.022-29.764-15.195-40.937-1.0859-5.584-1.6283-10.235-1.8991-13.486-0.13541-1.625-0.20291-2.899-0.23656-3.764-0.01682-0.433-0.02517-0.763-0.02932-0.983-0.00208-0.111-0.0031-0.194-0.00361-0.248l-4.3e-4 -0.061-5e-5 -0.014-1e-5 -3e-3h-0.064 0.064v-360h-0.064z" stroke="#000" stroke-opacity=".87" stroke-width="2"/><mask id="mask0_3610_179735" x="2" y="2" width="596" height="596" style="mask-type:alpha" maskUnits="userSpaceOnUse">
<path d="m2 121.2s0-119.2 139.07-119.2c123.67-5.6e-4 193.2 2.7e-4 317.87 0 139.07-3.1e-4 139.07 119.2 139.07 119.2v357.6s0 119.2-139.07 119.2h-317.87c-139.07 0-139.07-119.2-139.07-119.2v-357.6z" fill="#EBEBEB"/>
<path d="m2.9398 121.2h0.06019l1e-5 -3e-3 5e-5 -0.014 4.3e-4 -0.06c5e-4 -0.054 0.00152-0.136 0.00358-0.246 0.00412-0.219 0.01242-0.547 0.02913-0.977 0.03341-0.859 0.10046-2.125 0.23496-3.739 0.26903-3.228 0.80778-7.848 1.8864-13.395 2.1579-11.098 6.4724-25.884 15.093-40.662 17.202-29.49 51.638-59.104 120.82-59.104 68.154-3.1e-4 119.87-2e-4 173.43-8e-5 43.627 1e-4 88.479 2e-4 144.44 8e-5 69.181-1.6e-4 103.62 29.614 120.82 59.104 8.62 14.778 12.935 29.564 15.092 40.662 1.079 5.547 1.618 10.167 1.887 13.395 0.134 1.614 0.201 2.88 0.235 3.739 0.017 0.43 0.025 0.758 0.029 0.977 2e-3 0.11 3e-3 0.192 4e-3 0.246v0.06 0.014 3e-3h0.06-0.06v357.6h0.05-0.05v3e-3 0.014 0.06c-1e-3 0.054-2e-3 0.137-4e-3 0.246-4e-3 0.219-0.012 0.547-0.029 0.977-0.034 0.859-0.101 2.125-0.235 3.739-0.269 3.228-0.808 7.848-1.887 13.395-2.157 11.098-6.472 25.885-15.092 40.662-17.203 29.49-51.639 59.104-120.82 59.104h-317.87c-69.181 0-103.62-29.614-120.82-59.104-8.6202-14.777-12.935-29.564-15.093-40.662-1.0786-5.547-1.6173-10.167-1.8864-13.395-0.1345-1.614-0.20155-2.88-0.23496-3.739-0.01671-0.43-0.02501-0.758-0.02913-0.977-0.00206-0.109-0.00308-0.192-0.00358-0.246l-4.3e-4 -0.06-5e-5 -0.014-1e-5 -3e-3h-0.05001 0.05001v-357.6h-0.06019z" stroke="#000" stroke-opacity=".87" stroke-width="2"/>
</mask><g mask="url(#mask0_3610_179735)">
<rect transform="scale(1,-1)" x="2" y="-585" width="600" height="419" fill="#c8dff7" tb:tag="fluid-background"/>
</g><g tb:tag="scale">
<line x1="168" x2="200" y1="8.9727" y2="8.9727" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="180" x2="200" y1="20.613" y2="20.613" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="32.254" y2="32.254" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="43.894" y2="43.894" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="55.537" y2="55.537" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="168" x2="200" y1="67.178" y2="67.178" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="180" x2="200" y1="78.818" y2="78.818" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="90.459" y2="90.459" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="102.1" y2="102.1" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="113.74" y2="113.74" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="168" x2="200" y1="125.38" y2="125.38" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="180" x2="200" y1="137.02" y2="137.02" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="148.66" y2="148.66" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="160.3" y2="160.3" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="171.94" y2="171.94" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="168" x2="200" y1="183.59" y2="183.59" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="180" x2="200" y1="195.23" y2="195.23" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="206.87" y2="206.87" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="218.51" y2="218.51" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="230.15" y2="230.15" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="168" x2="200" y1="241.79" y2="241.79" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="180" x2="200" y1="253.43" y2="253.43" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="265.07" y2="265.07" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="276.71" y2="276.71" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="288.35" y2="288.35" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="168" x2="200" y1="299.99" y2="299.99" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="180" x2="200" y1="311.64" y2="311.64" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="323.28" y2="323.28" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="334.92" y2="334.92" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="346.56" y2="346.56" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="168" x2="200" y1="358.2" y2="358.2" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="180" x2="200" y1="369.84" y2="369.84" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="381.48" y2="381.48" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="393.12" y2="393.12" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="404.76" y2="404.76" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="168" x2="200" y1="416.4" y2="416.4" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="180" x2="200" y1="428.04" y2="428.04" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="439.69" y2="439.69" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="451.33" y2="451.33" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="462.97" y2="462.97" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="168" x2="200" y1="474.61" y2="474.61" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="180" x2="200" y1="486.25" y2="486.25" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="497.89" y2="497.89" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="509.53" y2="509.53" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="521.17" y2="521.17" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="168" x2="200" y1="532.81" y2="532.81" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="180" x2="200" y1="544.45" y2="544.45" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="556.1" y2="556.1" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="567.74" y2="567.74" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="180" x2="200" y1="579.38" y2="579.38" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="168" x2="200" y1="591.02" y2="591.02" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<path d="m127.58 2.8789v17.121h-2.824v-13.77l-4.184 1.418v-2.332l6.668-2.4375h0.34zm17.301 7.1602v2.789c0 1.336-0.133 2.4766-0.399 3.4219-0.257 0.9375-0.632 1.6992-1.125 2.2852-0.492 0.5859-1.082 1.0156-1.769 1.289-0.68 0.2735-1.441 0.4102-2.285 0.4102-0.672 0-1.297-0.086-1.875-0.2578-0.571-0.1719-1.086-0.4414-1.547-0.8086s-0.856-0.8399-1.184-1.418c-0.32-0.5859-0.57-1.2852-0.75-2.0977-0.172-0.8125-0.257-1.7539-0.257-2.8242v-2.789c0-1.3438 0.132-2.4766 0.398-3.3985 0.266-0.92968 0.644-1.6836 1.137-2.2617 0.492-0.58594 1.078-1.0117 1.757-1.2774 0.688-0.26562 1.454-0.39844 2.297-0.39844 0.68 0 1.305 0.08594 1.875 0.25782 0.578 0.16406 1.094 0.42578 1.547 0.78515 0.461 0.35938 0.852 0.82813 1.172 1.4062 0.328 0.57032 0.578 1.2617 0.75 2.0742 0.172 0.80469 0.258 1.7422 0.258 2.8125zm-2.824 3.1875v-3.6094c0-0.67969-0.039-1.2774-0.117-1.793-0.079-0.52344-0.196-0.96484-0.352-1.3242-0.148-0.36719-0.336-0.66406-0.563-0.89062-0.226-0.23438-0.484-0.40235-0.773-0.50391-0.289-0.10938-0.613-0.16406-0.973-0.16406-0.437 0-0.828 0.08593-1.172 0.25781-0.343 0.16406-0.632 0.42969-0.867 0.79687-0.234 0.36719-0.414 0.85157-0.539 1.4531-0.117 0.59375-0.176 1.3164-0.176 2.168v3.6094c0 0.6875 0.039 1.2929 0.118 1.8164 0.078 0.5234 0.195 0.9726 0.351 1.3476 0.156 0.3672 0.344 0.6719 0.563 0.9141 0.226 0.2344 0.484 0.4062 0.773 0.5156 0.297 0.1094 0.621 0.1641 0.973 0.1641 0.445 0 0.84-0.086 1.183-0.2578 0.344-0.1719 0.633-0.4454 0.868-0.8204 0.234-0.3828 0.41-0.8789 0.527-1.4882 0.117-0.6094 0.176-1.3399 0.176-2.1914zm16.715-3.1875v2.789c0 1.336-0.133 2.4766-0.399 3.4219-0.258 0.9375-0.633 1.6992-1.125 2.2852-0.492 0.5859-1.082 1.0156-1.769 1.289-0.68 0.2735-1.442 0.4102-2.286 0.4102-0.671 0-1.296-0.086-1.875-0.2578-0.57-0.1719-1.086-0.4414-1.546-0.8086-0.461-0.3672-0.856-0.8399-1.184-1.418-0.32-0.5859-0.57-1.2852-0.75-2.0977-0.172-0.8125-0.258-1.7539-0.258-2.8242v-2.789c0-1.3438 0.133-2.4766 0.399-3.3985 0.265-0.92968 0.644-1.6836 1.136-2.2617 0.492-0.58594 1.078-1.0117 1.758-1.2774 0.688-0.26562 1.453-0.39844 2.297-0.39844 0.68 0 1.305 0.08594 1.875 0.25782 0.578 0.16406 1.094 0.42578 1.547 0.78515 0.461 0.35938 0.851 0.82813 1.172 1.4062 0.328 0.57032 0.578 1.2617 0.75 2.0742 0.172 0.80469 0.258 1.7422 0.258 2.8125zm-2.825 3.1875v-3.6094c0-0.67969-0.039-1.2774-0.117-1.793-0.078-0.52344-0.195-0.96484-0.351-1.3242-0.149-0.36719-0.336-0.66406-0.563-0.89062-0.226-0.23438-0.484-0.40235-0.773-0.50391-0.289-0.10938-0.614-0.16406-0.973-0.16406-0.438 0-0.828 0.08593-1.172 0.25781-0.344 0.16406-0.633 0.42969-0.867 0.79687-0.234 0.36719-0.414 0.85157-0.539 1.4531-0.117 0.59375-0.176 1.3164-0.176 2.168v3.6094c0 0.6875 0.039 1.2929 0.117 1.8164 0.078 0.5234 0.196 0.9726 0.352 1.3476 0.156 0.3672 0.344 0.6719 0.562 0.9141 0.227 0.2344 0.485 0.4062 0.774 0.5156 0.297 0.1094 0.621 0.1641 0.972 0.1641 0.446 0 0.84-0.086 1.184-0.2578 0.344-0.1719 0.633-0.4454 0.867-0.8204 0.235-0.3828 0.41-0.8789 0.528-1.4882 0.117-0.6094 0.175-1.3399 0.175-2.1914z" fill="#000" fill-opacity=".38"/>
<path d="m136.03 75.373h0.235c1.078 0 1.976-0.1406 2.695-0.4218 0.727-0.2891 1.305-0.6875 1.734-1.1953 0.43-0.5079 0.739-1.1055 0.926-1.793 0.188-0.6875 0.281-1.4336 0.281-2.2383v-2.9414c0-0.6953-0.074-1.3047-0.222-1.8281-0.141-0.5313-0.344-0.9727-0.61-1.3242-0.258-0.3594-0.558-0.6289-0.902-0.8086-0.336-0.1797-0.699-0.2696-1.09-0.2696-0.43 0-0.816 0.0977-1.16 0.293-0.336 0.1875-0.621 0.4492-0.856 0.7852-0.226 0.3281-0.402 0.7148-0.527 1.1601-0.117 0.4375-0.176 0.9063-0.176 1.4063 0 0.4687 0.055 0.9218 0.164 1.3593 0.118 0.4297 0.289 0.8125 0.516 1.1485 0.227 0.3359 0.512 0.6015 0.856 0.7969 0.343 0.1953 0.746 0.2929 1.207 0.2929 0.437 0 0.839-0.082 1.207-0.2461 0.367-0.1718 0.687-0.4023 0.961-0.6914 0.273-0.289 0.488-0.6133 0.644-0.9726 0.156-0.3594 0.242-0.7266 0.258-1.1016l1.078 0.3281c0 0.5938-0.125 1.1797-0.375 1.7578-0.242 0.5704-0.582 1.0938-1.02 1.5704-0.429 0.4687-0.933 0.8437-1.511 1.125-0.571 0.2812-1.192 0.4218-1.864 0.4218-0.812 0-1.531-0.1523-2.156-0.457-0.617-0.3125-1.133-0.7344-1.547-1.2656-0.406-0.5313-0.711-1.1407-0.914-1.8282s-0.304-1.4101-0.304-2.1679c0-0.8203 0.125-1.5899 0.375-2.3086 0.25-0.7188 0.613-1.3516 1.089-1.8985 0.477-0.5546 1.055-0.9843 1.735-1.289 0.687-0.3125 1.465-0.4688 2.332-0.4688 0.922 0 1.73 0.1797 2.426 0.5391 0.695 0.3594 1.281 0.8555 1.757 1.4883 0.477 0.6328 0.836 1.3672 1.079 2.2031 0.242 0.8359 0.363 1.7344 0.363 2.6953v0.9961c0 1.0078-0.09 1.9805-0.27 2.918-0.179 0.9297-0.472 1.7929-0.879 2.5898-0.398 0.7891-0.925 1.4844-1.582 2.086-0.648 0.5937-1.445 1.0586-2.39 1.3945-0.938 0.3281-2.039 0.4922-3.305 0.4922h-0.258v-2.3321zm22.739-7.7343v2.789c0 1.336-0.133 2.4766-0.399 3.4219-0.258 0.9375-0.633 1.6992-1.125 2.2852-0.492 0.5859-1.082 1.0156-1.769 1.289-0.68 0.2735-1.442 0.4102-2.286 0.4102-0.671 0-1.296-0.086-1.875-0.2578-0.57-0.1719-1.086-0.4414-1.546-0.8086-0.461-0.3672-0.856-0.8399-1.184-1.418-0.32-0.5859-0.57-1.2851-0.75-2.0976-0.172-0.8125-0.258-1.754-0.258-2.8243v-2.789c0-1.3438 0.133-2.4766 0.399-3.3985 0.265-0.9297 0.644-1.6836 1.136-2.2617 0.492-0.5859 1.078-1.0117 1.758-1.2773 0.688-0.2657 1.453-0.3985 2.297-0.3985 0.68 0 1.305 0.086 1.875 0.2578 0.578 0.1641 1.094 0.4258 1.547 0.7852 0.461 0.3594 0.851 0.8281 1.172 1.4063 0.328 0.5703 0.578 1.2617 0.75 2.0742 0.172 0.8047 0.258 1.7422 0.258 2.8125zm-2.825 3.1875v-3.6094c0-0.6797-0.039-1.2773-0.117-1.793-0.078-0.5234-0.195-0.9648-0.351-1.3242-0.149-0.3672-0.336-0.6641-0.563-0.8906-0.226-0.2344-0.484-0.4024-0.773-0.5039-0.289-0.1094-0.614-0.1641-0.973-0.1641-0.438 0-0.828 0.086-1.172 0.2578-0.344 0.1641-0.633 0.4297-0.867 0.7969s-0.414 0.8516-0.539 1.4531c-0.117 0.5938-0.176 1.3164-0.176 2.168v3.6094c0 0.6875 0.039 1.2929 0.117 1.8164 0.078 0.5234 0.196 0.9726 0.352 1.3476 0.156 0.3672 0.344 0.6719 0.562 0.9141 0.227 0.2344 0.485 0.4062 0.774 0.5156 0.297 0.1094 0.621 0.1641 0.972 0.1641 0.446 0 0.84-0.086 1.184-0.2578 0.344-0.1719 0.633-0.4453 0.867-0.8203 0.235-0.3829 0.41-0.8789 0.528-1.4883 0.117-0.6094 0.175-1.3399 0.175-2.1914z" fill="#000" fill-opacity=".38"/>
<path d="m144.91 130.55c0 1.062-0.246 1.957-0.738 2.683-0.492 0.727-1.164 1.278-2.016 1.653-0.844 0.367-1.797 0.551-2.859 0.551-1.063 0-2.02-0.184-2.871-0.551-0.852-0.375-1.524-0.926-2.016-1.653-0.492-0.726-0.738-1.621-0.738-2.683 0-0.703 0.136-1.34 0.41-1.91 0.273-0.578 0.66-1.075 1.16-1.489 0.508-0.421 1.102-0.746 1.781-0.972 0.688-0.227 1.438-0.34 2.25-0.34 1.078 0 2.043 0.199 2.895 0.598 0.851 0.398 1.519 0.949 2.004 1.652 0.492 0.703 0.738 1.523 0.738 2.461zm-2.836-0.141c0-0.57-0.117-1.07-0.351-1.5-0.235-0.429-0.563-0.761-0.985-0.996-0.422-0.234-0.91-0.351-1.465-0.351-0.562 0-1.05 0.117-1.465 0.351-0.414 0.235-0.738 0.567-0.972 0.996-0.227 0.43-0.34 0.93-0.34 1.5 0 0.578 0.113 1.078 0.34 1.5 0.226 0.414 0.551 0.731 0.972 0.949 0.422 0.219 0.918 0.329 1.489 0.329 0.57 0 1.062-0.11 1.476-0.329 0.414-0.218 0.735-0.535 0.961-0.949 0.227-0.422 0.34-0.922 0.34-1.5zm2.449-7.781c0 0.852-0.226 1.609-0.679 2.273-0.446 0.664-1.063 1.188-1.852 1.571-0.789 0.375-1.687 0.562-2.695 0.562-1.016 0-1.922-0.187-2.719-0.562-0.789-0.383-1.41-0.907-1.863-1.571-0.446-0.664-0.668-1.421-0.668-2.273 0-1.016 0.222-1.871 0.668-2.566 0.453-0.704 1.074-1.239 1.863-1.606s1.691-0.551 2.707-0.551 1.918 0.184 2.707 0.551 1.406 0.902 1.852 1.606c0.453 0.695 0.679 1.55 0.679 2.566zm-2.824 0.094c0-0.508-0.101-0.953-0.305-1.336-0.195-0.391-0.472-0.695-0.832-0.914-0.359-0.219-0.785-0.328-1.277-0.328s-0.918 0.105-1.277 0.316c-0.36 0.211-0.637 0.508-0.832 0.891-0.196 0.382-0.293 0.84-0.293 1.371 0 0.523 0.097 0.98 0.293 1.371 0.195 0.383 0.472 0.683 0.832 0.902 0.367 0.219 0.797 0.328 1.289 0.328s0.918-0.109 1.277-0.328c0.36-0.219 0.637-0.519 0.832-0.902 0.195-0.391 0.293-0.848 0.293-1.371zm17.067 2.519v2.789c0 1.336-0.133 2.477-0.399 3.422-0.258 0.938-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.274-1.442 0.411-2.286 0.411-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.442-1.546-0.809-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.097-0.172-0.813-0.258-1.754-0.258-2.825v-2.789c0-1.343 0.133-2.476 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.012 1.758-1.277 0.688-0.266 1.453-0.399 2.297-0.399 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.36 0.851 0.828 1.172 1.407 0.328 0.57 0.578 1.261 0.75 2.074 0.172 0.804 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.61c0-0.679-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.964-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.89-0.226-0.235-0.484-0.403-0.773-0.504-0.289-0.11-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.257-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.852-0.539 1.453c-0.117 0.594-0.176 1.317-0.176 2.168v3.61c0 0.687 0.039 1.293 0.117 1.816 0.078 0.524 0.196 0.973 0.352 1.348 0.156 0.367 0.344 0.672 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.446 0.867-0.821c0.235-0.382 0.41-0.878 0.528-1.488 0.117-0.609 0.175-1.34 0.175-2.191z" fill="#000" fill-opacity=".38"/>
<path d="m145.09 175.74v1.547l-6.82 15.516h-2.977l6.809-14.813h-8.836v-2.25h11.824zm13.68 7.102v2.789c0 1.336-0.133 2.476-0.399 3.422-0.258 0.937-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.273-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.441-1.546-0.808-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.098-0.172-0.812-0.258-1.754-0.258-2.824v-2.789c0-1.344 0.133-2.477 0.399-3.399 0.265-0.929 0.644-1.683 1.136-2.261 0.492-0.586 1.078-1.012 1.758-1.278 0.688-0.265 1.453-0.398 2.297-0.398 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.359 0.851 0.828 1.172 1.406 0.328 0.57 0.578 1.262 0.75 2.074 0.172 0.805 0.258 1.743 0.258 2.813zm-2.825 3.187v-3.609c0-0.68-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.965-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.891-0.226-0.234-0.484-0.402-0.773-0.504-0.289-0.109-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.851-0.539 1.453c-0.117 0.594-0.176 1.316-0.176 2.168v3.609c0 0.688 0.039 1.293 0.117 1.817 0.078 0.523 0.196 0.972 0.352 1.347 0.156 0.368 0.344 0.672 0.562 0.914 0.227 0.235 0.485 0.407 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.445 0.867-0.82c0.235-0.383 0.41-0.879 0.528-1.488 0.117-0.61 0.175-1.34 0.175-2.192z" fill="#000" fill-opacity=".38"/>
<path d="m142.29 233.23h0.352v2.309h-0.2c-1.007 0-1.879 0.156-2.613 0.469-0.726 0.312-1.324 0.742-1.793 1.289s-0.82 1.187-1.055 1.922c-0.226 0.726-0.339 1.5-0.339 2.32v2.684c0 0.679 0.074 1.281 0.222 1.804 0.149 0.516 0.356 0.95 0.621 1.301 0.274 0.344 0.586 0.606 0.938 0.785 0.351 0.18 0.73 0.27 1.137 0.27 0.421 0 0.804-0.086 1.148-0.258 0.344-0.18 0.637-0.426 0.879-0.738 0.242-0.313 0.426-0.684 0.551-1.114 0.125-0.429 0.187-0.898 0.187-1.406 0-0.484-0.062-0.937-0.187-1.359-0.117-0.43-0.293-0.805-0.528-1.125-0.234-0.328-0.527-0.582-0.879-0.762-0.343-0.187-0.742-0.281-1.195-0.281-0.562 0-1.074 0.133-1.535 0.398-0.453 0.266-0.82 0.614-1.102 1.043-0.273 0.422-0.421 0.871-0.445 1.348l-1.078-0.352c0.063-0.726 0.223-1.379 0.48-1.957 0.266-0.578 0.614-1.07 1.043-1.476 0.43-0.407 0.926-0.715 1.489-0.926 0.57-0.219 1.195-0.328 1.875-0.328 0.828 0 1.547 0.156 2.156 0.469 0.609 0.312 1.113 0.738 1.512 1.277 0.406 0.531 0.707 1.141 0.902 1.828 0.203 0.68 0.305 1.391 0.305 2.133 0 0.82-0.125 1.586-0.375 2.297-0.25 0.703-0.617 1.32-1.102 1.851-0.476 0.532-1.058 0.946-1.746 1.242-0.68 0.297-1.453 0.446-2.32 0.446-0.914 0-1.731-0.176-2.449-0.528-0.711-0.351-1.317-0.836-1.817-1.453-0.492-0.617-0.867-1.328-1.125-2.133-0.258-0.804-0.387-1.656-0.387-2.554v-1.172c0-1.297 0.164-2.52 0.493-3.668 0.328-1.156 0.832-2.176 1.511-3.059 0.688-0.882 1.567-1.574 2.637-2.074 1.07-0.508 2.348-0.762 3.832-0.762zm16.481 7.207v2.79c0 1.335-0.133 2.476-0.399 3.421-0.258 0.938-0.633 1.7-1.125 2.286-0.492 0.585-1.082 1.015-1.769 1.289-0.68 0.273-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.441-1.546-0.809-0.461-0.367-0.856-0.839-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.097-0.172-0.813-0.258-1.754-0.258-2.824v-2.79c0-1.343 0.133-2.476 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.011 1.758-1.277 0.688-0.266 1.453-0.398 2.297-0.398 0.68 0 1.305 0.085 1.875 0.257 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.36 0.851 0.829 1.172 1.407 0.328 0.57 0.578 1.261 0.75 2.074 0.172 0.805 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.609c0-0.68-0.039-1.278-0.117-1.793-0.078-0.524-0.195-0.965-0.351-1.325-0.149-0.367-0.336-0.664-0.563-0.89-0.226-0.235-0.484-0.403-0.773-0.504-0.289-0.11-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.429-0.867 0.796-0.234 0.368-0.414 0.852-0.539 1.454-0.117 0.593-0.176 1.316-0.176 2.168v3.609c0 0.687 0.039 1.293 0.117 1.816 0.078 0.524 0.196 0.973 0.352 1.348 0.156 0.367 0.344 0.672 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.445 0.867-0.82c0.235-0.383 0.41-0.879 0.528-1.489 0.117-0.609 0.175-1.339 0.175-2.191z" fill="#000" fill-opacity=".38"/>
<path d="m136.72 300.07-2.25-0.55 0.925-8.578h9.2v2.39h-6.856l-0.469 4.184c0.266-0.157 0.633-0.309 1.102-0.457 0.469-0.157 1.004-0.235 1.605-0.235 0.805 0 1.524 0.133 2.157 0.399 0.64 0.258 1.183 0.636 1.629 1.136 0.445 0.493 0.785 1.094 1.019 1.805 0.235 0.703 0.352 1.496 0.352 2.379 0 0.789-0.117 1.527-0.352 2.215-0.226 0.687-0.57 1.293-1.031 1.816-0.461 0.524-1.043 0.934-1.746 1.231-0.696 0.289-1.52 0.433-2.473 0.433-0.711 0-1.394-0.101-2.051-0.304-0.648-0.211-1.23-0.52-1.746-0.926-0.515-0.414-0.929-0.922-1.242-1.524-0.312-0.609-0.492-1.308-0.539-2.097h2.766c0.07 0.555 0.222 1.027 0.457 1.418 0.242 0.383 0.562 0.676 0.961 0.879 0.398 0.203 0.859 0.304 1.382 0.304 0.477 0 0.887-0.082 1.231-0.246 0.344-0.172 0.629-0.414 0.855-0.726 0.235-0.321 0.407-0.696 0.516-1.125 0.117-0.43 0.176-0.907 0.176-1.43 0-0.5-0.067-0.957-0.199-1.371-0.125-0.414-0.317-0.774-0.575-1.078-0.25-0.305-0.566-0.539-0.949-0.703-0.383-0.172-0.824-0.258-1.324-0.258-0.672 0-1.188 0.098-1.547 0.293-0.352 0.195-0.68 0.437-0.984 0.726zm22.047-2.027v2.789c0 1.336-0.133 2.477-0.399 3.422-0.258 0.938-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.274-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.257-0.57-0.172-1.086-0.442-1.546-0.809-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.098-0.172-0.812-0.258-1.754-0.258-2.824v-2.789c0-1.344 0.133-2.477 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.012 1.758-1.277 0.688-0.266 1.453-0.399 2.297-0.399 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.359 0.851 0.828 1.172 1.406 0.328 0.571 0.578 1.262 0.75 2.075 0.172 0.804 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.61c0-0.679-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.965-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.891-0.226-0.234-0.484-0.402-0.773-0.504-0.289-0.109-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.852-0.539 1.453c-0.117 0.594-0.176 1.317-0.176 2.168v3.61c0 0.687 0.039 1.293 0.117 1.816s0.196 0.973 0.352 1.348c0.156 0.367 0.344 0.671 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.515 0.297 0.11 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.257 0.344-0.172 0.633-0.446 0.867-0.821 0.235-0.383 0.41-0.879 0.528-1.488 0.117-0.609 0.175-1.34 0.175-2.191z" fill="#000" fill-opacity=".38"/>
<path d="m145.51 359.52v2.25h-12.305l-0.094-1.7 7.36-11.531h2.261l-2.449 4.195-4.23 6.786h9.457zm-2.133-10.981v17.063h-2.824v-17.063h2.824zm15.391 7.102v2.789c0 1.336-0.133 2.476-0.399 3.422-0.258 0.937-0.633 1.699-1.125 2.285s-1.082 1.015-1.769 1.289c-0.68 0.273-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.441-1.546-0.808-0.461-0.368-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.286-0.75-2.098-0.172-0.813-0.258-1.754-0.258-2.824v-2.789c0-1.344 0.133-2.477 0.399-3.399 0.265-0.929 0.644-1.683 1.136-2.261 0.492-0.586 1.078-1.012 1.758-1.278 0.688-0.265 1.453-0.398 2.297-0.398 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.425 1.547 0.785 0.461 0.359 0.851 0.828 1.172 1.406 0.328 0.57 0.578 1.262 0.75 2.074 0.172 0.805 0.258 1.742 0.258 2.813zm-2.825 3.187v-3.609c0-0.68-0.039-1.278-0.117-1.793-0.078-0.524-0.195-0.965-0.351-1.324-0.149-0.368-0.336-0.664-0.563-0.891-0.226-0.234-0.484-0.402-0.773-0.504-0.289-0.109-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.851-0.539 1.453c-0.117 0.594-0.176 1.316-0.176 2.168v3.609c0 0.688 0.039 1.293 0.117 1.817 0.078 0.523 0.196 0.972 0.352 1.347 0.156 0.367 0.344 0.672 0.562 0.914 0.227 0.235 0.485 0.407 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.445 0.867-0.82c0.235-0.383 0.41-0.879 0.528-1.488 0.117-0.61 0.175-1.34 0.175-2.192z" fill="#000" fill-opacity=".38"/>
<path d="m137.07 413.39h1.688c0.656 0 1.199-0.114 1.629-0.34 0.437-0.227 0.761-0.539 0.972-0.938 0.211-0.398 0.317-0.855 0.317-1.371 0-0.539-0.098-1-0.293-1.383-0.188-0.39-0.477-0.691-0.867-0.902-0.383-0.211-0.872-0.316-1.465-0.316-0.5 0-0.953 0.101-1.36 0.304-0.398 0.196-0.715 0.477-0.949 0.844-0.234 0.359-0.351 0.789-0.351 1.289h-2.836c0-0.906 0.238-1.711 0.714-2.414 0.477-0.703 1.125-1.254 1.946-1.652 0.828-0.407 1.758-0.61 2.789-0.61 1.101 0 2.062 0.184 2.883 0.551 0.828 0.359 1.472 0.899 1.933 1.617 0.461 0.719 0.692 1.61 0.692 2.672 0 0.485-0.114 0.977-0.34 1.477-0.227 0.5-0.563 0.957-1.008 1.371-0.445 0.406-1 0.738-1.664 0.996-0.664 0.25-1.434 0.375-2.309 0.375h-2.121v-1.57zm0 2.203v-1.547h2.121c1 0 1.852 0.117 2.555 0.351 0.711 0.235 1.289 0.559 1.734 0.973 0.446 0.406 0.77 0.871 0.973 1.395 0.211 0.523 0.316 1.078 0.316 1.664 0 0.797-0.144 1.508-0.433 2.132-0.281 0.618-0.684 1.141-1.207 1.571-0.524 0.429-1.137 0.754-1.84 0.972-0.695 0.219-1.453 0.329-2.273 0.329-0.735 0-1.438-0.102-2.11-0.305s-1.273-0.504-1.805-0.902c-0.531-0.407-0.953-0.911-1.265-1.512-0.305-0.61-0.457-1.313-0.457-2.11h2.824c0 0.508 0.117 0.957 0.352 1.348 0.242 0.383 0.578 0.684 1.007 0.902 0.438 0.219 0.938 0.329 1.5 0.329 0.594 0 1.106-0.106 1.536-0.317 0.429-0.211 0.757-0.523 0.984-0.937 0.234-0.414 0.352-0.914 0.352-1.5 0-0.664-0.129-1.203-0.387-1.618-0.258-0.414-0.625-0.718-1.102-0.914-0.476-0.203-1.039-0.304-1.687-0.304h-1.688zm21.696-2.356v2.789c0 1.336-0.133 2.477-0.399 3.422-0.258 0.938-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.274-1.442 0.411-2.286 0.411-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.442-1.546-0.809-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.097-0.172-0.813-0.258-1.754-0.258-2.825v-2.789c0-1.343 0.133-2.476 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.012 1.758-1.277 0.688-0.266 1.453-0.399 2.297-0.399 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.36 0.851 0.828 1.172 1.407 0.328 0.57 0.578 1.261 0.75 2.074 0.172 0.804 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.61c0-0.679-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.964-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.89-0.226-0.235-0.484-0.403-0.773-0.504-0.289-0.11-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.257-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.852-0.539 1.453c-0.117 0.594-0.176 1.317-0.176 2.168v3.61c0 0.687 0.039 1.293 0.117 1.816 0.078 0.524 0.196 0.973 0.352 1.348 0.156 0.367 0.344 0.672 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.446 0.867-0.821c0.235-0.382 0.41-0.878 0.528-1.488 0.117-0.609 0.175-1.34 0.175-2.191z" fill="#000" fill-opacity=".38"/>
<path d="m145.2 478.55v2.25h-11.437v-1.934l5.554-6.058c0.61-0.688 1.09-1.282 1.442-1.782 0.351-0.5 0.598-0.949 0.738-1.347 0.149-0.407 0.223-0.801 0.223-1.184 0-0.539-0.102-1.012-0.305-1.418-0.195-0.414-0.484-0.738-0.867-0.973-0.383-0.242-0.848-0.363-1.395-0.363-0.632 0-1.164 0.137-1.593 0.41-0.43 0.274-0.754 0.653-0.973 1.137-0.219 0.477-0.328 1.023-0.328 1.641h-2.824c0-0.992 0.226-1.899 0.679-2.719 0.453-0.828 1.11-1.484 1.969-1.969 0.86-0.492 1.895-0.738 3.106-0.738 1.14 0 2.109 0.191 2.906 0.574s1.402 0.926 1.816 1.629c0.422 0.703 0.633 1.535 0.633 2.496 0 0.531-0.086 1.059-0.258 1.582-0.172 0.524-0.418 1.047-0.738 1.57-0.313 0.516-0.684 1.036-1.113 1.559-0.43 0.516-0.903 1.039-1.418 1.57l-3.692 4.067h7.875zm13.575-7.711v2.789c0 1.336-0.133 2.476-0.399 3.422-0.258 0.937-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.273-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.441-1.546-0.808-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.098-0.172-0.812-0.258-1.754-0.258-2.824v-2.789c0-1.344 0.133-2.477 0.399-3.399 0.265-0.929 0.644-1.683 1.136-2.261 0.492-0.586 1.078-1.012 1.758-1.278 0.688-0.265 1.453-0.398 2.297-0.398 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.359 0.851 0.828 1.172 1.406 0.328 0.57 0.578 1.262 0.75 2.074 0.172 0.805 0.258 1.743 0.258 2.813zm-2.825 3.187v-3.609c0-0.68-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.965-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.891-0.226-0.234-0.484-0.402-0.773-0.504-0.289-0.109-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.851-0.539 1.453c-0.117 0.594-0.176 1.316-0.176 2.168v3.609c0 0.688 0.039 1.293 0.117 1.817 0.078 0.523 0.196 0.972 0.352 1.347 0.156 0.368 0.344 0.672 0.562 0.914 0.227 0.235 0.485 0.407 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.445 0.867-0.82c0.235-0.383 0.41-0.879 0.528-1.488 0.117-0.61 0.175-1.34 0.175-2.192z" fill="#000" fill-opacity=".38"/>
<path d="m141.47 521.28v17.121h-2.824v-13.769l-4.184 1.418v-2.332l6.668-2.438h0.34zm17.301 7.16v2.79c0 1.335-0.133 2.476-0.399 3.421-0.258 0.938-0.633 1.7-1.125 2.286-0.492 0.585-1.082 1.015-1.769 1.289-0.68 0.273-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.441-1.546-0.809-0.461-0.367-0.856-0.839-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.097-0.172-0.813-0.258-1.754-0.258-2.824v-2.79c0-1.343 0.133-2.476 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.011 1.758-1.277 0.688-0.266 1.453-0.398 2.297-0.398 0.68 0 1.305 0.085 1.875 0.257 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.36 0.851 0.829 1.172 1.407 0.328 0.57 0.578 1.261 0.75 2.074 0.172 0.805 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.609c0-0.68-0.039-1.278-0.117-1.793-0.078-0.524-0.195-0.965-0.351-1.325-0.149-0.367-0.336-0.664-0.563-0.89-0.226-0.235-0.484-0.403-0.773-0.504-0.289-0.11-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.429-0.867 0.796-0.234 0.368-0.414 0.852-0.539 1.454-0.117 0.593-0.176 1.316-0.176 2.168v3.609c0 0.687 0.039 1.293 0.117 1.816 0.078 0.524 0.196 0.973 0.352 1.348 0.156 0.367 0.344 0.672 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.445 0.867-0.82c0.235-0.383 0.41-0.879 0.528-1.489 0.117-0.609 0.175-1.339 0.175-2.191z" fill="#000" fill-opacity=".38"/>
<path d="m158.77 586.04v2.789c0 1.336-0.133 2.477-0.399 3.422-0.258 0.938-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.274-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.257-0.57-0.172-1.086-0.442-1.546-0.809-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.098-0.172-0.812-0.258-1.754-0.258-2.824v-2.789c0-1.344 0.133-2.477 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.012 1.758-1.277 0.688-0.266 1.453-0.399 2.297-0.399 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.359 0.851 0.828 1.172 1.406 0.328 0.571 0.578 1.262 0.75 2.075 0.172 0.804 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.61c0-0.679-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.965-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.891-0.226-0.234-0.484-0.402-0.773-0.504-0.289-0.109-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.852-0.539 1.453c-0.117 0.594-0.176 1.317-0.176 2.168v3.61c0 0.687 0.039 1.293 0.117 1.816s0.196 0.973 0.352 1.348c0.156 0.367 0.344 0.671 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.515 0.297 0.11 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.257 0.344-0.172 0.633-0.446 0.867-0.821 0.235-0.383 0.41-0.879 0.528-1.488 0.117-0.609 0.175-1.34 0.175-2.191z" fill="#000" fill-opacity=".38"/>
</g><path d="m201.79 0s-201.79 0-201.79 100.5v492.54c0 3.9768 5.3727 6.9606 12 6.9606h576c6.627 0 12-2.9838 12-6.9606v-492.54c0-100.5-198.21-100.5-198.21-100.5h-101.79zm201.21 121.8c-3.866 0-7 1.8804-7 4.2v450.6c0 2.3196 3.134 4.2 7 4.2h43.998c3.866 0 7-1.8804 7-4.2v-450.6c0-2.3196-3.134-4.2-7-4.2z" fill="#000" fill-opacity="0" style="stroke-width:.43301" tb:tag="clickArea"/><g fill="#d12730" style="display: none;" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><g transform="translate(0 -4.9442)" fill="#faa405" style="display: none;" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 50 KiB

751
application/src/main/data/json/system/scada_symbols/simple-horizontal-scale-hp.svg

@ -0,0 +1,751 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="800" height="400" fill="none" version="1.1" viewBox="0 0 800 400"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Simple horizontal scale",
"description": "Simple horizontal scale with current volume value and configurable warning and critical scale.",
"searchTags": [
"scale"
],
"widgetSizeX": 4,
"widgetSizeY": 2,
"tags": [
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "highCriticalScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalValue = ctx.properties.showHighCriticalScale;\nvar highCriticalValue = ctx.properties.highCriticalScale;\n\nif (showHighCriticalValue) {\n element.show();\n var offset = calculateOffset(highCriticalValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(653-offset);\n childrenElement[1].height(653-offset-1);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "highCriticalScaleBackground",
"stateRenderFunction": "var value = ctx.values.value;\nvar showHighCriticalValue = ctx.properties.showHighCriticalScale;\n\nif (showHighCriticalValue && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (value >= highCriticalScale && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"actions": null
},
{
"tag": "highWarningScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningValue = ctx.properties.highWarningScale;\nif (showHighWarningScale) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(653-offset);\n childrenElement[1].height(653-offset-1);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "highWarningScaleBackground",
"stateRenderFunction": "var value = ctx.values.value;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\n\nif (showHighWarningScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (!showHighCriticalScale) {\n highCriticalScale = ctx.properties.maxValue;\n }\n var highWarningScale = ctx.properties.highWarningScale;\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}",
"actions": null
},
{
"tag": "label",
"stateRenderFunction": "if (ctx.properties.label) {\n element.show();\n ctx.api.font(element, ctx.properties.labelFont, ctx.properties.labelColor);\n ctx.api.text(element, ctx.properties.labelText);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "lowCriticalScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.properties.lowCriticalScale;\nif (showLowCriticalScale) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(offset);\n childrenElement[1].height(offset-1);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "lowCriticalScaleBackground",
"stateRenderFunction": "var value = ctx.values.value;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"actions": null
},
{
"tag": "lowWarningScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar lowWarningValue = ctx.properties.lowWarningScale;\nif (showLowWarningScale) {\n element.show();\n var offset = calculateOffset(lowWarningValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(offset);\n childrenElement[1].height(offset-1);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "lowWarningScaleBackground",
"stateRenderFunction": "var value = ctx.values.value;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\n\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (!showLowCriticalScale) {\n lowCriticalScale = ctx.properties.minValue;\n }\n var lowWarningScale = ctx.properties.lowWarningScale;\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}",
"actions": null
},
{
"tag": "maxValue",
"stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.properties.maxValue);\n}",
"actions": null
},
{
"tag": "minMaxValue",
"stateRenderFunction": "if (ctx.properties.minMaxValue) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "minValue",
"stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.properties.minValue);\n}",
"actions": null
},
{
"tag": "scaleBackground",
"stateRenderFunction": "element.fill(ctx.properties.scaleColor);",
"actions": null
},
{
"tag": "target",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showTarget = ctx.properties.showTarget;\nvar targetValue = ctx.values.targetValue;\n\nif (showTarget && targetValue !== null) {\n element.show();\n var offset = calculateOffset(targetValue, minValue, maxValue);\n element.transform({translateX: -(Math.min(Math.max(offset-17, 0), 617))});\n} else {\n element.hide();\n}\n\n",
"actions": null
},
{
"tag": "targetBackground",
"stateRenderFunction": "element.fill(ctx.properties.targetColor);",
"actions": null
},
{
"tag": "units",
"stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.properties.units);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "value",
"stateRenderFunction": "if (ctx.properties.value !== null) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, null, ctx.properties.valueDecimals));\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "valuePointer",
"stateRenderFunction": "element.fill(ctx.properties.arrowColor);",
"actions": null
},
{
"tag": "valuePosition",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar value = ctx.values.value;\n\nif (value !== null) {\n var offset = calculateOffset(value, minValue, maxValue);\n element.transform({translateX: -offset});\n}\n",
"actions": null
}
],
"behavior": [
{
"id": "value",
"name": "{i18n:scada.symbol.value}",
"hint": "{i18n:scada.symbol.value-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "GET_TIME_SERIES",
"defaultValue": null,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "temperature"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "targetValue",
"name": "{i18n:scada.symbol.target-value}",
"hint": "{i18n:scada.symbol.target-value-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": null,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SERVER_SCOPE",
"key": "target"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
}
],
"properties": [
{
"id": "minMaxValue",
"name": "{i18n:scada.symbol.min-max-value}",
"type": "switch",
"default": false,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "minValue",
"name": "{i18n:scada.symbol.min-max-value}",
"type": "number",
"default": 0,
"required": true,
"subLabel": "{i18n:scada.symbol.min-value}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": 1
},
{
"id": "maxValue",
"name": "{i18n:scada.symbol.min-max-value}",
"type": "number",
"default": 100,
"required": true,
"subLabel": "{i18n:scada.symbol.max-value}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": 1
},
{
"id": "showHighCriticalScale",
"name": "{i18n:scada.symbol.high-critical-scale}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "highCriticalScale",
"name": "{i18n:scada.symbol.high-critical-scale}",
"type": "number",
"default": 85,
"required": null,
"subLabel": "",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "showHighCriticalScale",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": 1
},
{
"id": "showHighWarningScale",
"name": "{i18n:scada.symbol.high-warning-scale}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "highWarningScale",
"name": "{i18n:scada.symbol.high-warning-scale}",
"type": "number",
"default": 70,
"required": null,
"subLabel": "",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "showHighWarningScale",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": 1
},
{
"id": "showLowWarningScale",
"name": "{i18n:scada.symbol.low-warning-scale}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "lowWarningScale",
"name": "{i18n:scada.symbol.low-warning-scale}",
"type": "number",
"default": 30,
"required": null,
"subLabel": "",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "showLowWarningScale",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": 1
},
{
"id": "showLowCriticalScale",
"name": "{i18n:scada.symbol.low-critical-scale}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "lowCriticalScale",
"name": "{i18n:scada.symbol.low-critical-scale}",
"type": "number",
"default": 15,
"required": null,
"subLabel": "",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "showLowCriticalScale",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": 1
},
{
"id": "value",
"name": "{i18n:scada.symbol.value}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "valueDecimals",
"name": "{i18n:scada.symbol.value}",
"type": "number",
"default": 0,
"required": true,
"subLabel": "Decimals",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "value",
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 10,
"step": 1
},
{
"id": "valueFont",
"name": "{i18n:scada.symbol.value}",
"type": "font",
"default": {
"size": 60,
"sizeUnit": "px",
"family": "Roboto",
"weight": "400",
"style": "normal"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "value",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "valueColor",
"name": "{i18n:scada.symbol.value}",
"type": "color",
"default": "#002878",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "value",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "label",
"name": "{i18n:scada.symbol.label}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "labelText",
"name": "{i18n:scada.symbol.label}",
"type": "text",
"default": "Outdoor",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "label",
"rowClass": "",
"fieldClass": "flex",
"min": null,
"max": null,
"step": null
},
{
"id": "labelFont",
"name": "{i18n:scada.symbol.label}",
"type": "font",
"default": {
"size": 56,
"sizeUnit": "px",
"family": "Roboto",
"weight": "500",
"style": "normal"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "label",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "labelColor",
"name": "{i18n:scada.symbol.label}",
"type": "color",
"default": "#000000DE",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "label",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "showUnits",
"name": "{i18n:scada.symbol.units}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "units",
"name": "{i18n:scada.symbol.units}",
"type": "units",
"default": "°C",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "showUnits",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "unitsFont",
"name": "{i18n:scada.symbol.units}",
"type": "font",
"default": {
"size": 56,
"sizeUnit": "px",
"family": "Roboto",
"weight": "500",
"style": "normal"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "showUnits",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "unitsColor",
"name": "{i18n:scada.symbol.units}",
"type": "color",
"default": "#000000DE",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "showUnits",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "arrowColor",
"name": "{i18n:scada.symbol.arrow-color}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "showTarget",
"name": "{i18n:scada.symbol.target}",
"type": "switch",
"default": false,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "targetColor",
"name": "{i18n:scada.symbol.target}",
"type": "color",
"default": "#DEDEDE",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "showTarget",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "scaleColor",
"name": "{i18n:scada.symbol.scale-color}",
"type": "color",
"default": "#C8DFF7",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "defaultWarningScaleColor",
"name": "{i18n:scada.symbol.warning-scale-color}",
"type": "color",
"default": "#EBEBEB",
"required": null,
"subLabel": "Default",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "activeWarningScaleColor",
"name": "{i18n:scada.symbol.warning-scale-color}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": "Active",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "defaultCriticalScaleColor",
"name": "{i18n:scada.symbol.critical-scale-color}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": "Default",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "activeCriticalScaleColor",
"name": "{i18n:scada.symbol.critical-scale-color}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": "Active",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<text x="409.16602" y="113.23438" fill="#000000" font-family="Roboto" font-size="56px" font-weight="400" text-anchor="middle" tb:tag="label" xml:space="preserve"><tspan transform="translate(0,-144)" dominant-baseline="middle">Outdoor</tspan></text><text x="765.21875" y="225.25" fill="#000000" font-family="Roboto" font-size="56px" font-weight="400" text-anchor="middle" tb:tag="units" xml:space="preserve"><tspan transform="translate(0,-144)" dominant-baseline="middle">°C</tspan></text><g fill="#666" font-family="Roboto" font-size="32px" font-weight="500" text-anchor="middle" style="display: none;" tb:tag="minMaxValue">
<text x="99.023438" y="268" tb:tag="maxValue" xml:space="preserve" style=""><tspan dominant-baseline="middle">100</tspan></text>
<text x="717.07031" y="268" tb:tag="minValue" xml:space="preserve" style=""><tspan dominant-baseline="middle">0</tspan></text>
</g><g transform="translate(-350)" tb:tag="valuePosition">
<path d="m724 191 24-42h-48z" fill="#666" tb:tag="valuePointer"/>
<text x="723.7793" y="295.82501" fill="#002878" font-family="Roboto" font-size="60px" font-weight="400" text-anchor="middle" tb:tag="value" xml:space="preserve"><tspan transform="translate(0,-144)" dominant-baseline="middle">26</tspan></text>
</g><g tb:tag="scale">
<rect transform="rotate(-90)" x="-242" y="72" width="42" height="653" fill="#c8dff7" style="stroke-width:.9497" tb:tag="scaleBackground"/>
<rect transform="rotate(-90)" x="-241.52" y="72.475" width="41.05" height="652.05" stroke="#000" stroke-opacity=".87" style="stroke-width:.95024"/>
<g tb:tag="highWarningScale">
<rect transform="rotate(-90)" x="-242" y="72" width="42" height="153.12" fill="#ebebeb" style="stroke-width:.94905" tb:tag="highWarningScaleBackground"/>
<rect transform="rotate(-90)" x="-241.53" y="72.475" width="41.051" height="152.17" stroke="#000" stroke-opacity=".87" style="stroke-width:.94948"/>
</g>
<g tb:tag="highCriticalScale">
<rect transform="rotate(-90)" x="-242" y="72" width="42" height="81.062" fill="#666" style="stroke-width:.94905" tb:tag="highCriticalScaleBackground"/>
<rect transform="rotate(-90)" x="-241.53" y="72.475" width="41.051" height="80.113" stroke="#000" stroke-opacity=".87" style="stroke-width:.94934"/>
</g>
<g transform="rotate(90 700.5 265.5)" tb:tag="lowWarningScale">
<rect transform="scale(-1,1)" x="-677" y="241" width="42" height="153.12" fill="#ebebeb" style="stroke-width:.94905" tb:tag="lowWarningScaleBackground"/>
<rect transform="scale(-1,1)" x="-676.53" y="241.47" width="41.051" height="152.17" stroke="#000" stroke-opacity=".87" style="stroke-width:.94948"/>
</g>
<g transform="rotate(90 700.5 265.5)" tb:tag="lowCriticalScale">
<rect transform="scale(-1,1)" x="-677" y="241" width="42" height="81.062" fill="#666" style="stroke-width:.94905" tb:tag="lowCriticalScaleBackground"/>
<rect transform="scale(-1,1)" x="-676.53" y="241.48" width="41.051" height="80.113" stroke="#000" stroke-opacity=".87" style="stroke-width:.94934"/>
</g>
</g><g transform="translate(-180)" tb:tag="target">
<rect transform="rotate(45)" x="644.15" y="-355.65" width="24" height="24" fill="#dedede" tb:tag="targetBackground"/>
<rect transform="rotate(45)" x="645.15" y="-354.65" width="22" height="22" stroke="#000" stroke-opacity=".87" stroke-width="2"/>
</g><path d="m269.06 0s-269.06 0-269.06 67v328.36c0 2.6512 7.1634 4.6404 16 4.6404h768c8.836 0 16-1.9892 16-4.6404v-328.36c0-67-264.28-67-264.28-67h-135.72zm268.28 81.2c-5.1546 0-9.3334 1.2536-9.3334 2.8v300.4c0 1.5464 4.1786 2.8 9.3334 2.8h58.666c5.1546 0 9.3334-1.2536 9.3334-2.8v-300.4c0-1.5464-4.1786-2.8-9.3334-2.8z" fill="#000" fill-opacity="0" style="stroke-width:.7303" tb:tag="clickArea"/>
</svg>

After

Width:  |  Height:  |  Size: 28 KiB

82
application/src/main/data/json/system/scada_symbols/top-right-elbow-connector-hp.svg

@ -0,0 +1,82 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="200" fill="none" version="1.1" viewBox="0 0 200 200"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Top right elbow connector",
"description": "Top right elbow connector",
"widgetSizeX": 1,
"widgetSizeY": 1,
"tags": [
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"actions": null
}
],
"behavior": [],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": null,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"type": "color",
"default": "#1A1A1A",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<path d="M100 0V69C100 86.1208 113.879 100 131 100H200" stroke="#1A1A1A" stroke-width="6" tb:tag="line"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

87
application/src/main/data/json/system/scada_symbols/top-tee-connector-hp.svg

@ -0,0 +1,87 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="200" fill="none" version="1.1" viewBox="0 0 200 200"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Top tee connector",
"description": "Top tee connector",
"widgetSizeX": 1,
"widgetSizeY": 1,
"tags": [
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"actions": null
},
{
"tag": "line-color",
"stateRenderFunction": "element.fill(ctx.properties.lineColor);\nelement.stroke(ctx.properties.lineColor);",
"actions": null
}
],
"behavior": [],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": null,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"type": "color",
"default": "#1A1A1A",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<path d="M200 100H115C106.716 100 100 93.2843 100 85V0" stroke="#1A1A1A" stroke-width="6" id="path2" tb:tag="line"/><path d="M0 100H85C93.2843 100 100 93.2843 100 85V0" stroke="#1A1A1A" stroke-width="6" id="path4" tb:tag="line"/><path d="M0 100L200 100" stroke="#1A1A1A" stroke-width="6" id="path6" tb:tag="line"/><path d="M87 100H113C113 100 100 100 100 86C100 100 87 100 87 100Z" fill="#1A1A1A" stroke="#1A1A1A" stroke-width="2" id="path8" tb:tag="line-color"/>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

174
application/src/main/data/json/system/scada_symbols/vertical-connector-hp.svg

@ -0,0 +1,174 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="200" fill="none" version="1.1" viewBox="0 0 200 200"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Vertical connector",
"description": "Vertical connector",
"widgetSizeX": 1,
"widgetSizeY": 1,
"tags": [
{
"tag": "arrow",
"stateRenderFunction": "var arrow = ctx.values.arrow;\nif (arrow) {\n element.show();\n var arrowDirection = ctx.values.arrowDirection;\n var direticon = element.remember('direticon');\n var angle = arrowDirection ? 0 : 180;\n if (!direticon) {\n element.transform({rotate: angle});\n } else {\n ctx.api.transform({rotate: angle, originY: 100});\n element.remember('direticon', false);\n }\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "line",
"stateRenderFunction": "element.stroke(ctx.properties.lineColor);\nif (ctx.properties.mainLine) {\n element.attr({'stroke-width': ctx.properties.mainLineSize});\n} else {\n element.attr({'stroke-width': ctx.properties.secondaryLineSize});\n}",
"actions": null
}
],
"behavior": [
{
"id": "arrow",
"name": "{i18n:scada.symbol.arrow-presence}",
"hint": "{i18n:scada.symbol.arrow-presence-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": "{i18n:scada.symbol.present}",
"falseLabel": "{i18n:scada.symbol.absent}",
"stateLabel": "{i18n:scada.symbol.arrow-present}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "arrowDirection",
"name": "{i18n:scada.symbol.arrow-direction}",
"hint": "{i18n:scada.symbol.arrow-direction-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": "{i18n:scada.symbol.forward}",
"falseLabel": "{i18n:scada.symbol.reverse}",
"stateLabel": "{i18n:scada.symbol.forward}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": true,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
}
],
"properties": [
{
"id": "mainLine",
"name": "{i18n:scada.symbol.main-line}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "mainLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 6,
"required": true,
"subLabel": "Main",
"divider": true,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "secondaryLineSize",
"name": "{i18n:scada.symbol.line}",
"type": "number",
"default": 2,
"required": true,
"subLabel": "Secondary",
"divider": null,
"fieldSuffix": "px",
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 99,
"step": 1
},
{
"id": "lineColor",
"name": "{i18n:scada.symbol.line-color}",
"type": "color",
"default": "#1A1A1A",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "arrowColor",
"name": "{i18n:scada.symbol.arrow-color}",
"type": "color",
"default": "#1A1A1A",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<path d="M100 200L100 0" stroke="#1A1A1A" stroke-width="6" tb:tag="line"/><path d="M100 71L129 129H71L100 71Z" fill="#1A1A1A" tb:tag="arrow"/>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

751
application/src/main/data/json/system/scada_symbols/vertical-scale-hp.svg

@ -0,0 +1,751 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="400" height="800" fill="none" version="1.1" viewBox="0 0 400 800"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Simple vertical scale",
"description": "Simple vertical scale with current volume value and configurable warning and critical scale.",
"searchTags": [
"scale"
],
"widgetSizeX": 2,
"widgetSizeY": 4,
"tags": [
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "highCriticalScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighCriticalValue = ctx.properties.showHighCriticalScale;\nvar highCriticalValue = ctx.properties.highCriticalScale;\n\nif (showHighCriticalValue) {\n element.show();\n var offset = calculateOffset(highCriticalValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(653-offset);\n childrenElement[1].height(653-offset-1);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "highCriticalScaleBackground",
"stateRenderFunction": "var value = ctx.values.value;\nvar showHighCriticalValue = ctx.properties.showHighCriticalScale;\n\nif (showHighCriticalValue && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (value >= highCriticalScale && value <= ctx.properties.maxValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"actions": null
},
{
"tag": "highWarningScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar highWarningValue = ctx.properties.highWarningScale;\nif (showHighWarningScale) {\n element.show();\n var offset = calculateOffset(highWarningValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(653-offset);\n childrenElement[1].height(653-offset-1);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "highWarningScaleBackground",
"stateRenderFunction": "var value = ctx.values.value;\nvar showHighWarningScale = ctx.properties.showHighWarningScale;\nvar showHighCriticalScale = ctx.properties.showHighCriticalScale;\n\nif (showHighWarningScale && value !== null) {\n var highCriticalScale = ctx.properties.highCriticalScale;\n if (!showHighCriticalScale) {\n highCriticalScale = ctx.properties.maxValue;\n }\n var highWarningScale = ctx.properties.highWarningScale;\n if (value < highCriticalScale && value >= highWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}",
"actions": null
},
{
"tag": "label",
"stateRenderFunction": "if (ctx.properties.label) {\n element.show();\n ctx.api.font(element, ctx.properties.labelFont, ctx.properties.labelColor);\n ctx.api.text(element, ctx.properties.labelText);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "lowCriticalScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nvar lowCriticalValue = ctx.properties.lowCriticalScale;\nif (showLowCriticalScale) {\n element.show();\n var offset = calculateOffset(lowCriticalValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(offset);\n childrenElement[1].height(offset-1);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "lowCriticalScaleBackground",
"stateRenderFunction": "var value = ctx.values.value;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\nif (showLowCriticalScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (value <= lowCriticalScale && value >= ctx.properties.minValue) {\n element.fill(ctx.properties.activeCriticalScaleColor);\n } else {\n element.fill(ctx.properties.defaultCriticalScaleColor)\n }\n}",
"actions": null
},
{
"tag": "lowWarningScale",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar lowWarningValue = ctx.properties.lowWarningScale;\nif (showLowWarningScale) {\n element.show();\n var offset = calculateOffset(lowWarningValue, minValue, maxValue);\n var childrenElement = element.children();\n childrenElement[0].height(offset);\n childrenElement[1].height(offset-1);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "lowWarningScaleBackground",
"stateRenderFunction": "var value = ctx.values.value;\nvar showLowWarningScale = ctx.properties.showLowWarningScale;\nvar showLowCriticalScale = ctx.properties.showLowCriticalScale;\n\nif (showLowWarningScale && value !== null) {\n var lowCriticalScale = ctx.properties.lowCriticalScale;\n if (!showLowCriticalScale) {\n lowCriticalScale = ctx.properties.minValue;\n }\n var lowWarningScale = ctx.properties.lowWarningScale;\n if (value > lowCriticalScale && value <= lowWarningScale) {\n element.fill(ctx.properties.activeWarningScaleColor);\n } else {\n element.fill(ctx.properties.defaultWarningScaleColor);\n }\n}",
"actions": null
},
{
"tag": "maxValue",
"stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.properties.maxValue);\n}",
"actions": null
},
{
"tag": "minMaxValue",
"stateRenderFunction": "if (ctx.properties.minMaxValue) {\n element.show();\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "minValue",
"stateRenderFunction": "if (ctx.properties.minMaxValue) {\n ctx.api.text(element, ctx.properties.minValue);\n}",
"actions": null
},
{
"tag": "scaleBackground",
"stateRenderFunction": "element.fill(ctx.properties.scaleColor);",
"actions": null
},
{
"tag": "target",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar showTarget = ctx.properties.showTarget;\nvar targetValue = ctx.values.targetValue;\n\nif (showTarget && targetValue !== null) {\n element.show();\n var offset = calculateOffset(targetValue, minValue, maxValue);\n element.transform({translateY: -(Math.min(Math.max(offset-17, 0), 617))});\n} else {\n element.hide();\n}\n\n",
"actions": null
},
{
"tag": "targetBackground",
"stateRenderFunction": "element.fill(ctx.properties.targetColor);",
"actions": null
},
{
"tag": "units",
"stateRenderFunction": "if (ctx.properties.showUnits) {\n element.show();\n ctx.api.font(element, ctx.properties.unitsFont, ctx.properties.unitsColor);\n ctx.api.text(element, ctx.properties.units);\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "value",
"stateRenderFunction": "if (ctx.properties.value !== null) {\n element.show();\n ctx.api.font(element, ctx.properties.valueFont, ctx.properties.valueColor);\n ctx.api.text(element, ctx.api.formatValue(ctx.values.value, ctx.properties.valueDecimals, null, ctx.properties.valueDecimals));\n} else {\n element.hide();\n}",
"actions": null
},
{
"tag": "valuePointer",
"stateRenderFunction": "element.fill(ctx.properties.arrowColor);",
"actions": null
},
{
"tag": "valuePosition",
"stateRenderFunction": "function calculateOffset(value, minValue, maxValue) {\n var clampedValue = Math.max(minValue, Math.min(value, maxValue));\n var normalizedValue = (clampedValue - minValue) / (maxValue - minValue);\n var offset = normalizedValue * 653;\n\n return offset;\n}\n\nvar minValue = ctx.properties.minValue;\nvar maxValue = ctx.properties.maxValue;\nvar value = ctx.values.value;\n\nif (value !== null) {\n var offset = calculateOffset(value, minValue, maxValue);\n element.transform({translateY: -offset});\n}\n",
"actions": null
}
],
"behavior": [
{
"id": "value",
"name": "{i18n:scada.symbol.value}",
"hint": "{i18n:scada.symbol.value-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "GET_TIME_SERIES",
"defaultValue": null,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "temperature"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "targetValue",
"name": "{i18n:scada.symbol.target-value}",
"hint": "{i18n:scada.symbol.target-value-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": null,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SERVER_SCOPE",
"key": "target"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
}
],
"properties": [
{
"id": "minMaxValue",
"name": "{i18n:scada.symbol.min-max-value}",
"type": "switch",
"default": false,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "minValue",
"name": "{i18n:scada.symbol.min-max-value}",
"type": "number",
"default": 0,
"required": true,
"subLabel": "{i18n:scada.symbol.min-value}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": 1
},
{
"id": "maxValue",
"name": "{i18n:scada.symbol.min-max-value}",
"type": "number",
"default": 100,
"required": true,
"subLabel": "{i18n:scada.symbol.max-value}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": 1
},
{
"id": "showHighCriticalScale",
"name": "{i18n:scada.symbol.high-critical-scale}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "highCriticalScale",
"name": "{i18n:scada.symbol.high-critical-scale}",
"type": "number",
"default": 85,
"required": null,
"subLabel": "",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "showHighCriticalScale",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": 1
},
{
"id": "showHighWarningScale",
"name": "{i18n:scada.symbol.high-warning-scale}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "highWarningScale",
"name": "{i18n:scada.symbol.high-warning-scale}",
"type": "number",
"default": 70,
"required": null,
"subLabel": "",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "showHighWarningScale",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": 1
},
{
"id": "showLowWarningScale",
"name": "{i18n:scada.symbol.low-warning-scale}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "lowWarningScale",
"name": "{i18n:scada.symbol.low-warning-scale}",
"type": "number",
"default": 30,
"required": null,
"subLabel": "",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "showLowWarningScale",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": 1
},
{
"id": "showLowCriticalScale",
"name": "{i18n:scada.symbol.low-critical-scale}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "lowCriticalScale",
"name": "{i18n:scada.symbol.low-critical-scale}",
"type": "number",
"default": 15,
"required": null,
"subLabel": "",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "showLowCriticalScale",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": 1
},
{
"id": "value",
"name": "{i18n:scada.symbol.value}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "valueDecimals",
"name": "{i18n:scada.symbol.value}",
"type": "number",
"default": 0,
"required": true,
"subLabel": "Decimals",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "value",
"rowClass": "",
"fieldClass": "",
"min": 0,
"max": 10,
"step": 1
},
{
"id": "valueFont",
"name": "{i18n:scada.symbol.value}",
"type": "font",
"default": {
"size": 60,
"sizeUnit": "px",
"family": "Roboto",
"weight": "400",
"style": "normal"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "value",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "valueColor",
"name": "{i18n:scada.symbol.value}",
"type": "color",
"default": "#002878",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "value",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "label",
"name": "{i18n:scada.symbol.label}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "labelText",
"name": "{i18n:scada.symbol.label}",
"type": "text",
"default": "Outdoor",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "label",
"rowClass": "",
"fieldClass": "flex",
"min": null,
"max": null,
"step": null
},
{
"id": "labelFont",
"name": "{i18n:scada.symbol.label}",
"type": "font",
"default": {
"size": 56,
"sizeUnit": "px",
"family": "Roboto",
"weight": "500",
"style": "normal"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "label",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "labelColor",
"name": "{i18n:scada.symbol.label}",
"type": "color",
"default": "#000000DE",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "label",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "showUnits",
"name": "{i18n:scada.symbol.units}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "units",
"name": "{i18n:scada.symbol.units}",
"type": "units",
"default": "°C",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "showUnits",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "unitsFont",
"name": "{i18n:scada.symbol.units}",
"type": "font",
"default": {
"size": 56,
"sizeUnit": "px",
"family": "Roboto",
"weight": "500",
"style": "normal"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "showUnits",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "unitsColor",
"name": "{i18n:scada.symbol.units}",
"type": "color",
"default": "#000000DE",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "showUnits",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "arrowColor",
"name": "{i18n:scada.symbol.arrow-color}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "showTarget",
"name": "{i18n:scada.symbol.target}",
"type": "switch",
"default": false,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "targetColor",
"name": "{i18n:scada.symbol.target}",
"type": "color",
"default": "#DEDEDE",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "showTarget",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "scaleColor",
"name": "{i18n:scada.symbol.scale-color}",
"type": "color",
"default": "#C8DFF7",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "defaultWarningScaleColor",
"name": "{i18n:scada.symbol.warning-scale-color}",
"type": "color",
"default": "#EBEBEB",
"required": null,
"subLabel": "Default",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "activeWarningScaleColor",
"name": "{i18n:scada.symbol.warning-scale-color}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": "Active",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "defaultCriticalScaleColor",
"name": "{i18n:scada.symbol.critical-scale-color}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": "Default",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "activeCriticalScaleColor",
"name": "{i18n:scada.symbol.critical-scale-color}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": "Active",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<text x="206.16602" y="43.234375" fill="#000000" font-family="Roboto" font-size="56px" font-weight="400" text-anchor="middle" tb:tag="label" xml:space="preserve"><tspan transform="translate(0,-144)" dominant-baseline="middle">Outdoor</tspan></text><text x="195.21875" y="774.25" fill="#000000" font-family="Roboto" font-size="56px" font-weight="400" text-anchor="middle" tb:tag="units" xml:space="preserve"><tspan transform="translate(0,-144)" dominant-baseline="middle">°C</tspan></text><g tb:tag="scale">
<rect x="176" y="83" width="42" height="652" fill="#c8dff7" tb:tag="scaleBackground"/>
<rect x="176.5" y="83.5" width="41" height="651" stroke="#000" stroke-opacity=".87"/>
<g tb:tag="lowWarningScale">
<rect transform="scale(1,-1)" x="176" y="-736.5" width="42" height="170" fill="#ebebeb" tb:tag="lowWarningScaleBackground"/>
<rect transform="scale(1,-1)" x="176.5" y="-736" width="41" height="169" stroke="#000" stroke-opacity=".87"/>
</g>
<g tb:tag="highWarningScale">
<rect x="176" y="83" width="42" height="170" fill="#ebebeb" tb:tag="highWarningScaleBackground"/>
<rect x="176.5" y="83.5" width="41" height="169" stroke="#000" stroke-opacity=".87"/>
</g>
<g tb:tag="lowCriticalScale">
<rect transform="scale(1,-1)" x="176" y="-736.5" width="42" height="90" fill="#666" tb:tag="lowCriticalScaleBackground"/>
<rect transform="scale(1,-1)" x="176.5" y="-736" width="41" height="89" stroke="#000" stroke-opacity=".87"/>
</g>
<g tb:tag="highCriticalScale">
<rect x="176" y="83" width="42" height="90" fill="#666" tb:tag="highCriticalScaleBackground"/>
<rect x="176.5" y="83.5" width="41" height="89" stroke="#000" stroke-opacity=".87"/>
</g>
</g><g font-family="Roboto" font-size="32px" font-weight="500" text-anchor="middle" style="display: none;" tb:tag="minMaxValue">
<text x="260.02344" y="97" fill="#666" style="" tb:tag="maxValue" xml:space="preserve"><tspan dominant-baseline="middle">100</tspan></text>
<text x="239.07031" y="727" fill="#666666" style="" tb:tag="minValue" xml:space="preserve"><tspan dominant-baseline="middle">0</tspan></text>
</g><g transform="translate(0,-350)" tb:tag="valuePosition">
<path d="m168 734-42-24v48z" fill="#666" tb:tag="valuePointer"/>
<text x="262.53329" y="738.125" fill="#002878" font-family="Roboto" font-size="60px" font-weight="400" text-anchor="middle" tb:tag="value" xml:space="preserve"><tspan transform="translate(0,-144)" dominant-baseline="middle">26</tspan></text>
</g><g transform="translate(0,-440)" tb:tag="target">
<rect transform="rotate(-45)" x="-379.7" y="634.25" width="24" height="24" fill="#dedede" tb:tag="targetBackground"/>
<rect transform="rotate(-45)" x="-378.7" y="635.25" width="22" height="22" stroke="#000" stroke-opacity=".87" stroke-width="2"/>
</g><path d="m134.53 0s-134.53 0-134.53 134v656.72c0 5.3024 3.5817 9.2808 8 9.2808h384c4.418 0 8-3.9784 8-9.2808v-656.72c0-134-132.14-134-132.14-134h-67.86zm134.14 162.4c-2.5773 0-4.6667 2.5072-4.6667 5.6v600.8c0 3.0928 2.0893 5.6 4.6667 5.6h29.333c2.5773 0 4.6667-2.5072 4.6667-5.6v-600.8c0-3.0928-2.0893-5.6-4.6667-5.6z" fill="#000" fill-opacity="0" style="stroke-width:.7303" tb:tag="clickArea"/>
</svg>

After

Width:  |  Height:  |  Size: 27 KiB

536
application/src/main/data/json/system/scada_symbols/vertical-tank-hp.svg

@ -0,0 +1,536 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="600" height="1e3" fill="none" version="1.1" viewBox="0 0 600 1e3"><tb:metadata xmlns=""><![CDATA[{
"title": "Vertical tank",
"description": "Vertical tank",
"searchTags": [
"vertical tank"
],
"widgetSizeX": 3,
"widgetSizeY": 5,
"tags": [
{
"tag": "background",
"stateRenderFunction": "element.attr({fill: ctx.properties.tankColor});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "fluid-background",
"stateRenderFunction": " var color = ctx.properties.fluidColor;\n element.attr({fill: color, 'fill-opacity': 1});\n \n var valueSet = element.remember('valueSet');\n if (!valueSet) {\n element.remember('valueSet', true);\n element.attr({height: 0});\n }\n \n var currentVolume = ctx.values.currentVolume; \n var tankCapacity = ctx.values.tankCapacity; \n\n var height = currentVolume / tankCapacity;\n height = Math.max(0, Math.min(1, height))*970; \n \n var elementHeight = element.remember('height');\n if (height !== elementHeight) {\n element.remember('height', height);\n element.attr({height: height});\n }",
"actions": null
},
{
"tag": "scale",
"stateRenderFunction": "if (!ctx.properties.scale) {\n element.hide();\n} else {\n var scaleSet = element.remember('scaleSet');\n if (!scaleSet) {\n element.remember('scaleSet', true);\n element.clear();\n \n var majorIntervals = ctx.properties.majorIntervals;\n var minorIntervals = ctx.properties.minorIntervals;\n \n var start = 15;\n var majorIntervalLength = 970 / majorIntervals;\n var minorIntervalLength = majorIntervalLength / minorIntervals;\n for (var i = 0; i < majorIntervals + 1; i++) {\n var y = start + i * majorIntervalLength;\n var line = ctx.svg.line(160, y, 192, y).stroke({ width: 3 }).attr({class: 'majorTick'});\n element.add(line);\n var majorText = (100 - i * (100/majorIntervals)).toFixed(0);\n var majorTickText = ctx.svg.text(majorText);\n majorTickText.attr({x: 150, y: y + 2, 'text-anchor': 'end', class: 'majorTickText'});\n majorTickText.first().attr({'dominant-baseline': 'middle'});\n element.add(majorTickText);\n if (i < majorIntervals) {\n drawMinorTicks(y, minorIntervals, minorIntervalLength);\n }\n }\n }\n \n var majorFont = ctx.properties.majorFont;\n var majorColor = ctx.properties.majorColor;\n var minorColor = ctx.properties.minorColor;\n \n var majorTicks = element.find('line.majorTick');\n majorTicks.forEach(t => t.attr({stroke: majorColor}));\n \n var majorTicksText = element.find('text.majorTickText');\n ctx.api.font(majorTicksText, majorFont, majorColor);\n \n var minorTicks = element.find('line.minorTick');\n minorTicks.forEach(t => t.attr({stroke: minorColor}));\n}\n\nfunction drawMinorTicks(start, minorIntervals, minorIntervalLength) {\n for (var i = 1; i < minorIntervals; i++) {\n var minorY = start + i * minorIntervalLength;\n var minorLine = ctx.svg.line(172, minorY, 192, minorY).stroke({ width: 3 }).attr({class: 'minorTick'});\n element.add(minorLine);\n }\n}",
"actions": null
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "tankCapacity",
"name": "{i18n:scada.symbol.tank-capacity}",
"hint": "{i18n:scada.symbol.tank-capacity-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": null,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SERVER_SCOPE",
"key": "tankCapacity"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "currentVolume",
"name": "{i18n:scada.symbol.current-volume}",
"hint": "{i18n:scada.symbol.current-volume-hint}",
"group": null,
"type": "value",
"valueType": "DOUBLE",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": {
"action": "GET_TIME_SERIES",
"defaultValue": null,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": null,
"key": "state"
},
"getTimeSeries": {
"key": "liquidVolume"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning-state}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "critical",
"name": "{i18n:scada.symbol.critical-state}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "criticalAnimation",
"name": "{i18n:scada.symbol.critical-state-animation}",
"hint": "{i18n:scada.symbol.critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warningClick",
"name": "{i18n:scada.symbol.warning-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
},
{
"id": "criticalClick",
"name": "{i18n:scada.symbol.critical-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
}
],
"properties": [
{
"id": "tankColor",
"name": "{i18n:scada.symbol.tank-color}",
"type": "color",
"default": "#EBEBEB",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "fluidColor",
"name": "{i18n:scada.symbol.fluid-color}",
"type": "color",
"default": "#C8DFF7",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "scale",
"name": "{i18n:scada.symbol.scale}",
"type": "switch",
"default": true,
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "majorIntervals",
"name": "{i18n:scada.symbol.major-ticks}",
"type": "number",
"default": 10,
"required": null,
"subLabel": "{i18n:scada.symbol.intervals}",
"divider": true,
"fieldSuffix": null,
"disableOnProperty": "scale",
"rowClass": "",
"fieldClass": "",
"min": 1,
"max": null,
"step": 1
},
{
"id": "majorFont",
"name": "{i18n:scada.symbol.major-ticks}",
"type": "font",
"default": {
"size": 24,
"sizeUnit": "px",
"family": "Roboto",
"weight": "500",
"style": "normal"
},
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "scale",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "majorColor",
"name": "{i18n:scada.symbol.major-ticks-color}",
"type": "color",
"default": "#00000061",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "scale",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "minorIntervals",
"name": "{i18n:scada.symbol.minor-ticks}",
"type": "number",
"default": 5,
"required": null,
"subLabel": "{i18n:scada.symbol.intervals}",
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "scale",
"rowClass": "",
"fieldClass": "",
"min": 1,
"max": null,
"step": null
},
{
"id": "minorColor",
"name": "{i18n:scada.symbol.minor-ticks-color}",
"type": "color",
"default": "#0000001F",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": "scale",
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.warning-color}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.critical-color}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<path d="m0 120s0-120 140-120c129 3.4657e-5 200.5-1.0591e-4 320 0 140 1.2408e-4 140 120 140 120v760s0 120-140 120h-320c-140 0-140-120-140-120v-760z" fill="#EBEBEB" tb:tag="background"/><path d="m0.936 120h0.064l1e-5 -3e-3 5e-5 -0.014 4.3e-4 -0.061c5.1e-4 -0.054 0.00153-0.137 0.00361-0.247 0.00415-0.221 0.0125-0.551 0.02932-0.984 0.03365-0.865 0.10115-2.139 0.23656-3.764 0.27086-3.25 0.81325-7.902 1.8991-13.486 2.1725-11.173 6.5161-26.06 15.195-40.937 17.319-29.69 51.988-59.504 121.64-59.504l141.99-2e-5 178.01 2e-5c69.648 6e-5 104.32 29.814 121.64 59.504 8.679 14.878 13.022 29.764 15.195 40.937 1.086 5.585 1.628 10.236 1.899 13.486 0.135 1.625 0.203 2.899 0.237 3.764 0.016 0.433 0.025 0.763 0.029 0.984 1e-3 0.047 1e-3 0.09 2e-3 0.127 1e-3 0.049 1e-3 0.089 2e-3 0.12v0.061 0.014 3e-3h0.064-0.064v760h0.064-0.064v3e-3 0.014 0.061c-1e-3 0.054-2e-3 0.137-4e-3 0.247-4e-3 0.221-0.013 0.551-0.029 0.984-0.034 0.865-0.102 2.139-0.237 3.764-0.271 3.25-0.813 7.902-1.899 13.486-2.173 11.173-6.516 26.06-15.195 40.937-17.319 29.69-51.988 59.504-121.64 59.504h-320c-69.648 0-104.32-29.814-121.64-59.504-8.6786-14.877-13.022-29.764-15.195-40.937-1.0859-5.584-1.6283-10.236-1.8991-13.486-0.13541-1.625-0.20291-2.899-0.23656-3.764-0.01682-0.433-0.02517-0.763-0.02932-0.984-0.00208-0.11-0.0031-0.193-0.00361-0.247l-4.3e-4 -0.061-5e-5 -0.014-1e-5 -3e-3h-0.064 0.064v-760h-0.064z" stroke="#000" stroke-opacity=".87" stroke-width="2"/><mask id="mask0_3610_179351" x="2" y="2" width="596" height="996" style="mask-type:alpha" maskUnits="userSpaceOnUse">
<path d="m2 121.52s0-119.52 139.07-119.52c128.14 3e-5 199.16-1.1e-4 317.87 0 139.07 1.2e-4 139.07 119.52 139.07 119.52v756.96s0 119.52-139.07 119.52h-317.87c-139.07 0-139.07-119.52-139.07-119.52v-756.96z" fill="#EBEBEB"/>
<path d="m2.936 121.52h0.064l1e-5 -3e-3 5e-5 -0.014 4.3e-4 -0.06c5e-4 -0.055 0.00152-0.137 0.00358-0.247 0.00412-0.22 0.01242-0.549 0.02913-0.979 0.03342-0.862 0.10047-2.131 0.23498-3.75 0.26905-3.237 0.80781-7.869 1.8864-13.432 2.158-11.128 6.4726-25.954 15.093-40.772 17.203-29.57 51.639-59.263 120.82-59.263l141.05-2e-5 176.82 2e-5c69.18 6e-5 103.62 29.693 120.82 59.263 8.621 14.818 12.935 29.644 15.093 40.772 1.079 5.563 1.618 10.195 1.887 13.432 0.134 1.619 0.201 2.888 0.235 3.75 0.017 0.43 0.025 0.759 0.029 0.979 2e-3 0.11 3e-3 0.192 4e-3 0.247v0.06 0.014 3e-3h0.064-0.064v756.96h0.122-0.122v3e-3 0.014 0.06c-1e-3 0.055-2e-3 0.137-4e-3 0.247-4e-3 0.22-0.012 0.549-0.029 0.979-0.034 0.862-0.101 2.131-0.235 3.75-0.269 3.237-0.808 7.869-1.887 13.432-2.158 11.128-6.472 25.954-15.093 40.772-17.203 29.57-51.639 59.263-120.82 59.263h-317.87c-69.18 0-103.62-29.693-120.82-59.263-8.6205-14.818-12.935-29.644-15.093-40.772-1.0786-5.563-1.6174-10.195-1.8864-13.432-0.13451-1.619-0.20156-2.888-0.23498-3.75-0.01671-0.43-0.02501-0.759-0.02913-0.979-0.00206-0.11-0.00308-0.192-0.00358-0.247l-4.3e-4 -0.06-5e-5 -0.014-1e-5 -3e-3h-0.064 0.064v-756.96h-0.064z" stroke="#000" stroke-opacity=".87" stroke-width="2"/>
</mask><g mask="url(#mask0_3610_179351)">
<rect transform="scale(1,-1)" x="2" y="-985" width="600" height="419" fill="#c8dff7" tb:tag="fluid-background"/>
</g><g tb:tag="scale">
<line x1="162" x2="194" y1="11.072" y2="11.072" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="174" x2="194" y1="30.69" y2="30.69" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="50.309" y2="50.309" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="69.926" y2="69.926" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="89.543" y2="89.543" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="162" x2="194" y1="109.16" y2="109.16" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="174" x2="194" y1="128.78" y2="128.78" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="148.4" y2="148.4" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="168.01" y2="168.01" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="187.63" y2="187.63" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="162" x2="194" y1="207.25" y2="207.25" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="174" x2="194" y1="226.87" y2="226.87" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="246.48" y2="246.48" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="266.1" y2="266.1" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="285.72" y2="285.72" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="162" x2="194" y1="305.34" y2="305.34" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="174" x2="194" y1="324.96" y2="324.96" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="344.57" y2="344.57" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="364.19" y2="364.19" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="383.81" y2="383.81" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="162" x2="194" y1="403.43" y2="403.43" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="174" x2="194" y1="423.04" y2="423.04" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="442.66" y2="442.66" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="462.28" y2="462.28" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="481.9" y2="481.9" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="162" x2="194" y1="501.52" y2="501.52" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="174" x2="194" y1="521.13" y2="521.13" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="540.75" y2="540.75" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="560.37" y2="560.37" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="579.99" y2="579.99" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="162" x2="194" y1="599.6" y2="599.6" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="174" x2="194" y1="619.22" y2="619.22" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="638.84" y2="638.84" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="658.46" y2="658.46" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="678.07" y2="678.07" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="162" x2="194" y1="697.69" y2="697.69" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="174" x2="194" y1="717.31" y2="717.31" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="736.93" y2="736.93" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="756.55" y2="756.55" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="776.16" y2="776.16" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="162" x2="194" y1="795.78" y2="795.78" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="174" x2="194" y1="815.4" y2="815.4" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="835.02" y2="835.02" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="854.64" y2="854.64" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="874.25" y2="874.25" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="162" x2="194" y1="893.87" y2="893.87" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<line x1="174" x2="194" y1="913.49" y2="913.49" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="933.1" y2="933.1" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="952.72" y2="952.72" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="174" x2="194" y1="972.34" y2="972.34" stroke="#000" stroke-opacity=".12" stroke-width="3"/>
<line x1="162" x2="194" y1="991.96" y2="991.96" stroke="#000" stroke-opacity=".38" stroke-width="3"/>
<path d="m121.58 4.8789v17.121h-2.824v-13.77l-4.184 1.418v-2.332l6.668-2.4375h0.34zm17.301 7.1602v2.789c0 1.336-0.133 2.4766-0.399 3.4219-0.257 0.9375-0.632 1.6992-1.125 2.2852-0.492 0.5859-1.082 1.0156-1.769 1.289-0.68 0.2735-1.441 0.4102-2.285 0.4102-0.672 0-1.297-0.086-1.875-0.2578-0.571-0.1719-1.086-0.4414-1.547-0.8086s-0.856-0.8399-1.184-1.418c-0.32-0.5859-0.57-1.2852-0.75-2.0977-0.172-0.8125-0.257-1.7539-0.257-2.8242v-2.789c0-1.3438 0.132-2.4766 0.398-3.3985 0.266-0.92968 0.644-1.6836 1.137-2.2617 0.492-0.58594 1.078-1.0117 1.757-1.2774 0.688-0.26562 1.454-0.39844 2.297-0.39844 0.68 0 1.305 0.08594 1.875 0.25782 0.578 0.16406 1.094 0.42578 1.547 0.78515 0.461 0.35938 0.852 0.82813 1.172 1.4062 0.328 0.57032 0.578 1.2617 0.75 2.0742 0.172 0.80464 0.258 1.7422 0.258 2.8125zm-2.824 3.1875v-3.6094c0-0.6797-0.039-1.2774-0.117-1.793-0.079-0.52344-0.196-0.96484-0.352-1.3242-0.148-0.36719-0.336-0.66406-0.563-0.89062-0.226-0.23438-0.484-0.40235-0.773-0.50391-0.289-0.10938-0.613-0.16406-0.973-0.16406-0.437 0-0.828 0.08593-1.172 0.25781-0.343 0.16406-0.632 0.42969-0.867 0.79687-0.234 0.36719-0.414 0.85157-0.539 1.4531-0.117 0.59378-0.176 1.3164-0.176 2.168v3.6094c0 0.6875 0.039 1.2929 0.118 1.8164 0.078 0.5234 0.195 0.9726 0.351 1.3476 0.156 0.3672 0.344 0.6719 0.563 0.9141 0.226 0.2344 0.484 0.4062 0.773 0.5156 0.297 0.1094 0.621 0.1641 0.973 0.1641 0.445 0 0.84-0.086 1.183-0.2578 0.344-0.1719 0.633-0.4454 0.868-0.8204 0.234-0.3828 0.41-0.8789 0.527-1.4882 0.117-0.6094 0.176-1.3399 0.176-2.1914zm16.715-3.1875v2.789c0 1.336-0.133 2.4766-0.399 3.4219-0.258 0.9375-0.633 1.6992-1.125 2.2852-0.492 0.5859-1.082 1.0156-1.769 1.289-0.68 0.2735-1.442 0.4102-2.286 0.4102-0.671 0-1.296-0.086-1.875-0.2578-0.57-0.1719-1.086-0.4414-1.546-0.8086-0.461-0.3672-0.856-0.8399-1.184-1.418-0.32-0.5859-0.57-1.2852-0.75-2.0977-0.172-0.8125-0.258-1.7539-0.258-2.8242v-2.789c0-1.3438 0.133-2.4766 0.399-3.3985 0.265-0.92968 0.644-1.6836 1.136-2.2617 0.492-0.58594 1.078-1.0117 1.758-1.2774 0.688-0.26562 1.453-0.39844 2.297-0.39844 0.68 0 1.305 0.08594 1.875 0.25782 0.578 0.16406 1.094 0.42578 1.547 0.78515 0.461 0.35938 0.851 0.82813 1.172 1.4062 0.328 0.57032 0.578 1.2617 0.75 2.0742 0.172 0.80464 0.258 1.7422 0.258 2.8125zm-2.825 3.1875v-3.6094c0-0.6797-0.039-1.2774-0.117-1.793-0.078-0.52344-0.195-0.96484-0.351-1.3242-0.149-0.36719-0.336-0.66406-0.563-0.89062-0.226-0.23438-0.484-0.40235-0.773-0.50391-0.289-0.10938-0.614-0.16406-0.973-0.16406-0.438 0-0.828 0.08593-1.172 0.25781-0.344 0.16406-0.633 0.42969-0.867 0.79687-0.234 0.36719-0.414 0.85157-0.539 1.4531-0.117 0.59378-0.176 1.3164-0.176 2.168v3.6094c0 0.6875 0.039 1.2929 0.117 1.8164 0.078 0.5234 0.196 0.9726 0.352 1.3476 0.156 0.3672 0.344 0.6719 0.562 0.9141 0.227 0.2344 0.485 0.4062 0.774 0.5156 0.297 0.1094 0.621 0.1641 0.972 0.1641 0.446 0 0.84-0.086 1.184-0.2578 0.344-0.1719 0.633-0.4454 0.867-0.8204 0.235-0.3828 0.41-0.8789 0.528-1.4882 0.117-0.6094 0.175-1.3399 0.175-2.1914z" fill="#000" fill-opacity=".38"/>
<path d="m130.03 117.17h0.235c1.078 0 1.976-0.141 2.695-0.422 0.727-0.289 1.305-0.688 1.734-1.195 0.43-0.508 0.739-1.106 0.926-1.793 0.188-0.688 0.281-1.434 0.281-2.239v-2.941c0-0.695-0.074-1.305-0.222-1.828-0.141-0.531-0.344-0.973-0.61-1.324-0.258-0.36-0.558-0.629-0.902-0.809-0.336-0.18-0.699-0.269-1.09-0.269-0.43 0-0.816 0.097-1.16 0.292-0.336 0.188-0.621 0.45-0.856 0.786-0.226 0.328-0.402 0.714-0.527 1.16-0.117 0.437-0.176 0.906-0.176 1.406 0 0.469 0.055 0.922 0.164 1.359 0.118 0.43 0.289 0.813 0.516 1.149s0.512 0.601 0.856 0.797c0.343 0.195 0.746 0.293 1.207 0.293 0.437 0 0.839-0.082 1.207-0.246 0.367-0.172 0.687-0.403 0.961-0.692 0.273-0.289 0.488-0.613 0.644-0.972 0.156-0.36 0.242-0.727 0.258-1.102l1.078 0.328c0 0.594-0.125 1.18-0.375 1.758-0.242 0.57-0.582 1.094-1.02 1.57-0.429 0.469-0.933 0.844-1.511 1.125-0.571 0.281-1.192 0.422-1.864 0.422-0.812 0-1.531-0.152-2.156-0.457-0.617-0.312-1.133-0.734-1.547-1.266-0.406-0.531-0.711-1.14-0.914-1.828-0.203-0.687-0.304-1.41-0.304-2.168 0-0.82 0.125-1.589 0.375-2.308s0.613-1.352 1.089-1.899c0.477-0.554 1.055-0.984 1.735-1.289 0.687-0.312 1.465-0.468 2.332-0.468 0.922 0 1.73 0.179 2.426 0.539 0.695 0.359 1.281 0.855 1.757 1.488 0.477 0.633 0.836 1.367 1.079 2.203 0.242 0.836 0.363 1.734 0.363 2.695v0.996c0 1.008-0.09 1.981-0.27 2.918-0.179 0.93-0.472 1.793-0.879 2.59-0.398 0.789-0.925 1.485-1.582 2.086-0.648 0.594-1.445 1.059-2.39 1.395-0.938 0.328-2.039 0.492-3.305 0.492h-0.258v-2.332zm22.739-7.735v2.79c0 1.335-0.133 2.476-0.399 3.421-0.258 0.938-0.633 1.7-1.125 2.286-0.492 0.585-1.082 1.015-1.769 1.289-0.68 0.273-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.441-1.546-0.809-0.461-0.367-0.856-0.839-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.097-0.172-0.813-0.258-1.754-0.258-2.824v-2.79c0-1.343 0.133-2.476 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.011 1.758-1.277 0.688-0.266 1.453-0.398 2.297-0.398 0.68 0 1.305 0.085 1.875 0.257 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.36 0.851 0.829 1.172 1.407 0.328 0.57 0.578 1.261 0.75 2.074 0.172 0.805 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.609c0-0.68-0.039-1.278-0.117-1.793-0.078-0.524-0.195-0.965-0.351-1.325-0.149-0.367-0.336-0.664-0.563-0.89-0.226-0.235-0.484-0.403-0.773-0.504-0.289-0.11-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.429-0.867 0.796-0.234 0.368-0.414 0.852-0.539 1.454-0.117 0.593-0.176 1.316-0.176 2.168v3.609c0 0.687 0.039 1.293 0.117 1.816 0.078 0.524 0.196 0.973 0.352 1.348 0.156 0.367 0.344 0.672 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.445 0.867-0.82c0.235-0.383 0.41-0.879 0.528-1.489 0.117-0.609 0.175-1.339 0.175-2.191z" fill="#000" fill-opacity=".38"/>
<path d="m138.91 212.15c0 1.063-0.246 1.957-0.738 2.684s-1.164 1.277-2.016 1.652c-0.844 0.368-1.797 0.551-2.859 0.551-1.063 0-2.02-0.183-2.871-0.551-0.852-0.375-1.524-0.925-2.016-1.652s-0.738-1.621-0.738-2.684c0-0.703 0.136-1.339 0.41-1.91 0.273-0.578 0.66-1.074 1.16-1.488 0.508-0.422 1.102-0.746 1.781-0.973 0.688-0.226 1.438-0.339 2.25-0.339 1.078 0 2.043 0.199 2.895 0.597 0.851 0.399 1.519 0.949 2.004 1.653 0.492 0.703 0.738 1.523 0.738 2.46zm-2.836-0.14c0-0.57-0.117-1.07-0.351-1.5-0.235-0.43-0.563-0.762-0.985-0.996-0.422-0.235-0.91-0.352-1.465-0.352-0.562 0-1.05 0.117-1.465 0.352-0.414 0.234-0.738 0.566-0.972 0.996-0.227 0.43-0.34 0.93-0.34 1.5 0 0.578 0.113 1.078 0.34 1.5 0.226 0.414 0.551 0.73 0.972 0.949 0.422 0.219 0.918 0.328 1.489 0.328 0.57 0 1.062-0.109 1.476-0.328s0.735-0.535 0.961-0.949c0.227-0.422 0.34-0.922 0.34-1.5zm2.449-7.781c0 0.851-0.226 1.609-0.679 2.273-0.446 0.664-1.063 1.188-1.852 1.57-0.789 0.375-1.687 0.563-2.695 0.563-1.016 0-1.922-0.188-2.719-0.563-0.789-0.382-1.41-0.906-1.863-1.57-0.446-0.664-0.668-1.422-0.668-2.273 0-1.016 0.222-1.872 0.668-2.567 0.453-0.703 1.074-1.238 1.863-1.605s1.691-0.551 2.707-0.551 1.918 0.184 2.707 0.551 1.406 0.902 1.852 1.605c0.453 0.695 0.679 1.551 0.679 2.567zm-2.824 0.093c0-0.508-0.101-0.953-0.305-1.336-0.195-0.39-0.472-0.695-0.832-0.914-0.359-0.218-0.785-0.328-1.277-0.328s-0.918 0.106-1.277 0.317c-0.36 0.211-0.637 0.507-0.832 0.89-0.196 0.383-0.293 0.84-0.293 1.371 0 0.524 0.097 0.981 0.293 1.371 0.195 0.383 0.472 0.684 0.832 0.903 0.367 0.218 0.797 0.328 1.289 0.328s0.918-0.11 1.277-0.328c0.36-0.219 0.637-0.52 0.832-0.903 0.195-0.39 0.293-0.847 0.293-1.371zm17.067 2.52v2.789c0 1.336-0.133 2.476-0.399 3.422-0.258 0.937-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.273-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.441-1.546-0.808-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.098-0.172-0.812-0.258-1.754-0.258-2.824v-2.789c0-1.344 0.133-2.477 0.399-3.399 0.265-0.929 0.644-1.683 1.136-2.261 0.492-0.586 1.078-1.012 1.758-1.278 0.688-0.265 1.453-0.398 2.297-0.398 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.359 0.851 0.828 1.172 1.406 0.328 0.57 0.578 1.262 0.75 2.074 0.172 0.805 0.258 1.743 0.258 2.813zm-2.825 3.187v-3.609c0-0.68-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.965-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.891-0.226-0.234-0.484-0.402-0.773-0.504-0.289-0.109-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.851-0.539 1.453c-0.117 0.594-0.176 1.316-0.176 2.168v3.609c0 0.688 0.039 1.293 0.117 1.817 0.078 0.523 0.196 0.972 0.352 1.347 0.156 0.368 0.344 0.672 0.562 0.914 0.227 0.235 0.485 0.407 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.445 0.867-0.82c0.235-0.383 0.41-0.879 0.528-1.488 0.117-0.61 0.175-1.34 0.175-2.192z" fill="#000" fill-opacity=".38"/>
<path d="m139.09 297.14v1.547l-6.82 15.515h-2.977l6.809-14.812h-8.836v-2.25h11.824zm13.68 7.101v2.789c0 1.336-0.133 2.477-0.399 3.422-0.258 0.938-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.274-1.442 0.411-2.286 0.411-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.442-1.546-0.809-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.097-0.172-0.813-0.258-1.754-0.258-2.825v-2.789c0-1.343 0.133-2.476 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.012 1.758-1.277 0.688-0.266 1.453-0.399 2.297-0.399 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.36 0.851 0.828 1.172 1.407 0.328 0.57 0.578 1.261 0.75 2.074 0.172 0.804 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.61c0-0.679-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.964-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.89-0.226-0.235-0.484-0.403-0.773-0.504-0.289-0.11-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.257-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.852-0.539 1.453c-0.117 0.594-0.176 1.317-0.176 2.168v3.61c0 0.687 0.039 1.293 0.117 1.816 0.078 0.524 0.196 0.973 0.352 1.348 0.156 0.367 0.344 0.672 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.446 0.867-0.821c0.235-0.382 0.41-0.878 0.528-1.488 0.117-0.609 0.175-1.34 0.175-2.191z" fill="#000" fill-opacity=".38"/>
<path d="m136.29 394.43h0.352v2.308h-0.2c-1.007 0-1.879 0.156-2.613 0.469-0.726 0.312-1.324 0.742-1.793 1.289s-0.82 1.188-1.055 1.922c-0.226 0.726-0.339 1.5-0.339 2.32v2.684c0 0.68 0.074 1.281 0.222 1.805 0.149 0.515 0.356 0.949 0.621 1.3 0.274 0.344 0.586 0.606 0.938 0.785 0.351 0.18 0.73 0.27 1.137 0.27 0.421 0 0.804-0.086 1.148-0.258 0.344-0.18 0.637-0.426 0.879-0.738 0.242-0.313 0.426-0.684 0.551-1.113 0.125-0.43 0.187-0.899 0.187-1.407 0-0.484-0.062-0.937-0.187-1.359-0.117-0.43-0.293-0.805-0.528-1.125-0.234-0.328-0.527-0.582-0.879-0.762-0.343-0.187-0.742-0.281-1.195-0.281-0.562 0-1.074 0.133-1.535 0.398-0.453 0.266-0.82 0.614-1.102 1.043-0.273 0.422-0.421 0.872-0.445 1.348l-1.078-0.351c0.063-0.727 0.223-1.379 0.48-1.958 0.266-0.578 0.614-1.07 1.043-1.476 0.43-0.406 0.926-0.715 1.489-0.926 0.57-0.219 1.195-0.328 1.875-0.328 0.828 0 1.547 0.156 2.156 0.469 0.609 0.312 1.113 0.738 1.512 1.277 0.406 0.531 0.707 1.141 0.902 1.828 0.203 0.68 0.305 1.391 0.305 2.133 0 0.82-0.125 1.586-0.375 2.297-0.25 0.703-0.617 1.32-1.102 1.851-0.476 0.532-1.058 0.946-1.746 1.243-0.68 0.297-1.453 0.445-2.32 0.445-0.914 0-1.731-0.176-2.449-0.527-0.711-0.352-1.317-0.836-1.817-1.453-0.492-0.618-0.867-1.329-1.125-2.133-0.258-0.805-0.387-1.657-0.387-2.555v-1.172c0-1.297 0.164-2.519 0.493-3.668 0.328-1.156 0.832-2.176 1.511-3.058 0.688-0.883 1.567-1.575 2.637-2.075 1.07-0.507 2.348-0.761 3.832-0.761zm16.481 7.207v2.789c0 1.336-0.133 2.476-0.399 3.422-0.258 0.937-0.633 1.699-1.125 2.285s-1.082 1.015-1.769 1.289c-0.68 0.273-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.441-1.546-0.808-0.461-0.368-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.286-0.75-2.098-0.172-0.813-0.258-1.754-0.258-2.824v-2.789c0-1.344 0.133-2.477 0.399-3.399 0.265-0.929 0.644-1.683 1.136-2.261 0.492-0.586 1.078-1.012 1.758-1.278 0.688-0.265 1.453-0.398 2.297-0.398 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.425 1.547 0.785 0.461 0.359 0.851 0.828 1.172 1.406 0.328 0.57 0.578 1.262 0.75 2.074 0.172 0.805 0.258 1.742 0.258 2.813zm-2.825 3.187v-3.609c0-0.68-0.039-1.278-0.117-1.793-0.078-0.524-0.195-0.965-0.351-1.324-0.149-0.368-0.336-0.664-0.563-0.891-0.226-0.234-0.484-0.402-0.773-0.504-0.289-0.109-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.851-0.539 1.453c-0.117 0.594-0.176 1.316-0.176 2.168v3.609c0 0.688 0.039 1.293 0.117 1.817 0.078 0.523 0.196 0.972 0.352 1.347 0.156 0.367 0.344 0.672 0.562 0.914 0.227 0.235 0.485 0.407 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.445 0.867-0.82c0.235-0.383 0.41-0.879 0.528-1.488 0.117-0.61 0.175-1.34 0.175-2.192z" fill="#000" fill-opacity=".38"/>
<path d="m130.72 501.07-2.25-0.55 0.925-8.578h9.2v2.39h-6.856l-0.469 4.184c0.266-0.157 0.633-0.309 1.102-0.457 0.469-0.157 1.004-0.235 1.605-0.235 0.805 0 1.524 0.133 2.157 0.399 0.64 0.258 1.183 0.636 1.629 1.136 0.445 0.493 0.785 1.094 1.019 1.805 0.235 0.703 0.352 1.496 0.352 2.379 0 0.789-0.117 1.527-0.352 2.215-0.226 0.687-0.57 1.293-1.031 1.816-0.461 0.524-1.043 0.934-1.746 1.231-0.696 0.289-1.52 0.433-2.473 0.433-0.711 0-1.394-0.101-2.051-0.304-0.648-0.211-1.23-0.52-1.746-0.926-0.515-0.414-0.929-0.922-1.242-1.524-0.312-0.609-0.492-1.308-0.539-2.097h2.766c0.07 0.555 0.222 1.027 0.457 1.418 0.242 0.383 0.562 0.676 0.961 0.879 0.398 0.203 0.859 0.304 1.382 0.304 0.477 0 0.887-0.082 1.231-0.246 0.344-0.172 0.629-0.414 0.855-0.726 0.235-0.321 0.407-0.696 0.516-1.125 0.117-0.43 0.176-0.907 0.176-1.43 0-0.5-0.067-0.957-0.199-1.371-0.125-0.414-0.317-0.774-0.575-1.078-0.25-0.305-0.566-0.539-0.949-0.703-0.383-0.172-0.824-0.258-1.324-0.258-0.672 0-1.188 0.098-1.547 0.293-0.352 0.195-0.68 0.437-0.984 0.726zm22.047-2.027v2.789c0 1.336-0.133 2.477-0.399 3.422-0.258 0.938-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.274-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.257-0.57-0.172-1.086-0.442-1.546-0.809-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.098-0.172-0.812-0.258-1.754-0.258-2.824v-2.789c0-1.344 0.133-2.477 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.012 1.758-1.277 0.688-0.266 1.453-0.399 2.297-0.399 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.359 0.851 0.828 1.172 1.406 0.328 0.571 0.578 1.262 0.75 2.075 0.172 0.804 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.61c0-0.679-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.965-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.891-0.226-0.234-0.484-0.402-0.773-0.504-0.289-0.109-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.852-0.539 1.453c-0.117 0.594-0.176 1.317-0.176 2.168v3.61c0 0.687 0.039 1.293 0.117 1.816s0.196 0.973 0.352 1.348c0.156 0.367 0.344 0.671 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.515 0.297 0.11 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.257 0.344-0.172 0.633-0.446 0.867-0.821 0.235-0.383 0.41-0.879 0.528-1.488 0.117-0.609 0.175-1.34 0.175-2.191z" fill="#000" fill-opacity=".38"/>
<path d="m139.51 600.32v2.25h-12.305l-0.094-1.699 7.36-11.531h2.261l-2.449 4.195-4.23 6.785h9.457zm-2.133-10.98v17.062h-2.824v-17.062h2.824zm15.391 7.101v2.79c0 1.335-0.133 2.476-0.399 3.421-0.258 0.938-0.633 1.7-1.125 2.286-0.492 0.585-1.082 1.015-1.769 1.289-0.68 0.273-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.441-1.546-0.809-0.461-0.367-0.856-0.839-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.097-0.172-0.813-0.258-1.754-0.258-2.824v-2.79c0-1.343 0.133-2.476 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.011 1.758-1.277 0.688-0.266 1.453-0.398 2.297-0.398 0.68 0 1.305 0.085 1.875 0.257 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.36 0.851 0.829 1.172 1.407 0.328 0.57 0.578 1.261 0.75 2.074 0.172 0.805 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.609c0-0.68-0.039-1.278-0.117-1.793-0.078-0.524-0.195-0.965-0.351-1.325-0.149-0.367-0.336-0.664-0.563-0.89-0.226-0.235-0.484-0.403-0.773-0.504-0.289-0.11-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.429-0.867 0.796-0.234 0.368-0.414 0.852-0.539 1.454-0.117 0.593-0.176 1.316-0.176 2.168v3.609c0 0.687 0.039 1.293 0.117 1.816 0.078 0.524 0.196 0.973 0.352 1.348 0.156 0.367 0.344 0.672 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.445 0.867-0.82c0.235-0.383 0.41-0.879 0.528-1.489 0.117-0.609 0.175-1.339 0.175-2.191z" fill="#000" fill-opacity=".38"/>
<path d="m131.07 693.99h1.688c0.656 0 1.199-0.113 1.629-0.34 0.437-0.226 0.761-0.539 0.972-0.937 0.211-0.399 0.317-0.856 0.317-1.371 0-0.539-0.098-1-0.293-1.383-0.188-0.391-0.477-0.691-0.867-0.902-0.383-0.211-0.872-0.317-1.465-0.317-0.5 0-0.953 0.102-1.36 0.305-0.398 0.195-0.715 0.476-0.949 0.844-0.234 0.359-0.351 0.789-0.351 1.289h-2.836c0-0.907 0.238-1.711 0.714-2.414 0.477-0.704 1.125-1.254 1.946-1.653 0.828-0.406 1.758-0.609 2.789-0.609 1.101 0 2.062 0.184 2.883 0.551 0.828 0.359 1.472 0.898 1.933 1.617s0.692 1.609 0.692 2.672c0 0.484-0.114 0.976-0.34 1.476-0.227 0.5-0.563 0.957-1.008 1.371-0.445 0.407-1 0.739-1.664 0.997-0.664 0.25-1.434 0.374-2.309 0.374h-2.121v-1.57zm0 2.203v-1.547h2.121c1 0 1.852 0.118 2.555 0.352 0.711 0.234 1.289 0.559 1.734 0.973 0.446 0.406 0.77 0.871 0.973 1.394 0.211 0.524 0.316 1.078 0.316 1.664 0 0.797-0.144 1.508-0.433 2.133-0.281 0.617-0.684 1.141-1.207 1.57-0.524 0.43-1.137 0.754-1.84 0.973-0.695 0.219-1.453 0.328-2.273 0.328-0.735 0-1.438-0.101-2.11-0.305-0.672-0.203-1.273-0.503-1.805-0.902-0.531-0.406-0.953-0.91-1.265-1.512-0.305-0.609-0.457-1.312-0.457-2.109h2.824c0 0.508 0.117 0.957 0.352 1.348 0.242 0.383 0.578 0.683 1.007 0.902 0.438 0.219 0.938 0.328 1.5 0.328 0.594 0 1.106-0.105 1.536-0.316 0.429-0.211 0.757-0.524 0.984-0.938 0.234-0.414 0.352-0.914 0.352-1.5 0-0.664-0.129-1.203-0.387-1.617s-0.625-0.719-1.102-0.914c-0.476-0.203-1.039-0.305-1.687-0.305h-1.688zm21.696-2.355v2.789c0 1.336-0.133 2.476-0.399 3.422-0.258 0.937-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.273-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.441-1.546-0.808-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.098-0.172-0.812-0.258-1.754-0.258-2.824v-2.789c0-1.344 0.133-2.477 0.399-3.399 0.265-0.929 0.644-1.683 1.136-2.261 0.492-0.586 1.078-1.012 1.758-1.278 0.688-0.265 1.453-0.398 2.297-0.398 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.359 0.851 0.828 1.172 1.406 0.328 0.57 0.578 1.262 0.75 2.074 0.172 0.805 0.258 1.743 0.258 2.813zm-2.825 3.187v-3.609c0-0.68-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.965-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.891-0.226-0.234-0.484-0.402-0.773-0.504-0.289-0.109-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.851-0.539 1.453c-0.117 0.594-0.176 1.316-0.176 2.168v3.609c0 0.688 0.039 1.293 0.117 1.817 0.078 0.523 0.196 0.972 0.352 1.347 0.156 0.368 0.344 0.672 0.562 0.914 0.227 0.235 0.485 0.407 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.445 0.867-0.82c0.235-0.383 0.41-0.879 0.528-1.488 0.117-0.61 0.175-1.34 0.175-2.192z" fill="#000" fill-opacity=".38"/>
<path d="m139.2 798.95v2.25h-11.437v-1.933l5.554-6.059c0.61-0.687 1.09-1.281 1.442-1.781 0.351-0.5 0.598-0.949 0.738-1.348 0.149-0.406 0.223-0.801 0.223-1.183 0-0.54-0.102-1.012-0.305-1.418-0.195-0.415-0.484-0.739-0.867-0.973-0.383-0.242-0.848-0.363-1.395-0.363-0.632 0-1.164 0.136-1.593 0.41-0.43 0.273-0.754 0.652-0.973 1.137-0.219 0.476-0.328 1.023-0.328 1.64h-2.824c0-0.992 0.226-1.898 0.679-2.719 0.453-0.828 1.11-1.484 1.969-1.968 0.86-0.493 1.895-0.739 3.106-0.739 1.14 0 2.109 0.192 2.906 0.575 0.797 0.382 1.402 0.925 1.816 1.628 0.422 0.704 0.633 1.536 0.633 2.497 0 0.531-0.086 1.058-0.258 1.582-0.172 0.523-0.418 1.046-0.738 1.57-0.313 0.516-0.684 1.035-1.113 1.558-0.43 0.516-0.903 1.04-1.418 1.571l-3.692 4.066h7.875zm13.575-7.711v2.789c0 1.336-0.133 2.477-0.399 3.422-0.258 0.938-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.274-1.442 0.411-2.286 0.411-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.442-1.546-0.809-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.097-0.172-0.813-0.258-1.754-0.258-2.825v-2.789c0-1.343 0.133-2.476 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.012 1.758-1.277 0.688-0.266 1.453-0.399 2.297-0.399 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.36 0.851 0.828 1.172 1.407 0.328 0.57 0.578 1.261 0.75 2.074 0.172 0.804 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.61c0-0.679-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.964-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.89-0.226-0.235-0.484-0.403-0.773-0.504-0.289-0.11-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.257-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.852-0.539 1.453c-0.117 0.594-0.176 1.317-0.176 2.168v3.61c0 0.687 0.039 1.293 0.117 1.816 0.078 0.524 0.196 0.973 0.352 1.348 0.156 0.367 0.344 0.672 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.446 0.867-0.821c0.235-0.382 0.41-0.878 0.528-1.488 0.117-0.609 0.175-1.34 0.175-2.191z" fill="#000" fill-opacity=".38"/>
<path d="m135.47 881.48v17.121h-2.824v-13.77l-4.184 1.418v-2.332l6.668-2.437h0.34zm17.301 7.16v2.789c0 1.336-0.133 2.476-0.399 3.422-0.258 0.937-0.633 1.699-1.125 2.285s-1.082 1.015-1.769 1.289c-0.68 0.273-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.258-0.57-0.172-1.086-0.441-1.546-0.808-0.461-0.368-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.286-0.75-2.098-0.172-0.813-0.258-1.754-0.258-2.824v-2.789c0-1.344 0.133-2.477 0.399-3.399 0.265-0.929 0.644-1.683 1.136-2.261 0.492-0.586 1.078-1.012 1.758-1.278 0.688-0.265 1.453-0.398 2.297-0.398 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.425 1.547 0.785 0.461 0.359 0.851 0.828 1.172 1.406 0.328 0.57 0.578 1.262 0.75 2.074 0.172 0.805 0.258 1.742 0.258 2.813zm-2.825 3.187v-3.609c0-0.68-0.039-1.278-0.117-1.793-0.078-0.524-0.195-0.965-0.351-1.324-0.149-0.368-0.336-0.664-0.563-0.891-0.226-0.234-0.484-0.402-0.773-0.504-0.289-0.109-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.851-0.539 1.453c-0.117 0.594-0.176 1.316-0.176 2.168v3.609c0 0.688 0.039 1.293 0.117 1.817 0.078 0.523 0.196 0.972 0.352 1.347 0.156 0.367 0.344 0.672 0.562 0.914 0.227 0.235 0.485 0.407 0.774 0.516 0.297 0.109 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.258s0.633-0.445 0.867-0.82c0.235-0.383 0.41-0.879 0.528-1.488 0.117-0.61 0.175-1.34 0.175-2.192z" fill="#000" fill-opacity=".38"/>
<path d="m152.77 986.04v2.789c0 1.336-0.133 2.477-0.399 3.422-0.258 0.938-0.633 1.699-1.125 2.285s-1.082 1.016-1.769 1.289c-0.68 0.274-1.442 0.41-2.286 0.41-0.671 0-1.296-0.086-1.875-0.257-0.57-0.172-1.086-0.442-1.546-0.809-0.461-0.367-0.856-0.84-1.184-1.418-0.32-0.586-0.57-1.285-0.75-2.098-0.172-0.812-0.258-1.754-0.258-2.824v-2.789c0-1.344 0.133-2.477 0.399-3.398 0.265-0.93 0.644-1.684 1.136-2.262 0.492-0.586 1.078-1.012 1.758-1.277 0.688-0.266 1.453-0.399 2.297-0.399 0.68 0 1.305 0.086 1.875 0.258 0.578 0.164 1.094 0.426 1.547 0.785 0.461 0.359 0.851 0.828 1.172 1.406 0.328 0.571 0.578 1.262 0.75 2.075 0.172 0.804 0.258 1.742 0.258 2.812zm-2.825 3.188v-3.61c0-0.679-0.039-1.277-0.117-1.793-0.078-0.523-0.195-0.965-0.351-1.324-0.149-0.367-0.336-0.664-0.563-0.891-0.226-0.234-0.484-0.402-0.773-0.504-0.289-0.109-0.614-0.164-0.973-0.164-0.438 0-0.828 0.086-1.172 0.258-0.344 0.164-0.633 0.43-0.867 0.797s-0.414 0.852-0.539 1.453c-0.117 0.594-0.176 1.317-0.176 2.168v3.61c0 0.687 0.039 1.293 0.117 1.816s0.196 0.973 0.352 1.348c0.156 0.367 0.344 0.671 0.562 0.914 0.227 0.234 0.485 0.406 0.774 0.515 0.297 0.11 0.621 0.164 0.972 0.164 0.446 0 0.84-0.086 1.184-0.257 0.344-0.172 0.633-0.446 0.867-0.821 0.235-0.383 0.41-0.879 0.528-1.488 0.117-0.609 0.175-1.34 0.175-2.191z" fill="#000" fill-opacity=".38"/>
</g><path d="m201.79 0s-201.79 0-201.79 167.5v820.9c0 6.628 5.3727 11.601 12 11.601h576c6.627 0 12-4.973 12-11.601v-820.9c0-167.5-198.21-167.5-198.21-167.5h-101.79zm201.21 203c-3.866 0-7 3.134-7 7v751c0 3.866 3.134 7 7 7h43.998c3.866 0 7-3.134 7-7v-751c0-3.866-3.134-7-7-7z" fill="#000" fill-opacity="0" style="stroke-width:.55902" tb:tag="clickArea"/><g fill="#d12730" style="display: none;" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><g transform="translate(0,-4.9442)" fill="#faa405" style="display: none;" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 50 KiB

328
application/src/main/data/json/system/scada_symbols/vertical-valve-hp.svg

@ -0,0 +1,328 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:tb="https://thingsboard.io/svg" width="200" height="200" fill="none" version="1.1" viewBox="0 0 200 200"><tb:metadata xmlns=""><![CDATA[{
"title": "HP Vertical valve",
"description": "Vertical valve with various states.",
"searchTags": [
"valve"
],
"widgetSizeX": 1,
"widgetSizeY": 1,
"tags": [
{
"tag": "background",
"stateRenderFunction": "var color = ctx.properties.closedColor;\nif (ctx.values.opened) {\n color = ctx.properties.openedColor;\n}\nelement.attr({fill: color});",
"actions": null
},
{
"tag": "clickArea",
"stateRenderFunction": null,
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'click');"
}
}
},
{
"tag": "critical",
"stateRenderFunction": "element.attr({fill: ctx.properties.criticalColor});\nif (ctx.values.critical) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = ctx.values.critical && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'criticalClick');"
}
}
},
{
"tag": "warning",
"stateRenderFunction": "element.attr({fill: ctx.properties.warningColor});\nvar warning = ctx.values.warning && !(ctx.values.warning && ctx.values.critical)\nif (warning) {\n element.show();\n} else {\n element.hide();\n}\n\nvar elementCriticalAnimation = element.remember('criticalAnimation');\nvar criticalAnimation = warning && ctx.values.criticalAnimation;\n\nif (elementCriticalAnimation !== criticalAnimation) {\n element.remember('criticalAnimation', criticalAnimation);\n if (criticalAnimation) {\n ctx.api.cssAnimate(element, 500).attr({opacity: 0.15}).loop(0, true);\n } else {\n ctx.api.resetCssAnimation(element);\n }\n}\n",
"actions": {
"click": {
"actionFunction": "ctx.api.callAction(event, 'warningClick');"
}
}
}
],
"behavior": [
{
"id": "opened",
"name": "{i18n:scada.symbol.opened}",
"hint": "{i18n:scada.symbol.opened-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.opened}",
"defaultGetValueSettings": {
"action": "GET_ATTRIBUTE",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"scope": "SERVER_SCOPE",
"key": "open"
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"dataToValueFunction": "/* Should return boolean value */\nreturn data;",
"compareToValue": true
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warning",
"name": "{i18n:scada.symbol.warning-state}",
"hint": "{i18n:scada.symbol.warning-state-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.warning}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "critical",
"name": "{i18n:scada.symbol.critical-state}",
"hint": "{i18n:scada.symbol.critical-state-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.critical}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "criticalAnimation",
"name": "{i18n:scada.symbol.critical-state-animation}",
"hint": "{i18n:scada.symbol.critical-state-animation-hint}",
"group": null,
"type": "value",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": "{i18n:scada.symbol.animation}",
"defaultGetValueSettings": {
"action": "DO_NOTHING",
"defaultValue": false,
"executeRpc": {
"method": "getState",
"requestTimeout": 5000,
"requestPersistent": false,
"persistentPollingInterval": 1000
},
"getAttribute": {
"key": "state",
"scope": null
},
"getTimeSeries": {
"key": "state"
},
"dataToValue": {
"type": "NONE",
"compareToValue": true,
"dataToValueFunction": "/* Should return boolean value */\nreturn data;"
}
},
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": null
},
{
"id": "warningClick",
"name": "{i18n:scada.symbol.warning-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
},
{
"id": "criticalClick",
"name": "{i18n:scada.symbol.critical-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
},
{
"id": "click",
"name": "{i18n:scada.symbol.on-click}",
"hint": "{i18n:scada.symbol.on-click-hint}",
"group": null,
"type": "widgetAction",
"valueType": "BOOLEAN",
"trueLabel": null,
"falseLabel": null,
"stateLabel": null,
"defaultGetValueSettings": null,
"defaultSetValueSettings": null,
"defaultWidgetActionSettings": {
"type": "doNothing",
"targetDashboardStateId": null,
"openRightLayout": false,
"setEntityId": false,
"stateEntityParamName": null
}
}
],
"properties": [
{
"id": "openedColor",
"name": "{i18n:scada.symbol.opened-color}",
"type": "color",
"default": "#FFFFFF",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "closedColor",
"name": "{i18n:scada.symbol.closed-color}",
"type": "color",
"default": "#666666",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "warningColor",
"name": "{i18n:scada.symbol.warning-color}",
"type": "color",
"default": "#FAA405",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
},
{
"id": "criticalColor",
"name": "{i18n:scada.symbol.critical-color}",
"type": "color",
"default": "#D12730",
"required": null,
"subLabel": null,
"divider": null,
"fieldSuffix": null,
"disableOnProperty": null,
"rowClass": "",
"fieldClass": "",
"min": null,
"max": null,
"step": null
}
]
}]]></tb:metadata>
<path d="m100 100h53" stroke="#000" stroke-opacity=".87" stroke-width="2"/><g fill="#fff" tb:tag="background">
<path d="m100 101 58-101h-116l58 101z" fill="#fff"/>
<path d="m43.727 0.99999 56.273 97.992 56.273-97.992h-112.55z" stroke="#000" stroke-opacity=".87" stroke-width="2"/>
<path d="M100 99L158 200H42L100 99Z" fill="#fff"/>
<path d="M43.7274 199L100 101.008L156.273 199H43.7274Z" stroke="#000" stroke-opacity=".87" stroke-width="2"/>
</g><rect x="152.54" y="52.185" width="48.169" height="96.338" fill="#DEDEDE"/><rect x="153.54" y="53.185" width="46.169" height="94.338" stroke="#000" stroke-opacity=".87" stroke-width="2"/><path d="m67.648-1e-4s-67.264 0-67.264 33.5v164.18c0 1.3256 1.7909 2.3202 4 2.3202h192c2.209 0 4-0.99459 4-2.3202v-164.18c0-33.5-66.07-33.5-66.07-33.5h-33.929zm67.07 40.6c-1.2887 0-2.3333 0.6268-2.3333 1.4v150.2c0 0.7732 1.0447 1.4 2.3333 1.4h14.666c1.2887 0 2.3333-0.6268 2.3333-1.4v-150.2c0-0.7732-1.0447-1.4-2.3333-1.4z" fill="#000" fill-opacity="0" tb:tag="clickArea"/><g transform="translate(0,116)" fill="#d12730" style="display: none;" tb:tag="critical">
<rect width="84" height="84" rx="4" fill="#fff" style=""/>
<rect width="84" height="84" rx="4" style=""/>
<rect x="2" y="2" width="80" height="80" rx="2" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 27.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g><g transform="translate(0,116)" fill="#faa405" style="display: none;" tb:tag="warning">
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" fill="#fff" style=""/>
<path d="m38.422 7.1554c1.4741-2.9482 5.6813-2.9482 7.1554 0l35.528 71.056c1.3298 2.6596-0.6042 5.7889-3.5777 5.7889h-71.056c-2.9735 0-4.9075-3.1292-3.5777-5.7889z" style=""/>
<path d="m40.211 8.0498c0.7371-1.4741 2.8407-1.4741 3.5778-1e-5l35.528 71.056c0.6649 1.3298-0.3021 2.8944-1.7888 2.8944h-71.056c-1.4868 0-2.4538-1.5646-1.7889-2.8944z" stroke="#000" stroke-opacity=".87" stroke-width="4" style=""/>
<path d="m44.559 37.562-0.4688 20.059h-4.0234l-0.4883-20.059zm-5.1172 26.211c0-0.7161 0.2344-1.3151 0.7031-1.7968 0.4818-0.4948 1.1459-0.7422 1.9922-0.7422 0.8334 0 1.4909 0.2474 1.9727 0.7422 0.4817 0.4817 0.7226 1.0807 0.7226 1.7968 0 0.6901-0.2409 1.2826-0.7226 1.7774-0.4818 0.4818-1.1393 0.7226-1.9727 0.7226-0.8463 0-1.5104-0.2408-1.9922-0.7226-0.4687-0.4948-0.7031-1.0873-0.7031-1.7774z" fill="#000" fill-opacity=".87" style=""/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

31
application/src/main/data/json/system/widget_bundles/general_high_performance_scada_symbols.json

@ -0,0 +1,31 @@
{
"widgetsBundle": {
"alias": "general_high_performance_scada_symbol",
"title": "General high-performance SCADA symbols",
"image": "tb-image:aHBfc2NhZGFfZ2VuZXJhbF9zeW1ib2xzX3N5c3RlbV9idW5kbGVfaW1hZ2UucG5n:IkdlbmVyYWwgaGlnaC1wZXJmb3JtYW5jZSBTQ0FEQSBzdW1ib2xzIiBzeXN0ZW0gYnVuZGxlIGltYWdl:SU1BR0U=;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAA8FBMVEXe3t7f39/f39/X19cAAADf39/e3t7d3d3e3t6VlZXI3/eZmZlmZmbGxsbr6+vc3Nytra3Y2Nikrri3xtZ9fX0aGho1NTW5ubnS0tKUlJS/v7+urq6zs7Pa2tqJiYm0tLR5eXnPz8/b29t8fHyhoaJxcXImJia+vr5WVlZjY2NNTU0wMDCpqamzx9zCx9GxsbGEhIRZWVlubm7Q09jJycmLmbhvg6tBQUG3ydvR0dEbPoW90+qzu8spSosNM3+nsMWZpb6TlpkAKHhhZWpfX19UVFRGRkbU1NR9jrKmpqZTbJ43VZFdZm5qampFYZh4foRgw9f8AAAACHRSTlPvvyAgAK+wr43PeosAAAVgSURBVHja7d1pV9pAGIZhuj9TMw1t02RiZawkYYlKRSm21qWb3Zf//286Y8RCgBgIlBecWwkh+sHrvIF45hyldPfOrRKWvXt37pbuLD9DpRgPsBKtwGmVVFoVCAyEWjcWsreftAdiTQp5YSU5IJaBgFgGAmIZCIhlICCWgWDSBJ9hYnEQyWbboiD8OWZayNFruSFlAzEQopCd9aSdohAh9NHFQbYeJW0UhVgMgIv+IrHMEMFCACHjCGtSS8pltS9CIBTcC0OhH+odshDJOXfBY2ZJRFHoiucJxI3kof4KXM4OWVRzpAu9E1OFxJZluXAc7ikOLx8+h8uRTEnKS4iFqx24gigkObWstuNIRBZz+yDMGQGJOWmIjACOGvBvIgIWEy5Q64O01Q7VUyuBIIpjCcc9jEPImOtj8WF8cbh/Iq7lSqKQsbkYjll0X35XH7JsF0QDoQKxbegPfX+5kxzVx5cI8uXbWkbfHkMlvPWMNr9QgNgv1zLzbAAeMvNmA3nyOunJNBDb3smGbEBJ1pHZ5mwgTx8mTQXBtRDbtpcAYg9ORH7+LFOQl2mI/8tH53zTiShB7BRkW0rX3/8kMyAbEZwd/1f9fJMYpNoPqalPue1sZ0BkiB3Hj+rn9XlAdn++nQqCFGRbQz4rTx+kqiB9P7TvY0duSHzCHCC778XHt7OYSCOOG2mIPQgRn/zzzpwgu+8BJZkKUlWQvl44a75cczImgo66/okO6nOA/IGK/yx4aqVLQ9IRm0gOSHX0dYTSc8SGgpSvn4jt/a8L4m70duqX33o2ZN1WdbaQUaez8OsIFKTCvfHVOxUNwdambuNis6Fv+rNXx6YBqTzOqFK1dehlA7Yq2VxFAZIlqVQqVQUBbPTXUxCDXEiuMMlepefoDcQecCRbOpAQl5JqtaI0A1Wr6qbSCsDGUIQmAnaVPz6WO4lFQZLs60LeFgyBrS1pD3r3SwS5tOhN8uDKhjyRggxVCEEKck0GMj4DKZSBjM9ACmUg41tRyMFZCxe1sPjyQR4jaQDSPf1+1AXw9fTkAAsvH8QPkdQHaZ220PpwgFdHFAaSE7JnRS+h6occvAOgID9eteYh4Zik/BDL2tsahOi634HTk5OjLmZesO0jf/khOn8rBXmnz6oP73BweoxZFzx7lp8yIcSKvAHI8UkLCtKCOr1QrDfldM1nzzSlPBRHYcjeOvohrZNjQCHOgKOzYoyoIVmqtobUApZONpuiGGT/ZerJfvS72+2eqfPr6/cTFKrpY6hAM95gRKxZBLIfPkYK8kp3DBx3vx6gSLyJERDFGOfm00PkFpCCzK6QYThvHANgDKlyQxRjjhDGCn8/jV8aDURlIAZiIAYyPwhjBmIgBmIgBmIgU0I4Y80mY2LpIaJ2sSzCMZwMMFFNb5EQBBoSYETbNYEJ8hpYKETUxgzEV778611h0OazgBzvJoWYuGDcQNTCj8i93sXKwCwg6yLpG6YYybiBjBGi6SNnRf4QZvKCaMxAkpFkr3dRgjCG4XgQNBpB4F273kUdknXcQAzEQAzEQAzEQAzkhkG8etIarlpOyIj/r2UgNwMytHy0rJCh5SNCEC/KXKfKXj6iBEHDG+lr5Fk+IgXhjWbWOlX28hElCFBmQ5VzLx9RgkxUECFvtCFkryOXGUhGBmIgKgPJ6CZDGkkBpo4GhGw3FSJCDsBjZVBrQojF2oBz6FiuAK0mhLTDNrhGtCVoNSGEMw7Z1jsctCph4hxyb9kxLSQCwaaAhDEASW0sU0DgSlF2V+A5Am7F7ecg1k29stPNQKhlINRamTfWLd3CSnS/dHslRqIYd2/fw7JXunX77l/CoXsjeoRJ3gAAAABJRU5ErkJggg==",
"scada": true,
"description": "Bundle with high-performance SCADA symbols",
"order": 9500,
"name": "General high-performance SCADA symbols"
},
"widgetTypeFqns": [
"hp_control_panel",
"hp_simple_horizontal_scale",
"hp_dynamic_horizontal_scale",
"hp_simple_vertical_scale",
"hp_dynamic_vertical_scale",
"hp_long_horizontal_connector",
"hp_long_vertical_connector",
"hp_horizontal_connector",
"hp_vertical_connector",
"hp_top_right_elbow_connector",
"hp_left_bottom_elbow_connector",
"hp_bottom_right_elbow_connector",
"hp_left_top_elbow_connector",
"hp_cross_connector",
"hp_bottom_tee_connector",
"hp_right_tee_connector",
"hp_left_tee_connector",
"hp_top_tee_connector"
]
}

23
application/src/main/data/json/system/widget_bundles/high_performance_scada_fluid_system.json

File diff suppressed because one or more lines are too long

2
application/src/main/data/json/system/widget_bundles/scada_fluid_system.json

File diff suppressed because one or more lines are too long

153
application/src/main/data/upgrade/3.8.1/schema_update.sql

@ -24,3 +24,156 @@ UPDATE user_credentials c SET failed_login_attempts = (SELECT (additional_info::
UPDATE tb_user SET additional_info = (additional_info::jsonb - 'lastLoginTs' - 'failedLoginAttempts' - 'userCredentialsEnabled')::text
WHERE additional_info IS NOT NULL AND additional_info != 'null';
-- CREATE MOBILE APP BUNDLES FROM EXISTING APPS
CREATE TABLE IF NOT EXISTS mobile_app_bundle (
id uuid NOT NULL CONSTRAINT mobile_app_bundle_pkey PRIMARY KEY,
created_time bigint NOT NULL,
tenant_id uuid,
title varchar(255),
description varchar(1024),
android_app_id uuid UNIQUE,
ios_app_id uuid UNIQUE,
layout_config varchar(16384),
oauth2_enabled boolean,
CONSTRAINT fk_android_app_id FOREIGN KEY (android_app_id) REFERENCES mobile_app(id),
CONSTRAINT fk_ios_app_id FOREIGN KEY (ios_app_id) REFERENCES mobile_app(id)
);
CREATE INDEX IF NOT EXISTS mobile_app_bundle_tenant_id ON mobile_app_bundle(tenant_id);
ALTER TABLE mobile_app ADD COLUMN IF NOT EXISTS platform_type varchar(32),
ADD COLUMN IF NOT EXISTS status varchar(32),
ADD COLUMN IF NOT EXISTS version_info varchar(100000),
ADD COLUMN IF NOT EXISTS store_info varchar(16384),
DROP CONSTRAINT IF EXISTS mobile_app_pkg_name_key,
DROP CONSTRAINT IF EXISTS mobile_app_unq_key;
-- rename mobile_app_oauth2_client to mobile_app_bundle_oauth2_client
DO
$$
BEGIN
-- in case of running the upgrade script a second time
IF EXISTS(SELECT 1 FROM information_schema.tables WHERE table_name = 'mobile_app_oauth2_client') THEN
ALTER TABLE mobile_app_oauth2_client RENAME TO mobile_app_bundle_oauth2_client;
ALTER TABLE mobile_app_bundle_oauth2_client DROP CONSTRAINT IF EXISTS fk_domain;
ALTER TABLE mobile_app_bundle_oauth2_client RENAME COLUMN mobile_app_id TO mobile_app_bundle_id;
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'fk_mobile_app_bundle_oauth2_client_bundle_id') THEN
ALTER TABLE mobile_app_bundle_oauth2_client ADD CONSTRAINT fk_mobile_app_bundle_oauth2_client_bundle_id
FOREIGN KEY (mobile_app_bundle_id) REFERENCES mobile_app_bundle(id) ON DELETE CASCADE;
END IF;
END IF;
END;
$$;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- duplicate each mobile app and create mobile app bundle for the pair of android and ios app
DO
$$
DECLARE
generatedBundleId uuid;
iosAppId uuid;
mobileAppRecord RECORD;
BEGIN
-- in case of running the upgrade script a second time
IF EXISTS(SELECT 1 FROM information_schema.columns WHERE table_name = 'mobile_app' and column_name = 'oauth2_enabled') THEN
UPDATE mobile_app SET platform_type = 'ANDROID' WHERE platform_type IS NULL;
UPDATE mobile_app SET status = 'DRAFT' WHERE mobile_app.status IS NULL;
FOR mobileAppRecord IN SELECT * FROM mobile_app
LOOP
-- duplicate app for iOS platform type
iosAppId := uuid_generate_v4();
INSERT INTO mobile_app(id, created_time, tenant_id, pkg_name, app_secret, platform_type, status)
VALUES (iosAppId, mobileAppRecord.created_time, mobileAppRecord.tenant_id, mobileAppRecord.pkg_name, mobileAppRecord.app_secret, 'IOS', mobileAppRecord.status)
ON CONFLICT DO NOTHING;
-- create bundle for android and iOS app
generatedBundleId := uuid_generate_v4();
INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, android_app_id, ios_app_id, oauth2_enabled)
VALUES (generatedBundleId, mobileAppRecord.created_time, mobileAppRecord.tenant_id,
mobileAppRecord.pkg_name || ' (autogenerated)', mobileAppRecord.id, iosAppId, mobileAppRecord.oauth2_enabled)
ON CONFLICT DO NOTHING;
UPDATE mobile_app_bundle_oauth2_client SET mobile_app_bundle_id = generatedBundleId WHERE mobile_app_bundle_id = mobileAppRecord.id;
END LOOP;
END IF;
ALTER TABLE mobile_app DROP COLUMN IF EXISTS oauth2_enabled;
IF NOT EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'mobile_app_pkg_name_platform_unq_key') THEN
ALTER TABLE mobile_app ADD CONSTRAINT mobile_app_pkg_name_platform_unq_key UNIQUE (pkg_name, platform_type);
END IF;
END;
$$;
ALTER TABLE IF EXISTS mobile_app_settings RENAME TO qr_code_settings;
ALTER TABLE qr_code_settings ADD COLUMN IF NOT EXISTS mobile_app_bundle_id uuid,
ADD COLUMN IF NOT EXISTS android_enabled boolean,
ADD COLUMN IF NOT EXISTS ios_enabled boolean;
-- migrate mobile apps from qr code settings to mobile_app, create mobile app bundle for the pair of apps
DO
$$
DECLARE
androidPkgName varchar;
iosPkgName varchar;
androidAppId uuid;
iosAppId uuid;
generatedBundleId uuid;
qrCodeRecord RECORD;
BEGIN
-- in case of running the upgrade script a second time
IF EXISTS(SELECT 1 FROM information_schema.columns WHERE table_name = 'qr_code_settings' AND column_name = 'android_config') THEN
FOR qrCodeRecord IN SELECT * FROM qr_code_settings
LOOP
generatedBundleId := NULL;
-- migrate android config
IF (qrCodeRecord.android_config IS NOT NULL AND qrCodeRecord.android_config::jsonb -> 'appPackage' IS NOT NULL) THEN
androidPkgName := qrCodeRecord.android_config::jsonb ->> 'appPackage';
SELECT id into androidAppId FROM mobile_app WHERE pkg_name = androidPkgName AND platform_type = 'ANDROID';
IF androidAppId IS NULL THEN
androidAppId := uuid_generate_v4();
INSERT INTO mobile_app(id, created_time, tenant_id, pkg_name, platform_type, status, store_info)
VALUES (androidAppId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id,
androidPkgName, 'ANDROID', 'DRAFT', qrCodeRecord.android_config::jsonb - 'appPackage' - 'enabled');
generatedBundleId := uuid_generate_v4();
INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, android_app_id)
VALUES (generatedBundleId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id, androidPkgName || ' (autogenerated)', androidAppId);
UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId,
android_enabled = (qrCodeRecord.android_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id;
ELSE
UPDATE mobile_app SET store_info = qrCodeRecord.android_config::jsonb - 'appPackage' - 'enabled' WHERE id = androidAppId;
UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.android_app_id = androidAppId),
android_enabled = (qrCodeRecord.android_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id;
END IF;
END IF;
-- migrate ios config
IF (qrCodeRecord.ios_config IS NOT NULL AND qrCodeRecord.ios_config::jsonb -> 'appId' IS NOT NULL) THEN
iosPkgName := substring(qrCodeRecord.ios_config::jsonb ->> 'appId', strpos(qrCodeRecord.ios_config::jsonb ->> 'appId', '.') + 1);
SELECT id INTO iosAppId FROM mobile_app WHERE pkg_name = iosPkgName AND platform_type = 'IOS';
IF iosAppId IS NULL THEN
iosAppId := uuid_generate_v4();
INSERT INTO mobile_app(id, created_time, tenant_id, pkg_name, platform_type, status, store_info)
VALUES (iosAppId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id,
iosPkgName, 'IOS', 'DRAFT', qrCodeRecord.ios_config);
IF generatedBundleId IS NULL THEN
generatedBundleId := uuid_generate_v4();
INSERT INTO mobile_app_bundle(id, created_time, tenant_id, title, ios_app_id)
VALUES (generatedBundleId, (extract(epoch from now()) * 1000), qrCodeRecord.tenant_id, iosPkgName || ' (autogenerated)', iosAppId);
UPDATE qr_code_settings SET mobile_app_bundle_id = generatedBundleId,
ios_enabled = (qrCodeRecord.ios_config::jsonb ->> 'enabled')::boolean WHERE id = qrCodeRecord.id;
ELSE
UPDATE mobile_app_bundle SET ios_app_id = iosAppId WHERE id = generatedBundleId;
END IF;
ELSE
UPDATE qr_code_settings SET mobile_app_bundle_id = (SELECT id FROM mobile_app_bundle WHERE mobile_app_bundle.ios_app_id = iosAppId),
ios_enabled = (qrCodeRecord.ios_config::jsonb -> 'enabled')::boolean WHERE id = qrCodeRecord.id;
UPDATE mobile_app SET store_info = qrCodeRecord.ios_config::jsonb - 'enabled' WHERE id = iosAppId;
END IF;
END IF;
END LOOP;
ALTER TABLE qr_code_settings RENAME CONSTRAINT mobile_app_settings_tenant_id_unq_key TO qr_code_settings_tenant_id_unq_key;
END IF;
ALTER TABLE qr_code_settings DROP COLUMN IF EXISTS android_config, DROP COLUMN IF EXISTS ios_config;
END;
$$;

5
application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java

@ -74,6 +74,7 @@ import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.entity.EntityService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.dao.mobile.MobileAppBundleService;
import org.thingsboard.server.dao.mobile.MobileAppService;
import org.thingsboard.server.dao.nosql.CassandraBufferedRateReadExecutor;
import org.thingsboard.server.dao.nosql.CassandraBufferedRateWriteExecutor;
@ -385,6 +386,10 @@ public class ActorSystemContext {
@Getter
private MobileAppService mobileAppService;
@Autowired
@Getter
private MobileAppBundleService mobileAppBundleService;
@Autowired
@Getter
private SlackService slackService;

6
application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java

@ -89,6 +89,7 @@ import org.thingsboard.server.dao.edge.EdgeService;
import org.thingsboard.server.dao.entity.EntityService;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.event.EventService;
import org.thingsboard.server.dao.mobile.MobileAppBundleService;
import org.thingsboard.server.dao.mobile.MobileAppService;
import org.thingsboard.server.dao.nosql.CassandraStatementTask;
import org.thingsboard.server.dao.nosql.TbResultSetFuture;
@ -843,6 +844,11 @@ class DefaultTbContext implements TbContext {
return mainCtx.getMobileAppService();
}
@Override
public MobileAppBundleService getMobileAppBundleService() {
return mainCtx.getMobileAppBundleService();
}
@Override
public SlackService getSlackService() {
return mainCtx.getSlackService();

52
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -51,6 +51,7 @@ import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.EntityViewInfo;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.HomeDashboardInfo;
import org.thingsboard.server.common.data.OtaPackage;
import org.thingsboard.server.common.data.OtaPackageInfo;
import org.thingsboard.server.common.data.StringUtils;
@ -87,6 +88,7 @@ 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.MobileAppBundleId;
import org.thingsboard.server.common.data.id.MobileAppId;
import org.thingsboard.server.common.data.id.OAuth2ClientId;
import org.thingsboard.server.common.data.id.OtaPackageId;
@ -101,7 +103,8 @@ import org.thingsboard.server.common.data.id.UUIDBased;
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.mobile.MobileApp;
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.OAuth2Client;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.SortOrder;
@ -135,6 +138,7 @@ 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.mobile.MobileAppBundleService;
import org.thingsboard.server.dao.mobile.MobileAppService;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.oauth2.OAuth2ClientService;
@ -198,6 +202,11 @@ 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";
protected final Logger log = org.slf4j.LoggerFactory.getLogger(getClass());
/*Swagger UI description*/
@ -262,6 +271,9 @@ public abstract class BaseController {
@Autowired
protected MobileAppService mobileAppService;
@Autowired
protected MobileAppBundleService mobileAppBundleService;
@Autowired
protected OAuth2ConfigTemplateService oAuth2ConfigTemplateService;
@ -645,6 +657,9 @@ public abstract class BaseController {
case MOBILE_APP:
checkMobileAppId(new MobileAppId(entityId.getId()), operation);
return;
case MOBILE_APP_BUNDLE:
checkMobileAppBundleId(new MobileAppBundleId(entityId.getId()), operation);
return;
default:
checkEntityId(entityId, entitiesService::findEntityByTenantIdAndId, operation);
}
@ -833,6 +848,10 @@ public abstract class BaseController {
return checkEntityId(mobileAppId, mobileAppService::findMobileAppById, operation);
}
MobileAppBundle checkMobileAppBundleId(MobileAppBundleId mobileAppBundleId, Operation operation) throws ThingsboardException {
return checkEntityId(mobileAppBundleId, mobileAppBundleService::findMobileAppBundleById, operation);
}
protected <I extends EntityId> I emptyId(EntityType entityType) {
return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID);
}
@ -915,6 +934,37 @@ public abstract class BaseController {
}
}
protected HomeDashboardInfo getHomeDashboardInfo(SecurityUser securityUser, JsonNode additionalInfo) {
HomeDashboardInfo homeDashboardInfo = extractHomeDashboardInfoFromAdditionalInfo(additionalInfo);
if (homeDashboardInfo == null) {
if (securityUser.isCustomerUser()) {
Customer customer = customerService.findCustomerById(securityUser.getTenantId(), securityUser.getCustomerId());
homeDashboardInfo = extractHomeDashboardInfoFromAdditionalInfo(customer.getAdditionalInfo());
}
if (homeDashboardInfo == null) {
Tenant tenant = tenantService.findTenantById(securityUser.getTenantId());
homeDashboardInfo = extractHomeDashboardInfoFromAdditionalInfo(tenant.getAdditionalInfo());
}
}
return homeDashboardInfo;
}
private HomeDashboardInfo extractHomeDashboardInfoFromAdditionalInfo(JsonNode additionalInfo) {
try {
if (additionalInfo != null && additionalInfo.has(HOME_DASHBOARD_ID) && !additionalInfo.get(HOME_DASHBOARD_ID).isNull()) {
String strDashboardId = additionalInfo.get(HOME_DASHBOARD_ID).asText();
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
checkDashboardId(dashboardId, Operation.READ);
boolean hideDashboardToolbar = true;
if (additionalInfo.has(HOME_DASHBOARD_HIDE_TOOLBAR)) {
hideDashboardToolbar = additionalInfo.get(HOME_DASHBOARD_HIDE_TOOLBAR).asBoolean();
}
return new HomeDashboardInfo(dashboardId, hideDashboardToolbar);
}
} catch (Exception ignored) {}
return null;
}
protected MediaType parseMediaType(String contentType) {
try {
return MediaType.parseMediaType(contentType);

33
application/src/main/java/org/thingsboard/server/controller/DashboardController.java

@ -101,7 +101,6 @@ public class DashboardController extends BaseController {
private final TbResourceService tbResourceService;
public static final String DASHBOARD_ID = "dashboardId";
private static final String HOME_DASHBOARD_ID = "homeDashboardId";
private static final String HOME_DASHBOARD_HIDE_TOOLBAR = "homeDashboardHideToolbar";
public static final String DASHBOARD_INFO_DEFINITION = "The Dashboard Info object contains lightweight information about the dashboard (e.g. title, image, assigned customers) but does not contain the heavyweight configuration JSON.";
@ -450,21 +449,7 @@ public class DashboardController extends BaseController {
}
User user = userService.findUserById(securityUser.getTenantId(), securityUser.getId());
JsonNode additionalInfo = user.getAdditionalInfo();
HomeDashboardInfo homeDashboardInfo;
homeDashboardInfo = extractHomeDashboardInfoFromAdditionalInfo(additionalInfo);
if (homeDashboardInfo == null) {
if (securityUser.isCustomerUser()) {
Customer customer = customerService.findCustomerById(securityUser.getTenantId(), securityUser.getCustomerId());
additionalInfo = customer.getAdditionalInfo();
homeDashboardInfo = extractHomeDashboardInfoFromAdditionalInfo(additionalInfo);
}
if (homeDashboardInfo == null) {
Tenant tenant = tenantService.findTenantById(securityUser.getTenantId());
additionalInfo = tenant.getAdditionalInfo();
homeDashboardInfo = extractHomeDashboardInfoFromAdditionalInfo(additionalInfo);
}
}
return homeDashboardInfo;
return getHomeDashboardInfo(securityUser, additionalInfo);
}
@ApiOperation(value = "Get Tenant Home Dashboard Info (getTenantHomeDashboardInfo)",
@ -517,22 +502,6 @@ public class DashboardController extends BaseController {
tenantService.saveTenant(tenant);
}
private HomeDashboardInfo extractHomeDashboardInfoFromAdditionalInfo(JsonNode additionalInfo) {
try {
if (additionalInfo != null && additionalInfo.has(HOME_DASHBOARD_ID) && !additionalInfo.get(HOME_DASHBOARD_ID).isNull()) {
String strDashboardId = additionalInfo.get(HOME_DASHBOARD_ID).asText();
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
checkDashboardId(dashboardId, Operation.READ);
boolean hideDashboardToolbar = true;
if (additionalInfo.has(HOME_DASHBOARD_HIDE_TOOLBAR)) {
hideDashboardToolbar = additionalInfo.get(HOME_DASHBOARD_HIDE_TOOLBAR).asBoolean();
}
return new HomeDashboardInfo(dashboardId, hideDashboardToolbar);
}
} catch (Exception ignored) {}
return null;
}
private HomeDashboard extractHomeDashboardFromAdditionalInfo(JsonNode additionalInfo) {
try {
if (additionalInfo != null && additionalInfo.has(HOME_DASHBOARD_ID) && !additionalInfo.get(HOME_DASHBOARD_ID).isNull()) {

131
application/src/main/java/org/thingsboard/server/controller/MobileAppBundleController.java

@ -0,0 +1,131 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.ArraySchema;
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.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.MobileAppBundleId;
import org.thingsboard.server.common.data.id.OAuth2ClientId;
import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundle;
import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundleInfo;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.mobile.TbMobileAppBundleService;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.List;
import java.util.UUID;
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.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK;
@RestController
@TbCoreComponent
@RequestMapping("/api")
@RequiredArgsConstructor
@Slf4j
public class MobileAppBundleController extends BaseController {
private final TbMobileAppBundleService tbMobileAppBundleService;
@ApiOperation(value = "Save Or update Mobile app bundle (saveMobileAppBundle)",
notes = "Create or update the Mobile app bundle that represents tha pair of ANDROID and IOS app and " +
"mobile settings like oauth2 clients, self-registration and layout configuration." +
"When creating mobile app bundle, platform generates Mobile App Bundle Id as " + UUID_WIKI_LINK +
"The newly created Mobile App Bundle Id will be present in the response. " +
"Referencing non-existing Mobile App Bundle Id will cause 'Not Found' error." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@PostMapping(value = "/mobile/bundle")
public MobileAppBundle saveMobileAppBundle(
@Parameter(description = "A JSON value representing the Mobile Application Bundle.", required = true)
@RequestBody @Valid MobileAppBundle mobileAppBundle,
@Parameter(description = "A list of oauth2 client ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")))
@RequestParam(name = "oauth2ClientIds", required = false) UUID[] ids) throws Exception {
mobileAppBundle.setTenantId(getTenantId());
checkEntity(mobileAppBundle.getId(), mobileAppBundle, Resource.MOBILE_APP_BUNDLE);
return tbMobileAppBundleService.save(mobileAppBundle, getOAuth2ClientIds(ids), getCurrentUser());
}
@ApiOperation(value = "Update oauth2 clients (updateOauth2Clients)",
notes = "Update oauth2 clients of the specified mobile app bundle." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@PutMapping(value = "/mobile/bundle/{id}/oauth2Clients")
public void updateOauth2Clients(@PathVariable UUID id,
@RequestBody UUID[] clientIds) throws ThingsboardException {
MobileAppBundleId mobileAppBundleId = new MobileAppBundleId(id);
MobileAppBundle mobileAppBundle = checkMobileAppBundleId(mobileAppBundleId, Operation.WRITE);
List<OAuth2ClientId> oAuth2ClientIds = getOAuth2ClientIds(clientIds);
tbMobileAppBundleService.updateOauth2Clients(mobileAppBundle, oAuth2ClientIds, getCurrentUser());
}
@ApiOperation(value = "Get mobile app bundle infos (getTenantMobileAppBundleInfos)", notes = SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/mobile/bundle/infos")
public PageData<MobileAppBundleInfo> getTenantMobileAppBundleInfos(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
@Parameter(description = "Case-insensitive 'substring' filter based on app's name")
@RequestParam(required = false) String textSearch,
@Parameter(description = SORT_PROPERTY_DESCRIPTION)
@RequestParam(required = false) String sortProperty,
@Parameter(description = SORT_ORDER_DESCRIPTION)
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
return mobileAppBundleService.findMobileAppBundleInfosByTenantId(getTenantId(), pageLink);
}
@ApiOperation(value = "Get mobile app bundle info by id (getMobileAppBundleInfoById)", notes = SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/mobile/bundle/info/{id}")
public MobileAppBundleInfo getMobileAppBundleInfoById(@PathVariable UUID id) throws ThingsboardException {
MobileAppBundleId mobileAppBundleId = new MobileAppBundleId(id);
return checkEntityId(mobileAppBundleId, mobileAppBundleService::findMobileAppBundleInfoById, Operation.READ);
}
@ApiOperation(value = "Delete Mobile App Bundle by ID (deleteMobileAppBundle)",
notes = "Deletes Mobile App Bundle by ID. Referencing non-existing mobile app bundle Id will cause an error." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@DeleteMapping(value = "/mobile/bundle/{id}")
public void deleteMobileAppBundle(@PathVariable UUID id) throws Exception {
MobileAppBundleId mobileAppBundleId = new MobileAppBundleId(id);
MobileAppBundle mobileAppBundle = checkMobileAppBundleId(mobileAppBundleId, Operation.DELETE);
tbMobileAppBundleService.delete(mobileAppBundle, getCurrentUser());
}
}

143
application/src/main/java/org/thingsboard/server/controller/MobileAppController.java

@ -15,8 +15,9 @@
*/
package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
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.validation.Valid;
import lombok.RequiredArgsConstructor;
@ -26,33 +27,43 @@ 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.common.util.JacksonUtil;
import org.thingsboard.server.common.data.HomeDashboardInfo;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.Views;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.MobileAppId;
import org.thingsboard.server.common.data.id.OAuth2ClientId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.MobileApp;
import org.thingsboard.server.common.data.mobile.MobileAppInfo;
import org.thingsboard.server.common.data.mobile.LoginMobileInfo;
import org.thingsboard.server.common.data.mobile.UserMobileInfo;
import org.thingsboard.server.common.data.mobile.app.MobileApp;
import org.thingsboard.server.common.data.mobile.app.MobileAppVersionInfo;
import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundle;
import org.thingsboard.server.common.data.mobile.layout.MobilePage;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientLoginInfo;
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.config.annotations.ApiOperation;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.entitiy.mobile.TbMobileAppService;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import static org.thingsboard.server.controller.ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER;
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.SYSTEM_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK;
@RestController
@ -64,70 +75,104 @@ public class MobileAppController extends BaseController {
private final TbMobileAppService tbMobileAppService;
@ApiOperation(value = "Get mobile app login info (getLoginMobileInfo)")
@GetMapping(value = "/noauth/mobile")
public LoginMobileInfo getLoginMobileInfo(@Parameter(description = "Mobile application package name")
@RequestParam String pkgName,
@Parameter(description = "Platform type", schema = @Schema(allowableValues = {"ANDROID", "IOS"}))
@RequestParam PlatformType platform) {
List<OAuth2ClientLoginInfo> oauth2Clients = oAuth2ClientService.findOAuth2ClientLoginInfosByMobilePkgNameAndPlatformType(pkgName, platform);
MobileApp mobileApp = mobileAppService.findMobileAppByPkgNameAndPlatformType(pkgName, platform);
return new LoginMobileInfo(oauth2Clients, mobileApp != null ? mobileApp.getVersionInfo() : null);
}
@ApiOperation(value = "Get user mobile app basic info (getUserMobileInfo)", notes = AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN','TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/mobile")
public UserMobileInfo getUserMobileInfo(@Parameter(description = "Mobile application package name")
@RequestParam String pkgName,
@Parameter(description = "Platform type", schema = @Schema(allowableValues = {"ANDROID", "IOS"}))
@RequestParam PlatformType platform) throws ThingsboardException, JsonProcessingException {
SecurityUser securityUser = getCurrentUser();
User user = userService.findUserById(securityUser.getTenantId(), securityUser.getId());
HomeDashboardInfo homeDashboardInfo = securityUser.isSystemAdmin() ? null : getHomeDashboardInfo(securityUser, user.getAdditionalInfo());
MobileAppBundle mobileAppBundle = mobileAppBundleService.findMobileAppBundleByPkgNameAndPlatform(securityUser.getTenantId(), pkgName, platform);
return new UserMobileInfo(user, homeDashboardInfo, getVisiblePages(mobileAppBundle));
}
@ApiOperation(value = "Get mobile app version info (getMobileVersionInfo)")
@GetMapping(value = "/mobile/versionInfo")
public MobileAppVersionInfo getMobileVersionInfo(@Parameter(description = "Mobile application package name")
@RequestParam String pkgName,
@Parameter(description = "Platform type", schema = @Schema(allowableValues = {"ANDROID", "IOS"}))
@RequestParam PlatformType platform) {
MobileApp mobileApp = mobileAppService.findMobileAppByPkgNameAndPlatformType(pkgName, platform);
return mobileApp != null ? mobileApp.getVersionInfo() : null;
}
@ApiOperation(value = "Save Or update Mobile app (saveMobileApp)",
notes = "Create or update the Mobile app. When creating mobile app, platform generates Mobile App Id as " + UUID_WIKI_LINK +
"The newly created Mobile App Id will be present in the response. " +
"Specify existing Mobile App Id to update the mobile app. " +
"Referencing non-existing Mobile App Id will cause 'Not Found' error." +
"\n\nMobile app package name is unique for entire platform setup.\n\n" + SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@PostMapping(value = "/mobileApp")
"\n\nThe pair of mobile app package name and platform type is unique for entire platform setup.\n\n" + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@PostMapping(value = "/mobile/app")
public MobileApp saveMobileApp(
@Parameter(description = "A JSON value representing the Mobile Application.", required = true)
@RequestBody @Valid MobileApp mobileApp,
@Parameter(description = "A list of entity oauth2 client ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")))
@RequestParam(name = "oauth2ClientIds", required = false) UUID[] ids) throws Exception {
@RequestBody @Valid MobileApp mobileApp) throws Exception {
mobileApp.setTenantId(getTenantId());
checkEntity(mobileApp.getId(), mobileApp, Resource.MOBILE_APP);
return tbMobileAppService.save(mobileApp, getOAuth2ClientIds(ids), getCurrentUser());
return tbMobileAppService.save(mobileApp, getCurrentUser());
}
@ApiOperation(value = "Update oauth2 clients (updateOauth2Clients)",
notes = "Update oauth2 clients of the specified mobile app. ")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@PutMapping(value = "/mobileApp/{id}/oauth2Clients")
public void updateOauth2Clients(@PathVariable UUID id,
@RequestBody UUID[] clientIds) throws ThingsboardException {
MobileAppId mobileAppId = new MobileAppId(id);
MobileApp mobileApp = checkMobileAppId(mobileAppId, Operation.WRITE);
List<OAuth2ClientId> oAuth2ClientIds = getOAuth2ClientIds(clientIds);
tbMobileAppService.updateOauth2Clients(mobileApp, oAuth2ClientIds, getCurrentUser());
}
@ApiOperation(value = "Get mobile app infos (getTenantMobileAppInfos)", notes = SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@GetMapping(value = "/mobileApp/infos")
public PageData<MobileAppInfo> getTenantMobileAppInfos(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
@Parameter(description = "Case-insensitive 'substring' filter based on app's name")
@RequestParam(required = false) String textSearch,
@Parameter(description = SORT_PROPERTY_DESCRIPTION)
@RequestParam(required = false) String sortProperty,
@Parameter(description = SORT_ORDER_DESCRIPTION)
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
accessControlService.checkPermission(getCurrentUser(), Resource.MOBILE_APP, Operation.READ);
@ApiOperation(value = "Get mobile app infos (getTenantMobileAppInfos)", notes = SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/mobile/app")
public PageData<MobileApp> getTenantMobileApps(@Parameter(description = "Platform type: ANDROID or IOS")
@RequestParam(required = false) PlatformType platformType,
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@Parameter(description = PAGE_NUMBER_DESCRIPTION, required = true)
@RequestParam int page,
@Parameter(description = "Case-insensitive 'substring' filter based on app's name")
@RequestParam(required = false) String textSearch,
@Parameter(description = SORT_PROPERTY_DESCRIPTION)
@RequestParam(required = false) String sortProperty,
@Parameter(description = SORT_ORDER_DESCRIPTION)
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
return mobileAppService.findMobileAppInfosByTenantId(getTenantId(), pageLink);
return mobileAppService.findMobileAppsByTenantId(getTenantId(), platformType, pageLink);
}
@ApiOperation(value = "Get mobile info by id (getMobileAppInfoById)", notes = SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@GetMapping(value = "/mobileApp/info/{id}")
public MobileAppInfo getMobileAppInfoById(@PathVariable UUID id) throws ThingsboardException {
@ApiOperation(value = "Get mobile info by id (getMobileAppInfoById)", notes = SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/mobile/app/{id}")
public MobileApp getMobileAppById(@PathVariable UUID id) throws ThingsboardException {
MobileAppId mobileAppId = new MobileAppId(id);
return checkEntityId(mobileAppId, mobileAppService::findMobileAppInfoById, Operation.READ);
return checkEntityId(mobileAppId, mobileAppService::findMobileAppById, Operation.READ);
}
@ApiOperation(value = "Delete Mobile App by ID (deleteMobileApp)",
notes = "Deletes Mobile App by ID. Referencing non-existing mobile app Id will cause an error." + SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('SYS_ADMIN')")
@DeleteMapping(value = "/mobileApp/{id}")
notes = "Deletes Mobile App by ID. Referencing non-existing mobile app Id will cause an error." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@DeleteMapping(value = "/mobile/app/{id}")
public void deleteMobileApp(@PathVariable UUID id) throws Exception {
MobileAppId mobileAppId = new MobileAppId(id);
MobileApp mobileApp = checkMobileAppId(mobileAppId, Operation.DELETE);
tbMobileAppService.delete(mobileApp, getCurrentUser());
}
private JsonNode getVisiblePages(MobileAppBundle mobileAppBundle) throws JsonProcessingException {
if (mobileAppBundle != null && mobileAppBundle.getLayoutConfig() != null) {
List<MobilePage> mobilePages = mobileAppBundle.getLayoutConfig().getPages()
.stream()
.filter(MobilePage::isVisible)
.collect(Collectors.toList());
return JacksonUtil.toJsonNode(JacksonUtil.writeValueAsViewIgnoringNullFields(mobilePages, Views.Public.class));
} else {
return JacksonUtil.newArrayNode();
}
}
}

29
application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java

@ -59,6 +59,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_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH;
@RestController
@TbCoreComponent
@ -104,8 +105,8 @@ public class OAuth2Controller extends BaseController {
}
}
@ApiOperation(value = "Save OAuth2 Client (saveOAuth2Client)", notes = SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@ApiOperation(value = "Save OAuth2 Client (saveOAuth2Client)", notes = SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@PostMapping(value = "/oauth2/client")
public OAuth2Client saveOAuth2Client(@RequestBody @Valid OAuth2Client oAuth2Client) throws Exception {
TenantId tenantId = getTenantId();
@ -114,8 +115,8 @@ public class OAuth2Controller extends BaseController {
return tbOauth2ClientService.save(oAuth2Client, getCurrentUser());
}
@ApiOperation(value = "Get OAuth2 Client infos (findTenantOAuth2ClientInfos)", notes = SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@ApiOperation(value = "Get OAuth2 Client infos (findTenantOAuth2ClientInfos)", notes = SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/oauth2/client/infos")
public PageData<OAuth2ClientInfo> findTenantOAuth2ClientInfos(@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@ -127,14 +128,13 @@ public class OAuth2Controller extends BaseController {
@RequestParam(required = false) String sortProperty,
@Parameter(description = SORT_ORDER_DESCRIPTION)
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CLIENT, Operation.READ);
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
return oAuth2ClientService.findOAuth2ClientInfosByTenantId(getTenantId(), pageLink);
}
@ApiOperation(value = "Get OAuth2 Client infos By Ids (findTenantOAuth2ClientInfosByIds)",
notes = "Fetch OAuth2 Client info objects based on the provided ids. ")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
notes = "Fetch OAuth2 Client info objects based on the provided ids. " + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/oauth2/client/infos", params = {"clientIds"})
public List<OAuth2ClientInfo> findTenantOAuth2ClientInfosByIds(
@Parameter(description = "A list of oauth2 ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")), required = true)
@ -143,8 +143,8 @@ public class OAuth2Controller extends BaseController {
return oAuth2ClientService.findOAuth2ClientInfosByIds(getTenantId(), oAuth2ClientIds);
}
@ApiOperation(value = "Get OAuth2 Client by id (getOAuth2ClientById)", notes = SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@ApiOperation(value = "Get OAuth2 Client by id (getOAuth2ClientById)", notes = SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/oauth2/client/{id}")
public OAuth2Client getOAuth2ClientById(@PathVariable UUID id) throws ThingsboardException {
OAuth2ClientId oAuth2ClientId = new OAuth2ClientId(id);
@ -152,8 +152,8 @@ public class OAuth2Controller extends BaseController {
}
@ApiOperation(value = "Delete oauth2 client (deleteOauth2Client)",
notes = "Deletes the oauth2 client. Referencing non-existing oauth2 client Id will cause an error." + SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAuthority('SYS_ADMIN')")
notes = "Deletes the oauth2 client. Referencing non-existing oauth2 client Id will cause an error." + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@DeleteMapping(value = "/oauth2/client/{id}")
public void deleteOauth2Client(@PathVariable UUID id) throws Exception {
OAuth2ClientId oAuth2ClientId = new OAuth2ClientId(id);
@ -164,11 +164,10 @@ public class OAuth2Controller extends BaseController {
@ApiOperation(value = "Get OAuth2 log in processing URL (getLoginProcessingUrl)", notes = "Returns the URL enclosed in " +
"double quotes. After successful authentication with OAuth2 provider, it makes a redirect to this path so that the platform can do " +
"further log in processing. This URL may be configured as 'security.oauth2.loginProcessingUrl' property in yml configuration file, or " +
"as 'SECURITY_OAUTH2_LOGIN_PROCESSING_URL' env variable. By default it is '/login/oauth2/code/'" + SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
"as 'SECURITY_OAUTH2_LOGIN_PROCESSING_URL' env variable. By default it is '/login/oauth2/code/'" + SYSTEM_OR_TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@GetMapping(value = "/oauth2/loginProcessingUrl")
public String getLoginProcessingUrl() throws ThingsboardException {
accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CLIENT, Operation.READ);
public String getLoginProcessingUrl() {
return "\"" + oAuth2Configuration.getLoginProcessingUrl() + "\"";
}

71
application/src/main/java/org/thingsboard/server/controller/MobileApplicationController.java → application/src/main/java/org/thingsboard/server/controller/QrCodeSettingsController.java

@ -31,13 +31,16 @@ 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.AndroidConfig;
import org.thingsboard.server.common.data.mobile.IosConfig;
import org.thingsboard.server.common.data.mobile.MobileAppSettings;
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.security.model.JwtPair;
import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.dao.mobile.MobileAppSettingsService;
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;
import org.thingsboard.server.service.security.model.SecurityUser;
@ -48,13 +51,15 @@ import org.thingsboard.server.service.security.system.SystemSecurityService;
import java.net.URI;
import java.net.URISyntaxException;
import static org.thingsboard.server.common.data.oauth2.PlatformType.ANDROID;
import static org.thingsboard.server.common.data.oauth2.PlatformType.IOS;
import static org.thingsboard.server.controller.ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER;
import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_AUTHORITY_PARAGRAPH;
@RequiredArgsConstructor
@RestController
@TbCoreComponent
public class MobileApplicationController extends BaseController {
public class QrCodeSettingsController extends BaseController {
@Value("${cache.specs.mobileSecretKey.timeToLiveInMinutes:2}")
private int mobileSecretKeyTtl;
@ -89,15 +94,15 @@ public class MobileApplicationController extends BaseController {
private final SystemSecurityService systemSecurityService;
private final MobileAppSecretService mobileAppSecretService;
private final MobileAppSettingsService mobileAppSettingsService;
private final QrCodeSettingService qrCodeSettingService;
@ApiOperation(value = "Get associated android applications (getAssetLinks)")
@GetMapping(value = "/.well-known/assetlinks.json")
public ResponseEntity<JsonNode> getAssetLinks() {
MobileAppSettings mobileAppSettings = mobileAppSettingsService.getMobileAppSettings(TenantId.SYS_TENANT_ID);
AndroidConfig androidConfig = mobileAppSettings.getAndroidConfig();
if (androidConfig != null && androidConfig.isEnabled() && androidConfig.getAppPackage() != null && androidConfig.getSha256CertFingerprints() != null) {
return ResponseEntity.ok(JacksonUtil.toJsonNode(String.format(ASSET_LINKS_PATTERN, androidConfig.getAppPackage(), androidConfig.getSha256CertFingerprints())));
MobileApp mobileApp = qrCodeSettingService.findAppFromQrCodeSettings(TenantId.SYS_TENANT_ID, ANDROID);
StoreInfo storeInfo = mobileApp != null ? mobileApp.getStoreInfo() : null;
if (storeInfo != null && storeInfo.getSha256CertFingerprints() != null) {
return ResponseEntity.ok(JacksonUtil.toJsonNode(String.format(ASSET_LINKS_PATTERN, mobileApp.getPkgName(), storeInfo.getSha256CertFingerprints())));
} else {
return ResponseEntity.notFound().build();
}
@ -106,10 +111,10 @@ public class MobileApplicationController extends BaseController {
@ApiOperation(value = "Get associated ios applications (getAppleAppSiteAssociation)")
@GetMapping(value = "/.well-known/apple-app-site-association")
public ResponseEntity<JsonNode> getAppleAppSiteAssociation() {
MobileAppSettings mobileAppSettings = mobileAppSettingsService.getMobileAppSettings(TenantId.SYS_TENANT_ID);
IosConfig iosConfig = mobileAppSettings.getIosConfig();
if (iosConfig != null && iosConfig.isEnabled() && iosConfig.getAppId() != null) {
return ResponseEntity.ok(JacksonUtil.toJsonNode(String.format(APPLE_APP_SITE_ASSOCIATION_PATTERN, iosConfig.getAppId())));
MobileApp mobileApp = qrCodeSettingService.findAppFromQrCodeSettings(TenantId.SYS_TENANT_ID, IOS);
StoreInfo storeInfo = mobileApp != null ? mobileApp.getStoreInfo() : null;
if (storeInfo != null && storeInfo.getAppId() != null) {
return ResponseEntity.ok(JacksonUtil.toJsonNode(String.format(APPLE_APP_SITE_ASSOCIATION_PATTERN, storeInfo.getAppId())));
} else {
return ResponseEntity.notFound().build();
}
@ -118,40 +123,35 @@ public class MobileApplicationController extends BaseController {
@ApiOperation(value = "Create Or Update the Mobile application settings (saveMobileAppSettings)",
notes = "The request payload contains configuration for android/iOS applications and platform qr code widget settings." + SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@PostMapping(value = "/api/mobile/app/settings")
public MobileAppSettings saveMobileAppSettings(@Parameter(description = "A JSON value representing the mobile apps configuration")
@RequestBody MobileAppSettings mobileAppSettings) throws ThingsboardException {
@PostMapping(value = "/api/mobile/qr/settings")
public QrCodeSettings saveQrCodeSettings(@Parameter(description = "A JSON value representing the mobile apps configuration")
@RequestBody QrCodeSettings qrCodeSettings) throws ThingsboardException {
SecurityUser currentUser = getCurrentUser();
accessControlService.checkPermission(currentUser, Resource.MOBILE_APP_SETTINGS, Operation.WRITE);
mobileAppSettings.setTenantId(getTenantId());
return mobileAppSettingsService.saveMobileAppSettings(currentUser.getTenantId(), mobileAppSettings);
qrCodeSettings.setTenantId(getTenantId());
return qrCodeSettingService.saveQrCodeSettings(currentUser.getTenantId(), qrCodeSettings);
}
@ApiOperation(value = "Get Mobile application settings (getMobileAppSettings)",
notes = "The response payload contains configuration for android/iOS applications and platform qr code widget settings." + AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/api/mobile/app/settings")
public MobileAppSettings getMobileAppSettings() throws ThingsboardException {
@GetMapping(value = "/api/mobile/qr/settings")
public QrCodeSettings getQrCodeSettings() throws ThingsboardException {
SecurityUser currentUser = getCurrentUser();
accessControlService.checkPermission(currentUser, Resource.MOBILE_APP_SETTINGS, Operation.READ);
return mobileAppSettingsService.getMobileAppSettings(TenantId.SYS_TENANT_ID);
return qrCodeSettingService.findQrCodeSettings(TenantId.SYS_TENANT_ID);
}
@ApiOperation(value = "Get the deep link to the associated mobile application (getMobileAppDeepLink)",
notes = "Fetch the url that takes user to linked mobile application " + AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@GetMapping(value = "/api/mobile/deepLink", produces = "text/plain")
@GetMapping(value = "/api/mobile/qr/deepLink", produces = "text/plain")
public String getMobileAppDeepLink(HttpServletRequest request) throws ThingsboardException, URISyntaxException {
String secret = mobileAppSecretService.generateMobileAppSecret(getCurrentUser());
String baseUrl = systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, null, request);
String platformDomain = new URI(baseUrl).getHost();
MobileAppSettings mobileAppSettings = mobileAppSettingsService.getMobileAppSettings(TenantId.SYS_TENANT_ID);
String appDomain;
if (!mobileAppSettings.isUseDefaultApp()) {
appDomain = platformDomain;
} else {
appDomain = defaultAppDomain;
}
QrCodeSettings qrCodeSettings = qrCodeSettingService.findQrCodeSettings(TenantId.SYS_TENANT_ID);
String appDomain = qrCodeSettings.isUseDefaultApp() ? defaultAppDomain : platformDomain;
String deepLink = String.format(DEEP_LINK_PATTERN, appDomain, secret, mobileSecretKeyTtl);
if (!appDomain.equals(platformDomain)) {
deepLink = deepLink + "&host=" + baseUrl;
@ -170,15 +170,14 @@ public class MobileApplicationController extends BaseController {
@GetMapping(value = "/api/noauth/qr")
public ResponseEntity<?> getApplicationRedirect(@RequestHeader(value = "User-Agent") String userAgent) {
MobileAppSettings mobileAppSettings = mobileAppSettingsService.getMobileAppSettings(TenantId.SYS_TENANT_ID);
boolean useDefaultApp = mobileAppSettings.isUseDefaultApp();
String googlePlayLink = useDefaultApp ? mobileAppSettings.getDefaultGooglePlayLink() : mobileAppSettings.getAndroidConfig().getStoreLink();
String appStoreLink = useDefaultApp ? mobileAppSettings.getDefaultAppStoreLink() : mobileAppSettings.getIosConfig().getStoreLink();
if (userAgent.contains("Android")) {
QrCodeSettings qrCodeSettings = qrCodeSettingService.findQrCodeSettings(TenantId.SYS_TENANT_ID);
if (userAgent.contains("Android") && qrCodeSettings.isAndroidEnabled()) {
String googlePlayLink = qrCodeSettings.getGooglePlayLink();
return ResponseEntity.status(HttpStatus.FOUND)
.header("Location", googlePlayLink)
.build();
} else if (userAgent.contains("iPhone") || userAgent.contains("iPad")) {
} else if (userAgent.contains("iPhone") || userAgent.contains("iPad") && qrCodeSettings.isIosEnabled()) {
String appStoreLink = qrCodeSettings.getAppStoreLink();
return ResponseEntity.status(HttpStatus.FOUND)
.header("Location", appStoreLink)
.build();

12
application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java

@ -34,13 +34,13 @@ import org.thingsboard.server.common.data.SystemParams;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.MobileAppSettings;
import org.thingsboard.server.common.data.mobile.QRCodeConfig;
import org.thingsboard.server.common.data.mobile.qrCodeSettings.QrCodeSettings;
import org.thingsboard.server.common.data.mobile.qrCodeSettings.QRCodeConfig;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.settings.UserSettings;
import org.thingsboard.server.common.data.settings.UserSettingsType;
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
import org.thingsboard.server.dao.mobile.MobileAppSettingsService;
import org.thingsboard.server.dao.mobile.QrCodeSettingService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.UserPrincipal;
@ -77,7 +77,7 @@ public class SystemInfoController extends BaseController {
private EntitiesVersionControlService versionControlService;
@Autowired
private MobileAppSettingsService mobileAppSettingsService;
private QrCodeSettingService qrCodeSettingService;
@PostConstruct
public void init() {
@ -142,8 +142,8 @@ public class SystemInfoController extends BaseController {
DefaultTenantProfileConfiguration tenantProfileConfiguration = tenantProfileCache.get(tenantId).getDefaultProfileConfiguration();
systemParams.setMaxResourceSize(tenantProfileConfiguration.getMaxResourceSize());
}
systemParams.setMobileQrEnabled(Optional.ofNullable(mobileAppSettingsService.getMobileAppSettings(TenantId.SYS_TENANT_ID))
.map(MobileAppSettings::getQrCodeConfig).map(QRCodeConfig::isShowOnHomePage)
systemParams.setMobileQrEnabled(Optional.ofNullable(qrCodeSettingService.findQrCodeSettings(TenantId.SYS_TENANT_ID))
.map(QrCodeSettings::getQrCodeConfig).map(QRCodeConfig::isShowOnHomePage)
.orElse(false));
return systemParams;
}

83
application/src/main/java/org/thingsboard/server/service/entitiy/mobile/DefaultTbMobileAppBundleService.java

@ -0,0 +1,83 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.entitiy.mobile;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.MobileAppBundleId;
import org.thingsboard.server.common.data.id.OAuth2ClientId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundle;
import org.thingsboard.server.dao.mobile.MobileAppBundleService;
import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
import java.util.List;
@Service
@AllArgsConstructor
public class DefaultTbMobileAppBundleService extends AbstractTbEntityService implements TbMobileAppBundleService {
private final MobileAppBundleService mobileAppBundleService;
@Override
public MobileAppBundle save(MobileAppBundle mobileAppBundle, List<OAuth2ClientId> oauth2Clients, User user) throws Exception {
ActionType actionType = mobileAppBundle.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
TenantId tenantId = mobileAppBundle.getTenantId();
try {
MobileAppBundle savedMobileAppBundle = checkNotNull(mobileAppBundleService.saveMobileAppBundle(tenantId, mobileAppBundle));
if (CollectionUtils.isNotEmpty(oauth2Clients)) {
mobileAppBundleService.updateOauth2Clients(tenantId, savedMobileAppBundle.getId(), oauth2Clients);
}
logEntityActionService.logEntityAction(tenantId, savedMobileAppBundle.getId(), savedMobileAppBundle, actionType, user);
return savedMobileAppBundle;
} catch (Exception e) {
logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.MOBILE_APP), mobileAppBundle, actionType, user, e);
throw e;
}
}
@Override
public void updateOauth2Clients(MobileAppBundle mobileAppBundle, List<OAuth2ClientId> oAuth2ClientIds, User user) {
ActionType actionType = ActionType.UPDATED;
TenantId tenantId = mobileAppBundle.getTenantId();
MobileAppBundleId mobileAppBundleId = mobileAppBundle.getId();
try {
mobileAppBundleService.updateOauth2Clients(tenantId, mobileAppBundleId, oAuth2ClientIds);
logEntityActionService.logEntityAction(tenantId, mobileAppBundleId, mobileAppBundle, actionType, user, oAuth2ClientIds);
} catch (Exception e) {
logEntityActionService.logEntityAction(tenantId, mobileAppBundleId, mobileAppBundle, actionType, user, e, oAuth2ClientIds);
throw e;
}
}
@Override
public void delete(MobileAppBundle mobileAppBundle, User user) {
ActionType actionType = ActionType.DELETED;
TenantId tenantId = mobileAppBundle.getTenantId();
MobileAppBundleId mobileAppBundleId = mobileAppBundle.getId();
try {
mobileAppBundleService.deleteMobileAppBundleById(tenantId, mobileAppBundleId);
logEntityActionService.logEntityAction(tenantId, mobileAppBundleId, mobileAppBundle, actionType, user);
} catch (Exception e) {
logEntityActionService.logEntityAction(tenantId, mobileAppBundleId, mobileAppBundle, actionType, user, e);
throw e;
}
}
}

24
application/src/main/java/org/thingsboard/server/service/entitiy/mobile/DefaultTbMobileAppService.java

@ -16,20 +16,16 @@
package org.thingsboard.server.service.entitiy.mobile;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.MobileAppId;
import org.thingsboard.server.common.data.id.OAuth2ClientId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.MobileApp;
import org.thingsboard.server.common.data.mobile.app.MobileApp;
import org.thingsboard.server.dao.mobile.MobileAppService;
import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
import java.util.List;
@Service
@AllArgsConstructor
public class DefaultTbMobileAppService extends AbstractTbEntityService implements TbMobileAppService {
@ -37,14 +33,11 @@ public class DefaultTbMobileAppService extends AbstractTbEntityService implement
private final MobileAppService mobileAppService;
@Override
public MobileApp save(MobileApp mobileApp, List<OAuth2ClientId> oauth2Clients, User user) throws Exception {
public MobileApp save(MobileApp mobileApp, User user) throws Exception {
ActionType actionType = mobileApp.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
TenantId tenantId = mobileApp.getTenantId();
try {
MobileApp savedMobileApp = checkNotNull(mobileAppService.saveMobileApp(tenantId, mobileApp));
if (CollectionUtils.isNotEmpty(oauth2Clients)) {
mobileAppService.updateOauth2Clients(tenantId, savedMobileApp.getId(), oauth2Clients);
}
logEntityActionService.logEntityAction(tenantId, savedMobileApp.getId(), savedMobileApp, actionType, user);
return savedMobileApp;
} catch (Exception e) {
@ -53,19 +46,6 @@ public class DefaultTbMobileAppService extends AbstractTbEntityService implement
}
}
@Override
public void updateOauth2Clients(MobileApp mobileApp, List<OAuth2ClientId> oAuth2ClientIds, User user) {
ActionType actionType = ActionType.UPDATED;
TenantId tenantId = mobileApp.getTenantId();
MobileAppId mobileAppId = mobileApp.getId();
try {
mobileAppService.updateOauth2Clients(tenantId, mobileAppId, oAuth2ClientIds);
logEntityActionService.logEntityAction(tenantId, mobileAppId, mobileApp, actionType, user, oAuth2ClientIds);
} catch (Exception e) {
logEntityActionService.logEntityAction(tenantId, mobileAppId, mobileApp, actionType, user, e, oAuth2ClientIds);
throw e;
}
}
@Override
public void delete(MobileApp mobileApp, User user) {

32
application/src/main/java/org/thingsboard/server/service/entitiy/mobile/TbMobileAppBundleService.java

@ -0,0 +1,32 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.entitiy.mobile;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.OAuth2ClientId;
import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundle;
import java.util.List;
public interface TbMobileAppBundleService {
MobileAppBundle save(MobileAppBundle mobileAppBundle, List<OAuth2ClientId> oauth2Clients, User user) throws Exception;
void updateOauth2Clients(MobileAppBundle mobileAppBundle, List<OAuth2ClientId> oAuth2ClientIds, User user);
void delete(MobileAppBundle mobileAppBundle, User user);
}

9
application/src/main/java/org/thingsboard/server/service/entitiy/mobile/TbMobileAppService.java

@ -16,16 +16,11 @@
package org.thingsboard.server.service.entitiy.mobile;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.OAuth2ClientId;
import org.thingsboard.server.common.data.mobile.MobileApp;
import java.util.List;
import org.thingsboard.server.common.data.mobile.app.MobileApp;
public interface TbMobileAppService {
MobileApp save(MobileApp mobileApp, List<OAuth2ClientId> oauth2Clients, User user) throws Exception;
void updateOauth2Clients(MobileApp mobileApp, List<OAuth2ClientId> oAuth2ClientIds, User user);
MobileApp save(MobileApp mobileApp, User user) throws Exception;
void delete(MobileApp mobileApp, User user);

4
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java

@ -69,7 +69,7 @@ import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.BooleanDataEntry;
import org.thingsboard.server.common.data.kv.DoubleDataEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.common.data.mobile.MobileApp;
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;
@ -308,7 +308,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
jwtSettingsService.saveJwtSettings(jwtSettings);
}
List<MobileApp> mobiles = mobileAppDao.findByTenantId(TenantId.SYS_TENANT_ID, new PageLink(Integer.MAX_VALUE,0)).getData();
List<MobileApp> mobiles = mobileAppDao.findByTenantId(TenantId.SYS_TENANT_ID, null, new PageLink(Integer.MAX_VALUE,0)).getData();
if (CollectionUtils.isNotEmpty(mobiles)) {
mobiles.stream()
.filter(mobileApp -> !validateKeyLength(mobileApp.getAppSecret()))

1
application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java

@ -36,6 +36,7 @@ public enum Resource {
OAUTH2_CLIENT(EntityType.OAUTH2_CLIENT),
DOMAIN(EntityType.DOMAIN),
MOBILE_APP(EntityType.MOBILE_APP),
MOBILE_APP_BUNDLE(EntityType.MOBILE_APP_BUNDLE),
OAUTH2_CONFIGURATION_TEMPLATE(),
TENANT_PROFILE(EntityType.TENANT_PROFILE),
DEVICE_PROFILE(EntityType.DEVICE_PROFILE),

5
application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java

@ -35,8 +35,9 @@ public class SysAdminPermissions extends AbstractPermissions {
put(Resource.USER, userPermissionChecker);
put(Resource.WIDGETS_BUNDLE, systemEntityPermissionChecker);
put(Resource.WIDGET_TYPE, systemEntityPermissionChecker);
put(Resource.OAUTH2_CLIENT, PermissionChecker.allowAllPermissionChecker);
put(Resource.MOBILE_APP, PermissionChecker.allowAllPermissionChecker);
put(Resource.OAUTH2_CLIENT, systemEntityPermissionChecker);
put(Resource.MOBILE_APP, systemEntityPermissionChecker);
put(Resource.MOBILE_APP_BUNDLE, systemEntityPermissionChecker);
put(Resource.DOMAIN, PermissionChecker.allowAllPermissionChecker);
put(Resource.OAUTH2_CONFIGURATION_TEMPLATE, PermissionChecker.allowAllPermissionChecker);
put(Resource.TENANT_PROFILE, PermissionChecker.allowAllPermissionChecker);

4
application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java

@ -51,6 +51,10 @@ public class TenantAdminPermissions extends AbstractPermissions {
put(Resource.VERSION_CONTROL, PermissionChecker.allowAllPermissionChecker);
put(Resource.NOTIFICATION, tenantEntityPermissionChecker);
put(Resource.MOBILE_APP_SETTINGS, new PermissionChecker.GenericPermissionChecker(Operation.READ));
put(Resource.OAUTH2_CLIENT, tenantEntityPermissionChecker);
put(Resource.OAUTH2_CONFIGURATION_TEMPLATE, new PermissionChecker.GenericPermissionChecker(Operation.READ));
put(Resource.MOBILE_APP, tenantEntityPermissionChecker);
put(Resource.MOBILE_APP_BUNDLE, tenantEntityPermissionChecker);
}
public static final PermissionChecker tenantEntityPermissionChecker = new PermissionChecker() {

4
application/src/main/resources/thingsboard.yml

@ -609,8 +609,8 @@ cache:
alarmTypes:
timeToLiveInMinutes: "${CACHE_SPECS_ALARM_TYPES_TTL:60}" # Alarm types cache TTL
maxSize: "${CACHE_SPECS_ALARM_TYPES_MAX_SIZE:10000}" # 0 means the cache is disabled
mobileAppSettings:
timeToLiveInMinutes: "${CACHE_SPECS_MOBILE_APP_SETTINGS_TTL:1440}" # Mobile application cache TTL
qrCodeSettings:
timeToLiveInMinutes: "${CACHE_SPECS_MOBILE_APP_SETTINGS_TTL:1440}" # Qr code settings cache TTL
maxSize: "${CACHE_SPECS_MOBILE_APP_SETTINGS_MAX_SIZE:10000}" # 0 means the cache is disabled
mobileSecretKey:
timeToLiveInMinutes: "${CACHE_MOBILE_SECRET_KEY_TTL:2}" # QR secret key cache TTL

169
application/src/test/java/org/thingsboard/server/controller/MobileAppBundleControllerTest.java

@ -0,0 +1,169 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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 lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.StringUtils;
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.mobile.bundle.MobileAppBundle;
import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundleInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2Client;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
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.dao.service.DaoSqlTest;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@Slf4j
@DaoSqlTest
public class MobileAppBundleControllerTest extends AbstractControllerTest {
static final TypeReference<PageData<MobileAppBundleInfo>> PAGE_DATA_MOBILE_APP_BUNDLE_TYPE_REF = new TypeReference<>() {
};
static final TypeReference<PageData<MobileApp>> PAGE_DATA_MOBILE_APP_TYPE_REF = new TypeReference<>() {
};
static final TypeReference<PageData<OAuth2ClientInfo>> PAGE_DATA_OAUTH2_CLIENT_TYPE_REF = new TypeReference<>() {
};
private MobileApp androidApp;
private MobileApp iosApp;
@Before
public void setUp() throws Exception {
loginSysAdmin();
androidApp = validMobileApp(TenantId.SYS_TENANT_ID, "my.android.package", PlatformType.ANDROID);
androidApp = doPost("/api/mobile/app", androidApp, MobileApp.class);
iosApp = validMobileApp(TenantId.SYS_TENANT_ID, "my.ios.package", PlatformType.IOS);
iosApp = doPost("/api/mobile/app", iosApp, MobileApp.class);
}
@After
public void tearDown() throws Exception {
PageData<MobileAppBundleInfo> pageData2 = doGetTypedWithPageLink("/api/mobile/bundle/infos?", PAGE_DATA_MOBILE_APP_BUNDLE_TYPE_REF, new PageLink(10, 0));
for (MobileAppBundleInfo appBundleInfo : pageData2.getData()) {
doDelete("/api/mobile/bundle/" + appBundleInfo.getId().getId())
.andExpect(status().isOk());
}
PageData<MobileApp> pageData = doGetTypedWithPageLink("/api/mobile/app?", PAGE_DATA_MOBILE_APP_TYPE_REF, new PageLink(10, 0));
for (MobileApp mobileApp : pageData.getData()) {
doDelete("/api/mobile/app/" + mobileApp.getId().getId())
.andExpect(status().isOk());
}
PageData<OAuth2ClientInfo> clients = doGetTypedWithPageLink("/api/oauth2/client/infos?", PAGE_DATA_OAUTH2_CLIENT_TYPE_REF, new PageLink(10, 0));
for (OAuth2ClientInfo oAuth2ClientInfo : clients.getData()) {
doDelete("/api/oauth2/client/" + oAuth2ClientInfo.getId().getId().toString())
.andExpect(status().isOk());
}
}
@Test
public void testSaveMobileAppBundle() {
MobileAppBundle mobileAppBundle = new MobileAppBundle();
mobileAppBundle.setTitle("Test bundle");
mobileAppBundle.setAndroidAppId(androidApp.getId());
mobileAppBundle.setIosAppId(iosApp.getId());
MobileAppBundle createdMobileAppBundle = doPost("/api/mobile/bundle", mobileAppBundle, MobileAppBundle.class);
assertThat(createdMobileAppBundle.getAndroidAppId()).isEqualTo(androidApp.getId());
assertThat(createdMobileAppBundle.getIosAppId()).isEqualTo(iosApp.getId());
}
@Test
public void testSaveMobileAppBundleWithoutApps() throws Exception {
MobileAppBundle mobileAppBundle = new MobileAppBundle();
mobileAppBundle.setTitle("Test bundle");
MobileAppBundle savedAppBundle = doPost("/api/mobile/bundle", mobileAppBundle, MobileAppBundle.class);
MobileAppBundleInfo retrievedMobileAppBundleInfo = doGet("/api/mobile/bundle/info/{id}", MobileAppBundleInfo.class, savedAppBundle.getId().getId());
assertThat(retrievedMobileAppBundleInfo).isEqualTo(new MobileAppBundleInfo(savedAppBundle, null, null, false,
Collections.emptyList()));
}
@Test
public void testUpdateMobileAppBundleOauth2Clients() throws Exception {
MobileAppBundle mobileAppBundle = new MobileAppBundle();
mobileAppBundle.setTitle("Test bundle");
mobileAppBundle.setAndroidAppId(androidApp.getId());
mobileAppBundle.setIosAppId(iosApp.getId());
MobileAppBundle savedAppBundle = doPost("/api/mobile/bundle", mobileAppBundle, MobileAppBundle.class);
OAuth2Client oAuth2Client = createOauth2Client(TenantId.SYS_TENANT_ID, "test google client");
OAuth2Client savedOAuth2Client = doPost("/api/oauth2/client", oAuth2Client, OAuth2Client.class);
OAuth2Client oAuth2Client2 = createOauth2Client(TenantId.SYS_TENANT_ID, "test facebook client");
OAuth2Client savedOAuth2Client2 = doPost("/api/oauth2/client", oAuth2Client2, OAuth2Client.class);
doPut("/api/mobile/bundle/" + savedAppBundle.getId() + "/oauth2Clients", List.of(savedOAuth2Client.getId().getId(), savedOAuth2Client2.getId().getId()));
MobileAppBundleInfo retrievedMobileAppBundleInfo = doGet("/api/mobile/bundle/info/{id}", MobileAppBundleInfo.class, savedAppBundle.getId().getId());
assertThat(retrievedMobileAppBundleInfo).isEqualTo(new MobileAppBundleInfo(savedAppBundle, androidApp.getPkgName(), iosApp.getPkgName(), false,
Stream.of(new OAuth2ClientInfo(savedOAuth2Client), new OAuth2ClientInfo(savedOAuth2Client2))
.sorted(Comparator.comparing(OAuth2ClientInfo::getTitle)).collect(Collectors.toList())
));
doPut("/api/mobile/bundle/" + savedAppBundle.getId() + "/oauth2Clients", List.of(savedOAuth2Client2.getId().getId()));
MobileAppBundleInfo retrievedMobileAppInfo2 = doGet("/api/mobile/bundle/info/{id}", MobileAppBundleInfo.class, savedAppBundle.getId().getId());
assertThat(retrievedMobileAppInfo2).isEqualTo(new MobileAppBundleInfo(savedAppBundle, androidApp.getPkgName(), iosApp.getPkgName(), false, List.of(new OAuth2ClientInfo(savedOAuth2Client2))));
}
@Test
public void testCreateMobileAppBundleWithOauth2Clients() throws Exception {
OAuth2Client oAuth2Client = createOauth2Client(TenantId.SYS_TENANT_ID, "test google client");
OAuth2Client savedOAuth2Client = doPost("/api/oauth2/client", oAuth2Client, OAuth2Client.class);
MobileAppBundle mobileAppBundle = new MobileAppBundle();
mobileAppBundle.setTitle("Test bundle");
mobileAppBundle.setAndroidAppId(androidApp.getId());
mobileAppBundle.setIosAppId(iosApp.getId());
MobileAppBundle savedMobileAppBundle = doPost("/api/mobile/bundle?oauth2ClientIds=" + savedOAuth2Client.getId().getId(), mobileAppBundle, MobileAppBundle.class);
MobileAppBundleInfo retrievedMobileAppInfo = doGet("/api/mobile/bundle/info/{id}", MobileAppBundleInfo.class, savedMobileAppBundle.getId().getId());
assertThat(retrievedMobileAppInfo).isEqualTo(new MobileAppBundleInfo(savedMobileAppBundle, androidApp.getPkgName(), iosApp.getPkgName(), false, List.of(new OAuth2ClientInfo(savedOAuth2Client))));
}
private MobileApp validMobileApp(TenantId tenantId, String mobileAppName, PlatformType platformType) {
MobileApp mobileApp = new MobileApp();
mobileApp.setTenantId(tenantId);
mobileApp.setStatus(MobileAppStatus.DRAFT);
mobileApp.setPkgName(mobileAppName);
mobileApp.setPlatformType(platformType);
mobileApp.setAppSecret(StringUtils.randomAlphanumeric(24));
return mobileApp;
}
}

104
application/src/test/java/org/thingsboard/server/controller/MobileAppControllerTest.java

@ -21,18 +21,13 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.MobileApp;
import org.thingsboard.server.common.data.mobile.MobileAppInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2Client;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
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.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.Collections;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -41,9 +36,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@DaoSqlTest
public class MobileAppControllerTest extends AbstractControllerTest {
static final TypeReference<PageData<MobileAppInfo>> PAGE_DATA_MOBILE_APP_TYPE_REF = new TypeReference<>() {
};
static final TypeReference<PageData<OAuth2ClientInfo>> PAGE_DATA_OAUTH2_CLIENT_TYPE_REF = new TypeReference<>() {
static final TypeReference<PageData<MobileApp>> PAGE_DATA_MOBILE_APP_TYPE_REF = new TypeReference<>() {
};
@Before
@ -53,89 +46,64 @@ public class MobileAppControllerTest extends AbstractControllerTest {
@After
public void tearDown() throws Exception {
PageData<MobileAppInfo> pageData = doGetTypedWithPageLink("/api/mobileApp/infos?", PAGE_DATA_MOBILE_APP_TYPE_REF, new PageLink(10, 0));
PageData<MobileApp> pageData = doGetTypedWithPageLink("/api/mobile/app?", PAGE_DATA_MOBILE_APP_TYPE_REF, new PageLink(10, 0));
for (MobileApp mobileApp : pageData.getData()) {
doDelete("/api/mobileApp/" + mobileApp.getId().getId())
.andExpect(status().isOk());
}
PageData<OAuth2ClientInfo> clients = doGetTypedWithPageLink("/api/oauth2/client/infos?", PAGE_DATA_OAUTH2_CLIENT_TYPE_REF, new PageLink(10, 0));
for (OAuth2ClientInfo oAuth2ClientInfo : clients.getData()) {
doDelete("/api/oauth2/client/" + oAuth2ClientInfo.getId().getId().toString())
doDelete("/api/mobile/app/" + mobileApp.getId().getId())
.andExpect(status().isOk());
}
}
@Test
public void testSaveMobileApp() throws Exception {
PageData<MobileAppInfo> pageData = doGetTypedWithPageLink("/api/mobileApp/infos?", PAGE_DATA_MOBILE_APP_TYPE_REF, new PageLink(10, 0));
PageData<MobileApp> pageData = doGetTypedWithPageLink("/api/mobile/app?", PAGE_DATA_MOBILE_APP_TYPE_REF, new PageLink(10, 0));
assertThat(pageData.getData()).isEmpty();
MobileApp mobileApp = validMobileApp(TenantId.SYS_TENANT_ID, "my.test.package", true);
MobileApp savedMobileApp = doPost("/api/mobileApp", mobileApp, MobileApp.class);
MobileApp mobileApp = validMobileApp("my.test.package", PlatformType.ANDROID);
MobileApp savedMobileApp = doPost("/api/mobile/app", mobileApp, MobileApp.class);
PageData<MobileAppInfo> pageData2 = doGetTypedWithPageLink("/api/mobileApp/infos?", PAGE_DATA_MOBILE_APP_TYPE_REF, new PageLink(10, 0));
PageData<MobileApp> pageData2 = doGetTypedWithPageLink("/api/mobile/app?", PAGE_DATA_MOBILE_APP_TYPE_REF, new PageLink(10, 0));
assertThat(pageData2.getData()).hasSize(1);
assertThat(pageData2.getData().get(0)).isEqualTo(new MobileAppInfo(savedMobileApp, Collections.emptyList()));
assertThat(pageData2.getData().get(0)).isEqualTo(savedMobileApp);
MobileAppInfo retrievedMobileAppInfo = doGet("/api/mobileApp/info/{id}", MobileAppInfo.class, savedMobileApp.getId().getId());
assertThat(retrievedMobileAppInfo).isEqualTo(new MobileAppInfo(savedMobileApp, Collections.emptyList()));
MobileApp retrievedMobileAppInfo = doGet("/api/mobile/app/{id}", MobileApp.class, savedMobileApp.getId().getId());
assertThat(retrievedMobileAppInfo).isEqualTo(savedMobileApp);
doDelete("/api/mobileApp/" + savedMobileApp.getId().getId());
doGet("/api/mobileApp/info/{id}", savedMobileApp.getId().getId())
doDelete("/api/mobile/app/" + savedMobileApp.getId().getId());
doGet("/api/mobile/app/{id}", savedMobileApp.getId().getId())
.andExpect(status().isNotFound());
}
@Test
public void testSaveMobileAppWithShortAppSecret() throws Exception {
MobileApp mobileApp = validMobileApp(TenantId.SYS_TENANT_ID, "mobileApp.ce", true);
MobileApp mobileApp = validMobileApp( "mobileApp.ce", PlatformType.ANDROID);
mobileApp.setAppSecret("short");
doPost("/api/mobileApp", mobileApp)
doPost("/api/mobile/app", mobileApp)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("appSecret must be at least 16 and max 2048 characters")));
}
@Test
public void testUpdateMobileAppOauth2Clients() throws Exception {
MobileApp mobileApp = validMobileApp(TenantId.SYS_TENANT_ID, "my.test.package", true);
MobileApp savedMobileApp = doPost("/api/mobileApp", mobileApp, MobileApp.class);
OAuth2Client oAuth2Client = createOauth2Client(TenantId.SYS_TENANT_ID, "test google client");
OAuth2Client savedOAuth2Client = doPost("/api/oauth2/client", oAuth2Client, OAuth2Client.class);
OAuth2Client oAuth2Client2 = createOauth2Client(TenantId.SYS_TENANT_ID, "test facebook client");
OAuth2Client savedOAuth2Client2 = doPost("/api/oauth2/client", oAuth2Client2, OAuth2Client.class);
doPut("/api/mobileApp/" + savedMobileApp.getId() + "/oauth2Clients", List.of(savedOAuth2Client.getId().getId(), savedOAuth2Client2.getId().getId()));
MobileAppInfo retrievedMobileAppInfo = doGet("/api/mobileApp/info/{id}", MobileAppInfo.class, savedMobileApp.getId().getId());
assertThat(retrievedMobileAppInfo).isEqualTo(new MobileAppInfo(savedMobileApp, List.of(new OAuth2ClientInfo(savedOAuth2Client2),
new OAuth2ClientInfo(savedOAuth2Client))));
doPut("/api/mobileApp/" + savedMobileApp.getId() + "/oauth2Clients", List.of(savedOAuth2Client2.getId().getId()));
MobileAppInfo retrievedMobileAppInfo2 = doGet("/api/mobileApp/info/{id}", MobileAppInfo.class, savedMobileApp.getId().getId());
assertThat(retrievedMobileAppInfo2).isEqualTo(new MobileAppInfo(savedMobileApp, List.of(new OAuth2ClientInfo(savedOAuth2Client2))));
}
@Test
public void testCreateMobileAppWithOauth2Clients() throws Exception {
OAuth2Client oAuth2Client = createOauth2Client(TenantId.SYS_TENANT_ID, "test google client");
OAuth2Client savedOAuth2Client = doPost("/api/oauth2/client", oAuth2Client, OAuth2Client.class);
MobileApp mobileApp = validMobileApp(TenantId.SYS_TENANT_ID, "my.test.package", true);
MobileApp savedMobileApp = doPost("/api/mobileApp?oauth2ClientIds=" + savedOAuth2Client.getId().getId(), mobileApp, MobileApp.class);
MobileAppInfo retrievedMobileAppInfo = doGet("/api/mobileApp/info/{id}", MobileAppInfo.class, savedMobileApp.getId().getId());
assertThat(retrievedMobileAppInfo).isEqualTo(new MobileAppInfo(savedMobileApp, List.of(new OAuth2ClientInfo(savedOAuth2Client))));
public void testGetTenantAppsByPlatformTypeSaveMobileApp() throws Exception {
MobileApp androidApp = doPost("/api/mobile/app", validMobileApp("android.1", PlatformType.ANDROID), MobileApp.class);
MobileApp androidApp2 = doPost("/api/mobile/app", validMobileApp("android.2", PlatformType.ANDROID), MobileApp.class);
MobileApp iosApp = doPost("/api/mobile/app", validMobileApp("ios.1", PlatformType.IOS), MobileApp.class);
PageData<MobileApp> pageData = doGetTypedWithPageLink("/api/mobile/app?", PAGE_DATA_MOBILE_APP_TYPE_REF, new PageLink(10, 0));
assertThat(pageData.getData()).hasSize(3);
assertThat(pageData.getData()).containsExactlyInAnyOrder(androidApp, androidApp2, iosApp);
PageData<MobileApp> androidPageData = doGetTypedWithPageLink("/api/mobile/app?platformType=ANDROID&", PAGE_DATA_MOBILE_APP_TYPE_REF, new PageLink(10, 0));
assertThat(androidPageData.getData()).hasSize(2);
assertThat(androidPageData.getData()).containsExactlyInAnyOrder(androidApp, androidApp2);
}
private MobileApp validMobileApp(TenantId tenantId, String mobileAppName, boolean oauth2Enabled) {
MobileApp MobileApp = new MobileApp();
MobileApp.setTenantId(tenantId);
MobileApp.setPkgName(mobileAppName);
MobileApp.setAppSecret(StringUtils.randomAlphanumeric(24));
MobileApp.setOauth2Enabled(oauth2Enabled);
return MobileApp;
private MobileApp validMobileApp(String mobileAppName, PlatformType platformType) {
MobileApp mobileApp = new MobileApp();
mobileApp.setPkgName(mobileAppName);
mobileApp.setAppSecret(StringUtils.randomAlphanumeric(24));
mobileApp.setPlatformType(platformType);
mobileApp.setStatus(MobileAppStatus.DRAFT);
return mobileApp;
}
}

253
application/src/test/java/org/thingsboard/server/controller/MobileApplicationControllerTest.java

@ -1,253 +0,0 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Value;
import org.thingsboard.server.common.data.mobile.AndroidConfig;
import org.thingsboard.server.common.data.mobile.IosConfig;
import org.thingsboard.server.common.data.mobile.MobileAppSettings;
import org.thingsboard.server.common.data.mobile.QRCodeConfig;
import org.thingsboard.server.common.data.security.model.JwtPair;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@Slf4j
@DaoSqlTest
public class MobileApplicationControllerTest extends AbstractControllerTest {
@Value("${cache.specs.mobileSecretKey.timeToLiveInMinutes:2}")
private int mobileSecretKeyTtl;
private static final String ANDROID_PACKAGE_NAME = "testAppPackage";
private static final String ANDROID_APP_SHA256 = "DF:28:32:66:8B:A7:D3:EC:7D:73:CF:CC";
private static final String APPLE_APP_ID = "testId";
private static final String TEST_LABEL = "Test label";
@Before
public void setUp() throws Exception {
loginSysAdmin();
MobileAppSettings mobileAppSettings = doGet("/api/mobile/app/settings", MobileAppSettings.class);
QRCodeConfig qrCodeConfig = new QRCodeConfig();
qrCodeConfig.setQrCodeLabel(TEST_LABEL);
mobileAppSettings.setUseDefaultApp(true);
AndroidConfig androidConfig = AndroidConfig.builder()
.appPackage(ANDROID_PACKAGE_NAME)
.sha256CertFingerprints(ANDROID_APP_SHA256)
.enabled(true)
.build();
IosConfig iosConfig = IosConfig.builder()
.appId(APPLE_APP_ID)
.enabled(true)
.build();
mobileAppSettings.setAndroidConfig(androidConfig);
mobileAppSettings.setIosConfig(iosConfig);
mobileAppSettings.setQrCodeConfig(qrCodeConfig);
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isOk());
}
@Test
public void testSaveMobileAppSettings() throws Exception {
loginSysAdmin();
MobileAppSettings mobileAppSettings = doGet("/api/mobile/app/settings", MobileAppSettings.class);
assertThat(mobileAppSettings.getQrCodeConfig().getQrCodeLabel()).isEqualTo(TEST_LABEL);
assertThat(mobileAppSettings.isUseDefaultApp()).isTrue();
mobileAppSettings.setUseDefaultApp(false);
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isOk());
MobileAppSettings updatedMobileAppSettings = doGet("/api/mobile/app/settings", MobileAppSettings.class);
assertThat(updatedMobileAppSettings.isUseDefaultApp()).isFalse();
}
@Test
public void testShouldNotSaveMobileAppSettingsWithoutRequiredConfig() throws Exception {
loginSysAdmin();
MobileAppSettings mobileAppSettings = doGet("/api/mobile/app/settings", MobileAppSettings.class);
mobileAppSettings.setUseDefaultApp(false);
mobileAppSettings.setAndroidConfig(null);
mobileAppSettings.setIosConfig(null);
mobileAppSettings.setQrCodeConfig(null);
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Android/ios settings are required to use custom application!")));
mobileAppSettings.setAndroidConfig(AndroidConfig.builder().enabled(false).build());
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Android/ios settings are required to use custom application!")));
mobileAppSettings.setIosConfig(IosConfig.builder().enabled(false).build());
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Qr code configuration is required!")));
mobileAppSettings.setQrCodeConfig(QRCodeConfig.builder().showOnHomePage(false).build());
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isOk());
}
@Test
public void testShouldNotSaveMobileAppSettingsWithoutRequiredAndroidConf() throws Exception {
loginSysAdmin();
MobileAppSettings mobileAppSettings = doGet("/api/mobile/app/settings", MobileAppSettings.class);
mobileAppSettings.setUseDefaultApp(false);
AndroidConfig androidConfig = AndroidConfig.builder()
.enabled(true)
.appPackage(null)
.sha256CertFingerprints(null)
.build();
mobileAppSettings.setAndroidConfig(androidConfig);
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Application package and sha256 cert fingerprints are required for custom android application!")));
androidConfig.setAppPackage("test_app_package");
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Application package and sha256 cert fingerprints are required for custom android application!")));
androidConfig.setSha256CertFingerprints("test_sha_256");
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isOk());
}
@Test
public void testShouldNotSaveMobileAppSettingsWithoutRequiredIosConf() throws Exception {
loginSysAdmin();
MobileAppSettings mobileAppSettings = doGet("/api/mobile/app/settings", MobileAppSettings.class);
mobileAppSettings.setUseDefaultApp(false);
IosConfig iosConfig = IosConfig.builder()
.enabled(true)
.appId(null)
.build();
mobileAppSettings.setIosConfig(iosConfig);
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Application id is required for custom ios application!")));
iosConfig.setAppId("test_app_id");
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isOk());
}
@Test
public void testShouldSaveMobileAppSettingsForDefaultApp() throws Exception {
loginSysAdmin();
MobileAppSettings mobileAppSettings = doGet("/api/mobile/app/settings", MobileAppSettings.class);
mobileAppSettings.setUseDefaultApp(true);
mobileAppSettings.setIosConfig(null);
mobileAppSettings.setAndroidConfig(null);
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isOk());
}
@Test
public void testGetApplicationAssociations() throws Exception {
loginSysAdmin();
MobileAppSettings mobileAppSettings = doGet("/api/mobile/app/settings", MobileAppSettings.class);
mobileAppSettings.setUseDefaultApp(false);
doPost("/api/mobile/app/settings", mobileAppSettings)
.andExpect(status().isOk());
JsonNode assetLinks = doGet("/.well-known/assetlinks.json", JsonNode.class);
assertThat(assetLinks.get(0).get("target").get("package_name").asText()).isEqualTo(ANDROID_PACKAGE_NAME);
assertThat(assetLinks.get(0).get("target").get("sha256_cert_fingerprints").get(0).asText()).isEqualTo(ANDROID_APP_SHA256);
JsonNode appleAssociation = doGet("/.well-known/apple-app-site-association", JsonNode.class);
assertThat(appleAssociation.get("applinks").get("details").get(0).get("appID").asText()).isEqualTo(APPLE_APP_ID);
}
@Test
public void testGetMobileDeepLink() throws Exception {
loginSysAdmin();
String deepLink = doGet("/api/mobile/deepLink", String.class);
Pattern expectedPattern = Pattern.compile("\"https://([^/]+)/api/noauth/qr\\?secret=([^&]+)&ttl=([^&]+)&host=([^&]+)\"");
Matcher parsedDeepLink = expectedPattern.matcher(deepLink);
assertThat(parsedDeepLink.matches()).isTrue();
String appHost = parsedDeepLink.group(1);
String secret = parsedDeepLink.group(2);
String ttl = parsedDeepLink.group(3);
assertThat(appHost).isEqualTo("demo.thingsboard.io");
assertThat(ttl).isEqualTo(String.valueOf(mobileSecretKeyTtl));
JwtPair jwtPair = doGet("/api/noauth/qr/" + secret, JwtPair.class);
assertThat(jwtPair).isNotNull();
loginTenantAdmin();
String tenantDeepLink = doGet("/api/mobile/deepLink", String.class);
Matcher tenantParsedDeepLink = expectedPattern.matcher(tenantDeepLink);
assertThat(tenantParsedDeepLink.matches()).isTrue();
String tenantSecret = tenantParsedDeepLink.group(2);
JwtPair tenantJwtPair = doGet("/api/noauth/qr/" + tenantSecret, JwtPair.class);
assertThat(tenantJwtPair).isNotNull();
loginCustomerUser();
String customerDeepLink = doGet("/api/mobile/deepLink", String.class);
Matcher customerParsedDeepLink = expectedPattern.matcher(customerDeepLink);
assertThat(customerParsedDeepLink.matches()).isTrue();
String customerSecret = customerParsedDeepLink.group(2);
JwtPair customerJwtPair = doGet("/api/noauth/qr/" + customerSecret, JwtPair.class);
assertThat(customerJwtPair).isNotNull();
// update mobile setting to use custom one
loginSysAdmin();
MobileAppSettings mobileAppSettings = doGet("/api/mobile/app/settings", MobileAppSettings.class);
mobileAppSettings.setUseDefaultApp(false);
doPost("/api/mobile/app/settings", mobileAppSettings);
String customAppDeepLink = doGet("/api/mobile/deepLink", String.class);
Pattern customAppExpectedPattern = Pattern.compile("\"https://([^/]+)/api/noauth/qr\\?secret=([^&]+)&ttl=([^&]+)\"");
Matcher customAppParsedDeepLink = customAppExpectedPattern.matcher(customAppDeepLink);
assertThat(customAppParsedDeepLink.matches()).isTrue();
assertThat(customAppParsedDeepLink.group(1)).isEqualTo("localhost");
loginTenantAdmin();
String tenantCustomAppDeepLink = doGet("/api/mobile/deepLink", String.class);
Matcher tenantCustomAppParsedDeepLink = customAppExpectedPattern.matcher(tenantCustomAppDeepLink);
assertThat(tenantCustomAppParsedDeepLink.matches()).isTrue();
assertThat(tenantCustomAppParsedDeepLink.group(1)).isEqualTo("localhost");
loginCustomerUser();
String customerCustomAppDeepLink = doGet("/api/mobile/deepLink", String.class);
Matcher customerCustomAppParsedDeepLink = customAppExpectedPattern.matcher(customerCustomAppDeepLink);
assertThat(customerCustomAppParsedDeepLink.matches()).isTrue();
assertThat(customerCustomAppParsedDeepLink.group(1)).isEqualTo("localhost");
}
}

262
application/src/test/java/org/thingsboard/server/controller/QrCodeSettingsControllerTest.java

@ -0,0 +1,262 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Value;
import org.thingsboard.server.common.data.StringUtils;
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.mobile.qrCodeSettings.QRCodeConfig;
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.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.model.JwtPair;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@Slf4j
@DaoSqlTest
public class QrCodeSettingsControllerTest extends AbstractControllerTest {
@Value("${cache.specs.mobileSecretKey.timeToLiveInMinutes:2}")
private int mobileSecretKeyTtl;
static final TypeReference<PageData<MobileAppBundleInfo>> PAGE_DATA_MOBILE_APP_BUNDLE_TYPE_REF = new TypeReference<>() {
};
static final TypeReference<PageData<MobileApp>> PAGE_DATA_MOBILE_APP_TYPE_REF = new TypeReference<>() {
};
private static final String ANDROID_PACKAGE_NAME = "testAppPackage";
private static final String ANDROID_APP_SHA256 = "DF:28:32:66:8B:A7:D3:EC:7D:73:CF:CC";
private static final String ANDROID_STORE_LINK = "https://store.link.com";
private static final String APPLE_APP_ID = "testId";
private static final String TEST_LABEL = "Test label";
private static final String IOS_STORE_LINK = "https://store.link.com";
private MobileAppBundle mobileAppBundle;
@Before
public void setUp() throws Exception {
loginSysAdmin();
MobileApp androidApp = validMobileApp( "my.android.package", PlatformType.ANDROID);
StoreInfo androidStoreInfo = StoreInfo.builder()
.sha256CertFingerprints(ANDROID_APP_SHA256)
.storeLink(ANDROID_STORE_LINK)
.build();
androidApp.setStoreInfo(androidStoreInfo);
MobileApp savedAndroidApp = doPost("/api/mobile/app", androidApp, MobileApp.class);
MobileApp iosApp = validMobileApp( "my.ios.package", PlatformType.IOS);
StoreInfo iosQrCodeConfig = StoreInfo.builder()
.appId(APPLE_APP_ID)
.storeLink(IOS_STORE_LINK)
.build();
iosApp.setStoreInfo(iosQrCodeConfig);
MobileApp savedIosApp = doPost("/api/mobile/app", iosApp, MobileApp.class);
mobileAppBundle = new MobileAppBundle();
mobileAppBundle.setTitle("Test bundle");
mobileAppBundle.setAndroidAppId(savedAndroidApp.getId());
mobileAppBundle.setIosAppId(savedIosApp.getId());
mobileAppBundle = doPost("/api/mobile/bundle", mobileAppBundle, MobileAppBundle.class);
QrCodeSettings qrCodeSettings = doGet("/api/mobile/qr/settings", QrCodeSettings.class);
QRCodeConfig qrCodeConfig = new QRCodeConfig();
qrCodeConfig.setQrCodeLabel(TEST_LABEL);
qrCodeSettings.setUseDefaultApp(true);
qrCodeSettings.setMobileAppBundleId(null);
qrCodeSettings.setQrCodeConfig(qrCodeConfig);
doPost("/api/mobile/qr/settings", qrCodeSettings)
.andExpect(status().isOk());
}
@After
public void tearDown() throws Exception {
loginSysAdmin();
PageData<MobileAppBundleInfo> pageData2 = doGetTypedWithPageLink("/api/mobile/bundle/infos?", PAGE_DATA_MOBILE_APP_BUNDLE_TYPE_REF, new PageLink(10, 0));
for (MobileAppBundleInfo appBundleInfo : pageData2.getData()) {
doDelete("/api/mobile/bundle/" + appBundleInfo.getId().getId())
.andExpect(status().isOk());
}
PageData<MobileApp> pageData = doGetTypedWithPageLink("/api/mobile/app?", PAGE_DATA_MOBILE_APP_TYPE_REF, new PageLink(10, 0));
for (MobileApp mobileApp : pageData.getData()) {
doDelete("/api/mobile/app/" + mobileApp.getId().getId())
.andExpect(status().isOk());
}
}
@Test
public void testSaveQrCodeSettings() throws Exception {
loginSysAdmin();
QrCodeSettings qrCodeSettings = doGet("/api/mobile/qr/settings", QrCodeSettings.class);
assertThat(qrCodeSettings.getQrCodeConfig().getQrCodeLabel()).isEqualTo(TEST_LABEL);
assertThat(qrCodeSettings.isUseDefaultApp()).isTrue();
qrCodeSettings.setUseDefaultApp(false);
qrCodeSettings.setMobileAppBundleId(mobileAppBundle.getId());
doPost("/api/mobile/qr/settings", qrCodeSettings)
.andExpect(status().isOk());
QrCodeSettings updatedQrCodeSettings = doGet("/api/mobile/qr/settings", QrCodeSettings.class);
assertThat(updatedQrCodeSettings.isUseDefaultApp()).isFalse();
}
@Test
public void testShouldNotSaveQrCodeSettingsWithoutRequiredConfig() throws Exception {
loginSysAdmin();
QrCodeSettings qrCodeSettings = doGet("/api/mobile/qr/settings", QrCodeSettings.class);
qrCodeSettings.setUseDefaultApp(false);
qrCodeSettings.setQrCodeConfig(null);
qrCodeSettings.setMobileAppBundleId(null);
doPost("/api/mobile/qr/settings", qrCodeSettings)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Validation error: qrCodeConfig must not be null")));
qrCodeSettings.setQrCodeConfig(QRCodeConfig.builder().showOnHomePage(false).build());
doPost("/api/mobile/qr/settings", qrCodeSettings)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Mobile app bundle is required to use custom application!")));
qrCodeSettings.setMobileAppBundleId(mobileAppBundle.getId());
doPost("/api/mobile/qr/settings", qrCodeSettings)
.andExpect(status().isOk());
}
@Test
public void testShouldSaveQrCodeSettingsForDefaultApp() throws Exception {
loginSysAdmin();
QrCodeSettings qrCodeSettings = doGet("/api/mobile/qr/settings", QrCodeSettings.class);
qrCodeSettings.setUseDefaultApp(true);
qrCodeSettings.setMobileAppBundleId(null);
doPost("/api/mobile/qr/settings", qrCodeSettings)
.andExpect(status().isOk());
}
@Test
public void testGetApplicationAssociations() throws Exception {
loginSysAdmin();
QrCodeSettings qrCodeSettings = doGet("/api/mobile/qr/settings", QrCodeSettings.class);
qrCodeSettings.setUseDefaultApp(true);
qrCodeSettings.setMobileAppBundleId(mobileAppBundle.getId());
doPost("/api/mobile/qr/settings", qrCodeSettings)
.andExpect(status().isOk());
JsonNode assetLinks = doGet("/.well-known/assetlinks.json", JsonNode.class);
assertThat(assetLinks.get(0).get("target").get("package_name").asText()).isEqualTo("my.android.package");
assertThat(assetLinks.get(0).get("target").get("sha256_cert_fingerprints").get(0).asText()).isEqualTo(ANDROID_APP_SHA256);
JsonNode appleAssociation = doGet("/.well-known/apple-app-site-association", JsonNode.class);
assertThat(appleAssociation.get("applinks").get("details").get(0).get("appID").asText()).isEqualTo(APPLE_APP_ID);
}
@Test
public void testGetMobileDeepLink() throws Exception {
loginSysAdmin();
String deepLink = doGet("/api/mobile/qr/deepLink", String.class);
Pattern expectedPattern = Pattern.compile("\"https://([^/]+)/api/noauth/qr\\?secret=([^&]+)&ttl=([^&]+)&host=([^&]+)\"");
Matcher parsedDeepLink = expectedPattern.matcher(deepLink);
assertThat(parsedDeepLink.matches()).isTrue();
String appHost = parsedDeepLink.group(1);
String secret = parsedDeepLink.group(2);
String ttl = parsedDeepLink.group(3);
assertThat(appHost).isEqualTo("demo.thingsboard.io");
assertThat(ttl).isEqualTo(String.valueOf(mobileSecretKeyTtl));
JwtPair jwtPair = doGet("/api/noauth/qr/" + secret, JwtPair.class);
assertThat(jwtPair).isNotNull();
loginTenantAdmin();
String tenantDeepLink = doGet("/api/mobile/qr/deepLink", String.class);
Matcher tenantParsedDeepLink = expectedPattern.matcher(tenantDeepLink);
assertThat(tenantParsedDeepLink.matches()).isTrue();
String tenantSecret = tenantParsedDeepLink.group(2);
JwtPair tenantJwtPair = doGet("/api/noauth/qr/" + tenantSecret, JwtPair.class);
assertThat(tenantJwtPair).isNotNull();
loginCustomerUser();
String customerDeepLink = doGet("/api/mobile/qr/deepLink", String.class);
Matcher customerParsedDeepLink = expectedPattern.matcher(customerDeepLink);
assertThat(customerParsedDeepLink.matches()).isTrue();
String customerSecret = customerParsedDeepLink.group(2);
JwtPair customerJwtPair = doGet("/api/noauth/qr/" + customerSecret, JwtPair.class);
assertThat(customerJwtPair).isNotNull();
// update mobile setting to use custom one
loginSysAdmin();
QrCodeSettings qrCodeSettings = doGet("/api/mobile/qr/settings", QrCodeSettings.class);
qrCodeSettings.setUseDefaultApp(false);
qrCodeSettings.setMobileAppBundleId(mobileAppBundle.getId());
doPost("/api/mobile/qr/settings", qrCodeSettings);
String customAppDeepLink = doGet("/api/mobile/qr/deepLink", String.class);
Pattern customAppExpectedPattern = Pattern.compile("\"https://([^/]+)/api/noauth/qr\\?secret=([^&]+)&ttl=([^&]+)\"");
Matcher customAppParsedDeepLink = customAppExpectedPattern.matcher(customAppDeepLink);
assertThat(customAppParsedDeepLink.matches()).isTrue();
assertThat(customAppParsedDeepLink.group(1)).isEqualTo("localhost");
loginTenantAdmin();
String tenantCustomAppDeepLink = doGet("/api/mobile/qr/deepLink", String.class);
Matcher tenantCustomAppParsedDeepLink = customAppExpectedPattern.matcher(tenantCustomAppDeepLink);
assertThat(tenantCustomAppParsedDeepLink.matches()).isTrue();
assertThat(tenantCustomAppParsedDeepLink.group(1)).isEqualTo("localhost");
loginCustomerUser();
String customerCustomAppDeepLink = doGet("/api/mobile/qr/deepLink", String.class);
Matcher customerCustomAppParsedDeepLink = customAppExpectedPattern.matcher(customerCustomAppDeepLink);
assertThat(customerCustomAppParsedDeepLink.matches()).isTrue();
assertThat(customerCustomAppParsedDeepLink.group(1)).isEqualTo("localhost");
}
private MobileApp validMobileApp(String mobileAppName, PlatformType platformType) {
MobileApp mobileApp = new MobileApp();
mobileApp.setTenantId(tenantId);
mobileApp.setStatus(MobileAppStatus.PUBLISHED);
mobileApp.setPkgName(mobileAppName);
mobileApp.setPlatformType(platformType);
mobileApp.setAppSecret(StringUtils.randomAlphanumeric(24));
StoreInfo storeInfo = StoreInfo.builder()
.storeLink("https://play.google/test")
.sha256CertFingerprints(ANDROID_APP_SHA256)
.appId("test.app.id").build();
mobileApp.setStoreInfo(storeInfo);
return mobileApp;
}
}

44
application/src/test/java/org/thingsboard/server/service/housekeeper/HousekeeperServiceTest.java

@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.ApiUsageState;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EventInfo;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import org.thingsboard.server.common.data.alarm.EntityAlarm;
@ -45,6 +46,9 @@ import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.MobileAppBundleId;
import org.thingsboard.server.common.data.id.MobileAppId;
import org.thingsboard.server.common.data.id.OAuth2ClientId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId;
@ -54,7 +58,12 @@ import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.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.msg.TbNodeConnectionType;
import org.thingsboard.server.common.data.oauth2.OAuth2Client;
import org.thingsboard.server.common.data.oauth2.PlatformType;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
@ -233,6 +242,27 @@ public class HousekeeperServiceTest extends AbstractControllerTest {
tenantId = differentTenantId;
createRelatedData(tenantId);
MobileApp androidApp = validMobileApp(TenantId.SYS_TENANT_ID, "my.android.package", PlatformType.ANDROID);
androidApp = doPost("/api/mobile/app", androidApp, MobileApp.class);
MobileAppId androidAppId = androidApp.getId();
MobileApp iosApp = validMobileApp(TenantId.SYS_TENANT_ID, "my.ios.package", PlatformType.IOS);
iosApp = doPost("/api/mobile/app", iosApp, MobileApp.class);
MobileAppId iosAppId = androidApp.getId();
OAuth2Client oAuth2Client = createOauth2Client(TenantId.SYS_TENANT_ID, "test google client");
OAuth2Client savedOAuth2Client = doPost("/api/oauth2/client", oAuth2Client, OAuth2Client.class);
OAuth2ClientId oAuth2ClientId = savedOAuth2Client.getId();
MobileAppBundle mobileAppBundle = new MobileAppBundle();
mobileAppBundle.setTitle("Test bundle");
mobileAppBundle.setAndroidAppId(androidApp.getId());
mobileAppBundle.setIosAppId(iosApp.getId());
MobileAppBundle savedAppBundle = doPost("/api/mobile/bundle?oauth2ClientIds=" + savedOAuth2Client.getId().getId(), mobileAppBundle, MobileAppBundle.class);
MobileAppBundleId appBundleId = savedAppBundle.getId();
createDifferentTenantCustomer();
createRelatedData(differentTenantCustomerId);
loginDifferentTenant();
@ -279,6 +309,10 @@ public class HousekeeperServiceTest extends AbstractControllerTest {
verifyNoRelatedData(userId);
verifyNoRelatedData(differentTenantCustomerId);
verifyNoRelatedData(tenantApiUsageState.getId());
verifyNoRelatedData(androidAppId);
verifyNoRelatedData(iosAppId);
verifyNoRelatedData(oAuth2ClientId);
verifyNoRelatedData(appBundleId);
verifyNoRelatedData(tenantId);
});
}
@ -483,4 +517,14 @@ public class HousekeeperServiceTest extends AbstractControllerTest {
return ruleChainService.loadRuleChainMetaData(tenantId, ruleChainId);
}
private MobileApp validMobileApp(TenantId tenantId, String mobileAppName, PlatformType platformType) {
MobileApp mobileApp = new MobileApp();
mobileApp.setTenantId(tenantId);
mobileApp.setStatus(MobileAppStatus.DRAFT);
mobileApp.setPkgName(mobileAppName);
mobileApp.setPlatformType(platformType);
mobileApp.setAppSecret(StringUtils.randomAlphanumeric(24));
return mobileApp;
}
}

46
common/dao-api/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleService.java

@ -0,0 +1,46 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.mobile;
import org.thingsboard.server.common.data.id.MobileAppBundleId;
import org.thingsboard.server.common.data.id.OAuth2ClientId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundle;
import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundleInfo;
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.dao.entity.EntityDaoService;
import java.util.List;
public interface MobileAppBundleService extends EntityDaoService {
MobileAppBundle saveMobileAppBundle(TenantId tenantId, MobileAppBundle mobileAppBundle);
void updateOauth2Clients(TenantId tenantId, MobileAppBundleId mobileAppBundleId, List<OAuth2ClientId> oAuth2ClientIds);
MobileAppBundle findMobileAppBundleById(TenantId tenantId, MobileAppBundleId mobileAppBundleId);
PageData<MobileAppBundleInfo> findMobileAppBundleInfosByTenantId(TenantId tenantId, PageLink pageLink);
MobileAppBundleInfo findMobileAppBundleInfoById(TenantId tenantId, MobileAppBundleId mobileAppBundleId);
MobileAppBundle findMobileAppBundleByPkgNameAndPlatform(TenantId tenantId, String pkgName, PlatformType platform);
void deleteMobileAppBundleById(TenantId tenantId, MobileAppBundleId mobileAppBundleId);
}

19
common/dao-api/src/main/java/org/thingsboard/server/dao/mobile/MobileAppService.java

@ -15,30 +15,27 @@
*/
package org.thingsboard.server.dao.mobile;
import org.thingsboard.server.common.data.id.MobileAppBundleId;
import org.thingsboard.server.common.data.id.MobileAppId;
import org.thingsboard.server.common.data.id.OAuth2ClientId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.MobileApp;
import org.thingsboard.server.common.data.mobile.MobileAppInfo;
import org.thingsboard.server.common.data.mobile.app.MobileApp;
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.dao.entity.EntityDaoService;
import java.util.List;
public interface MobileAppService extends EntityDaoService {
MobileApp saveMobileApp(TenantId tenantId, MobileApp mobileApp);
void deleteMobileAppById(TenantId tenantId, MobileAppId mobileAppId);
MobileApp findMobileAppById(TenantId tenantId, MobileAppId mobileAppId);
PageData<MobileAppInfo> findMobileAppInfosByTenantId(TenantId tenantId, PageLink pageLink);
PageData<MobileApp> findMobileAppsByTenantId(TenantId tenantId, PlatformType platformType, PageLink pageLink);
MobileAppInfo findMobileAppInfoById(TenantId tenantId, MobileAppId mobileAppId);
MobileApp findByBundleIdAndPlatformType(TenantId tenantId, MobileAppBundleId mobileAppBundleId, PlatformType platformType);
void updateOauth2Clients(TenantId tenantId, MobileAppId mobileAppId, List<OAuth2ClientId> oAuth2ClientIds);
MobileApp findMobileAppByPkgNameAndPlatformType(String pkgName, PlatformType platform);
void deleteMobileAppById(TenantId tenantId, MobileAppId mobileAppId);
void deleteMobileAppsByTenantId(TenantId tenantId);
}

2
common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java

@ -51,6 +51,6 @@ public class CacheConstants {
public static final String ENTITY_COUNT_CACHE = "entityCount";
public static final String RESOURCE_INFO_CACHE = "resourceInfo";
public static final String ALARM_TYPES_CACHE = "alarmTypes";
public static final String MOBILE_APP_SETTINGS_CACHE = "mobileAppSettings";
public static final String QR_CODE_SETTINGS_CACHE = "qrCodeSettings";
public static final String MOBILE_SECRET_KEY_CACHE = "mobileSecretKey";
}

3
common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java

@ -60,7 +60,8 @@ public enum EntityType {
QUEUE_STATS(34),
OAUTH2_CLIENT(35),
DOMAIN(36),
MOBILE_APP(37);
MOBILE_APP(37),
MOBILE_APP_BUNDLE(38);
@Getter
private final int protoNumber; // Corresponds to EntityTypeProto

21
common/data/src/main/java/org/thingsboard/server/common/data/Views.java

@ -0,0 +1,21 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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 class Views {
public static class Public {}
public static class Private extends Public {}
}

2
common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java

@ -111,6 +111,8 @@ public class EntityIdFactory {
return new MobileAppId(uuid);
case DOMAIN:
return new DomainId(uuid);
case MOBILE_APP_BUNDLE:
return new MobileAppBundleId(uuid);
}
throw new IllegalArgumentException("EntityType " + type + " is not supported!");
}

39
common/data/src/main/java/org/thingsboard/server/common/data/id/MobileAppBundleId.java

@ -0,0 +1,39 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.id;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.thingsboard.server.common.data.EntityType;
import java.util.UUID;
public class MobileAppBundleId extends UUIDBased implements EntityId{
@JsonCreator
public MobileAppBundleId(@JsonProperty("id") UUID id) {
super(id);
}
public static MobileAppBundleId fromString(String mobileAppId) {
return new MobileAppBundleId(UUID.fromString(mobileAppId));
}
@Override
public EntityType getEntityType() {
return EntityType.MOBILE_APP_BUNDLE;
}
}

8
common/data/src/main/java/org/thingsboard/server/common/data/id/MobileAppSettingsId.java → common/data/src/main/java/org/thingsboard/server/common/data/id/QrCodeSettingsId.java

@ -22,16 +22,16 @@ import io.swagger.v3.oas.annotations.media.Schema;
import java.util.UUID;
@Schema
public class MobileAppSettingsId extends UUIDBased {
public class QrCodeSettingsId extends UUIDBased {
private static final long serialVersionUID = 1L;
@JsonCreator
public MobileAppSettingsId(@JsonProperty("id") UUID id) {
public QrCodeSettingsId(@JsonProperty("id") UUID id) {
super(id);
}
public static MobileAppSettingsId fromString(String mobileAppSettingsId) {
return new MobileAppSettingsId(UUID.fromString(mobileAppSettingsId));
public static QrCodeSettingsId fromString(String qrCodeSettingsId) {
return new QrCodeSettingsId(UUID.fromString(qrCodeSettingsId));
}
}

24
common/data/src/main/java/org/thingsboard/server/common/data/mobile/LoginMobileInfo.java

@ -0,0 +1,24 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.mobile;
import org.thingsboard.server.common.data.mobile.app.MobileAppVersionInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientLoginInfo;
import java.util.List;
public record LoginMobileInfo(List<OAuth2ClientLoginInfo> oAuth2ClientLoginInfos, MobileAppVersionInfo versionInfo) {
}

11
common/data/src/main/java/org/thingsboard/server/common/data/mobile/UserMobileInfo.java

@ -15,13 +15,10 @@
*/
package org.thingsboard.server.common.data.mobile;
import lombok.Data;
import com.fasterxml.jackson.databind.JsonNode;
import org.thingsboard.server.common.data.HomeDashboardInfo;
import org.thingsboard.server.common.data.User;
import java.util.Map;
@Data
public class UserMobileInfo {
private Map<String, MobileSessionInfo> sessions;
public record UserMobileInfo(User user, HomeDashboardInfo homeDashboardInfo, JsonNode pages) {
}

27
common/data/src/main/java/org/thingsboard/server/common/data/mobile/UserMobileSessionInfo.java

@ -0,0 +1,27 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.mobile;
import lombok.Data;
import java.util.Map;
@Data
public class UserMobileSessionInfo {
private Map<String, MobileSessionInfo> sessions;
}

24
common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileApp.java → common/data/src/main/java/org/thingsboard/server/common/data/mobile/app/MobileApp.java

@ -13,12 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.mobile;
package org.thingsboard.server.common.data.mobile.app;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@ -27,6 +29,7 @@ import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.id.MobileAppId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.oauth2.PlatformType;
import org.thingsboard.server.common.data.validation.Length;
@EqualsAndHashCode(callSuper = true)
@ -44,8 +47,18 @@ public class MobileApp extends BaseData<MobileAppId> implements HasTenantId, Has
@NotEmpty
@Length(fieldName = "appSecret", min = 16, max = 2048, message = "must be at least 16 and max 2048 characters")
private String appSecret;
@Schema(description = "Whether OAuth2 settings are enabled or not")
private boolean oauth2Enabled;
@Schema(description = "Application platform type: ANDROID or IOS", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull
private PlatformType platformType;
@Schema(description = "Application status: PUBLISHED, DEPRECATED, SUSPENDED, DRAFT", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull
private MobileAppStatus status;
@Schema(description = "Application version info")
@Valid
private MobileAppVersionInfo versionInfo;
@Schema(description = "Application store information")
@Valid
private StoreInfo storeInfo;
public MobileApp() {
super();
@ -60,7 +73,10 @@ public class MobileApp extends BaseData<MobileAppId> implements HasTenantId, Has
this.tenantId = mobile.tenantId;
this.pkgName = mobile.pkgName;
this.appSecret = mobile.appSecret;
this.oauth2Enabled = mobile.oauth2Enabled;
this.platformType = mobile.platformType;
this.status = mobile.status;
this.versionInfo = mobile.versionInfo;
this.storeInfo = mobile.storeInfo;
}
@Override

25
common/data/src/main/java/org/thingsboard/server/common/data/mobile/app/MobileAppStatus.java

@ -0,0 +1,25 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.mobile.app;
public enum MobileAppStatus {
DRAFT,
PUBLISHED,
DEPRECATED,
SUSPENDED
}

49
common/data/src/main/java/org/thingsboard/server/common/data/mobile/app/MobileAppVersionInfo.java

@ -0,0 +1,49 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.mobile.app;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.validation.Length;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class MobileAppVersionInfo {
@Schema(description = "Minimum supported version")
@Length(fieldName = "minVersion", max = 20)
private String minVersion;
@Schema(description = "Release notes of minimum supported version")
@Length(fieldName = "minVersionReleaseNotes", max = 40000)
private String minVersionReleaseNotes;
@Schema(description = "Latest supported version")
@Length(fieldName = "latestVersion", max = 20)
private String latestVersion;
@Schema(description = "Release notes of latest supported version")
@Length(fieldName = "latestVersionReleaseNotes", max = 40000)
private String latestVersionReleaseNotes;
}

13
common/data/src/main/java/org/thingsboard/server/common/data/mobile/IosConfig.java → common/data/src/main/java/org/thingsboard/server/common/data/mobile/app/StoreInfo.java

@ -13,26 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.mobile;
package org.thingsboard.server.common.data.mobile.app;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.validation.NoXss;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class IosConfig {
public class StoreInfo {
private boolean enabled;
@NoXss
private String appId;
@NoXss
private String sha256CertFingerprints;
@NoXss
private String storeLink;
}

83
common/data/src/main/java/org/thingsboard/server/common/data/mobile/bundle/MobileAppBundle.java

@ -0,0 +1,83 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.mobile.bundle;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.id.MobileAppBundleId;
import org.thingsboard.server.common.data.id.MobileAppId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.layout.MobileLayoutConfig;
import org.thingsboard.server.common.data.validation.Length;
@EqualsAndHashCode(callSuper = true)
@Data
@ToString
public class MobileAppBundle extends BaseData<MobileAppBundleId> implements HasTenantId, HasName {
@Schema(description = "JSON object with Tenant Id")
private TenantId tenantId;
@Schema(description = "Application bundle title. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank
@Length(fieldName = "title")
private String title;
@Schema(description = "Application bundle description.")
@Length(fieldName = "description")
private String description;
@Schema(description = "Android application id")
private MobileAppId androidAppId;
@Schema(description = "IOS application id")
private MobileAppId iosAppId;
@Schema(description = "Application layout configuration")
@Valid
private MobileLayoutConfig layoutConfig;
@Schema(description = "Whether OAuth2 settings are enabled or not")
private Boolean oauth2Enabled;
public MobileAppBundle() {
super();
}
public MobileAppBundle(MobileAppBundleId id) {
super(id);
}
public MobileAppBundle(MobileAppBundle mobile) {
super(mobile);
this.tenantId = mobile.tenantId;
this.title = mobile.title;
this.description = mobile.description;
this.androidAppId = mobile.androidAppId;
this.iosAppId = mobile.iosAppId;
this.layoutConfig = mobile.layoutConfig;
this.oauth2Enabled = mobile.oauth2Enabled;
}
@Override
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@Schema(description = "Mobile app bundle title", example = "My main application", accessMode = Schema.AccessMode.READ_ONLY)
public String getName() {
return title;
}
}

63
common/data/src/main/java/org/thingsboard/server/common/data/mobile/bundle/MobileAppBundleInfo.java

@ -0,0 +1,63 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.mobile.bundle;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.id.MobileAppBundleId;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
@Schema
public class MobileAppBundleInfo extends MobileAppBundle {
@Schema(description = "Android package name")
private String androidPkgName;
@Schema(description = "IOS package name")
private String iosPkgName;
@Schema(description = "List of available oauth2 clients")
private List<OAuth2ClientInfo> oauth2ClientInfos;
@Schema(description = "Indicates if qr code is available for bundle")
private boolean qrCodeEnabled;
public MobileAppBundleInfo(MobileAppBundle mobileApp, String androidPkgName, String iosPkgName, boolean qrCodeEnabled) {
super(mobileApp);
this.androidPkgName = androidPkgName;
this.iosPkgName = iosPkgName;
this.qrCodeEnabled = qrCodeEnabled;
}
public MobileAppBundleInfo(MobileAppBundle mobileApp, String androidPkgName, String iosPkgName, boolean qrCodeEnabled, List<OAuth2ClientInfo> oauth2ClientInfos) {
super(mobileApp);
this.androidPkgName = androidPkgName;
this.iosPkgName = iosPkgName;
this.qrCodeEnabled = qrCodeEnabled;
this.oauth2ClientInfos = oauth2ClientInfos;
}
public MobileAppBundleInfo() {
super();
}
public MobileAppBundleInfo(MobileAppBundleId mobileAppBundleId) {
super(mobileAppBundleId);
}
}

8
common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppOauth2Client.java → common/data/src/main/java/org/thingsboard/server/common/data/mobile/bundle/MobileAppBundleOauth2Client.java

@ -13,20 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.mobile;
package org.thingsboard.server.common.data.mobile.bundle;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.id.MobileAppId;
import org.thingsboard.server.common.data.id.MobileAppBundleId;
import org.thingsboard.server.common.data.id.OAuth2ClientId;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MobileAppOauth2Client {
public class MobileAppBundleOauth2Client {
private MobileAppId mobileAppId;
private MobileAppBundleId mobileAppBundleId;
private OAuth2ClientId oAuth2ClientId;
}

35
common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/AbstractMobilePage.java

@ -0,0 +1,35 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.mobile.layout;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.thingsboard.server.common.data.Views;
@Data
public abstract class AbstractMobilePage implements MobilePage {
@Schema(description = "Page label", example = "Air quality")
@JsonView(Views.Public.class)
protected String label;
@Schema(description = "Indicates if page is visible", example = "true", requiredMode = Schema.RequiredMode.REQUIRED)
@JsonView(Views.Private.class)
protected boolean visible;
@Schema(description = "URL of the page icon", example = "home_icon")
@JsonView(Views.Public.class)
protected String icon;
}

42
common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/CustomMobilePage.java

@ -0,0 +1,42 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.mobile.layout;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.Views;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class CustomMobilePage extends AbstractMobilePage {
@Schema(description = "Path to custom page", example = "/alarmDetails/868c7083-032d-4f52-b8b4-7859aebb6a4e")
@JsonView(Views.Public.class)
private String path;
@Override
public MobilePageType getType() {
return MobilePageType.CUSTOM;
}
}

42
common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/DashboardPage.java

@ -0,0 +1,42 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.mobile.layout;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.Views;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class DashboardPage extends AbstractMobilePage {
@Schema(description = "Dashboard id", example = "784f394c-42b6-435a-983c-b7beff2784f9")
@JsonView(Views.Public.class)
private String dashboardId;
@Override
public MobilePageType getType() {
return MobilePageType.DASHBOARD;
}
}

43
common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/DefaultMobilePage.java

@ -0,0 +1,43 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.mobile.layout;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.Views;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class DefaultMobilePage extends AbstractMobilePage {
@Schema(description = "Identifier for default page", example = "HOME")
@JsonView(Views.Public.class)
private DefaultPageId id;
@Override
public MobilePageType getType() {
return MobilePageType.DEFAULT;
}
}

29
common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/DefaultPageId.java

@ -0,0 +1,29 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.mobile.layout;
public enum DefaultPageId {
HOME,
ALARMS,
DEVICES,
CUSTOMERS,
ASSETS,
AUDIT_LOGS,
NOTIFICATIONS,
DEVICE_LIST,
DASHBOARDS
}

23
common/data/src/main/java/org/thingsboard/server/common/data/mobile/AndroidConfig.java → common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/MobileLayoutConfig.java

@ -13,28 +13,31 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.mobile;
package org.thingsboard.server.common.data.mobile.layout;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.validation.NoXss;
import org.thingsboard.server.common.data.Views;
import java.util.ArrayList;
import java.util.List;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class AndroidConfig {
public class MobileLayoutConfig {
private boolean enabled;
@NoXss
private String appPackage;
@NoXss
private String sha256CertFingerprints;
@NoXss
private String storeLink;
@Schema(description = "List of pages")
@JsonView(Views.Public.class)
@Valid
private List<MobilePage> pages = new ArrayList<>();
}

45
common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/MobilePage.java

@ -0,0 +1,45 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.mobile.layout;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonView;
import org.thingsboard.server.common.data.Views;
import java.io.Serializable;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXISTING_PROPERTY,
property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = DefaultMobilePage.class, name = "DEFAULT"),
@JsonSubTypes.Type(value = DashboardPage.class, name = "DASHBOARD"),
@JsonSubTypes.Type(value = WebViewPage.class, name = "WEB_VIEW"),
@JsonSubTypes.Type(value = CustomMobilePage.class, name = "CUSTOM")
})
public interface MobilePage extends Serializable {
@JsonView(Views.Private.class)
MobilePageType getType();
@JsonView(Views.Private.class)
boolean isVisible();
}

24
common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/MobilePageType.java

@ -0,0 +1,24 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.mobile.layout;
public enum MobilePageType {
DEFAULT,
DASHBOARD,
WEB_VIEW,
CUSTOM
}

42
common/data/src/main/java/org/thingsboard/server/common/data/mobile/layout/WebViewPage.java

@ -0,0 +1,42 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.mobile.layout;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.Views;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class WebViewPage extends AbstractMobilePage {
@Schema(description = "Url", example = "/url")
@JsonView(Views.Public.class)
private String url;
@Override
public MobilePageType getType() {
return MobilePageType.WEB_VIEW;
}
}

2
common/data/src/main/java/org/thingsboard/server/common/data/mobile/BadgePosition.java → common/data/src/main/java/org/thingsboard/server/common/data/mobile/qrCodeSettings/BadgePosition.java

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.mobile;
package org.thingsboard.server.common.data.mobile.qrCodeSettings;
public enum BadgePosition {

3
common/data/src/main/java/org/thingsboard/server/common/data/mobile/QRCodeConfig.java → common/data/src/main/java/org/thingsboard/server/common/data/mobile/qrCodeSettings/QRCodeConfig.java

@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.mobile;
package org.thingsboard.server.common.data.mobile.qrCodeSettings;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.thingsboard.server.common.data.mobile.qrCodeSettings.BadgePosition;
import org.thingsboard.server.common.data.validation.NoXss;
@Data

37
common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppSettings.java → common/data/src/main/java/org/thingsboard/server/common/data/mobile/qrCodeSettings/QrCodeSettings.java

@ -13,49 +13,52 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.mobile;
package org.thingsboard.server.common.data.mobile.qrCodeSettings;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
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.MobileAppSettingsId;
import org.thingsboard.server.common.data.id.MobileAppBundleId;
import org.thingsboard.server.common.data.id.QrCodeSettingsId;
import org.thingsboard.server.common.data.id.TenantId;
@Schema
@Data
@EqualsAndHashCode(callSuper = true)
public class MobileAppSettings extends BaseData<MobileAppSettingsId> implements HasTenantId {
public class QrCodeSettings extends BaseData<QrCodeSettingsId> implements HasTenantId {
private static final long serialVersionUID = 2628323657987010348L;
@Schema(description = "JSON object with Tenant Id.", accessMode = Schema.AccessMode.READ_ONLY)
private TenantId tenantId;
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Type of application: true means use default Thingsboard app", example = "true")
@Schema(description = "Use settings from system level", example = "true")
private boolean useSystemSettings;
@Schema(description = "Type of application: true means use default Thingsboard app", example = "true")
private boolean useDefaultApp;
@Valid
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Android mobile app configuration.")
private AndroidConfig androidConfig;
@Valid
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "Ios mobile app configuration.")
private IosConfig iosConfig;
@Valid
@Schema(description = "Mobile app bundle.")
private MobileAppBundleId mobileAppBundleId;
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, description = "QR code config configuration.")
@Valid
@NotNull
private QRCodeConfig qrCodeConfig;
@Schema(description = "Indicates if google play link is available", example = "true")
private boolean androidEnabled;
@Schema(description = "Indicates if apple store link is available", example = "true")
private boolean iosEnabled;
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private String defaultGooglePlayLink;
private String googlePlayLink;
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private String defaultAppStoreLink;
private String appStoreLink;
public MobileAppSettings() {
public QrCodeSettings() {
}
public MobileAppSettings(MobileAppSettingsId id) {
public QrCodeSettings(QrCodeSettingsId id) {
super(id);
}

1
common/proto/src/main/proto/queue.proto

@ -58,6 +58,7 @@ enum EntityTypeProto {
OAUTH2_CLIENT = 35;
DOMAIN = 36;
MOBILE_APP = 37;
MOBILE_APP_BUNDLE = 38;
}
/**

13
common/util/src/main/java/org/thingsboard/common/util/JacksonUtil.java

@ -39,6 +39,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.kv.DataType;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.Views;
import java.io.File;
import java.io.IOException;
@ -181,6 +182,10 @@ public class JacksonUtil {
}
}
public static String writeValueAsViewIgnoringNullFields(Object value, Class<Views.Public> serializationView) throws JsonProcessingException {
return value == null ? "" : OBJECT_MAPPER.writerWithView(serializationView).writeValueAsString(value);
}
public static String toPrettyString(Object o) {
try {
return PRETTY_SORTED_JSON_MAPPER.writeValueAsString(o);
@ -234,6 +239,14 @@ public class JacksonUtil {
}
}
public static <T> T readValue(String object, TypeReference<T> clazz) {
try {
return OBJECT_MAPPER.readValue(object, clazz);
} catch (IOException e) {
throw new IllegalArgumentException("Can't read object: " + object, e);
}
}
public static <T> T readValue(File file, TypeReference<T> clazz) {
try {
return OBJECT_MAPPER.readValue(file, clazz);

120
dao/src/main/java/org/thingsboard/server/dao/mobile/BaseMobileAppSettingsService.java

@ -1,120 +0,0 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.mobile;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.event.TransactionalEventListener;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.AndroidConfig;
import org.thingsboard.server.common.data.mobile.BadgePosition;
import org.thingsboard.server.common.data.mobile.IosConfig;
import org.thingsboard.server.common.data.mobile.MobileAppSettings;
import org.thingsboard.server.common.data.mobile.QRCodeConfig;
import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
import org.thingsboard.server.dao.service.DataValidator;
import java.util.Map;
import static org.thingsboard.server.dao.service.Validator.validateId;
@Service
@Slf4j
@RequiredArgsConstructor
public class BaseMobileAppSettingsService extends AbstractCachedEntityService<TenantId, MobileAppSettings, MobileAppSettingsEvictEvent> implements MobileAppSettingsService {
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
private static final String DEFAULT_QR_CODE_LABEL = "Scan to connect or download mobile app";
@Value("${mobileApp.googlePlayLink:https://play.google.com/store/apps/details?id=org.thingsboard.demo.app}")
private String googlePlayLink;
@Value("${mobileApp.appStoreLink:https://apps.apple.com/us/app/thingsboard-live/id1594355695}")
private String appStoreLink;
private final MobileAppSettingsDao mobileAppSettingsDao;
private final DataValidator<MobileAppSettings> mobileAppSettingsDataValidator;
@Override
public MobileAppSettings saveMobileAppSettings(TenantId tenantId, MobileAppSettings mobileAppSettings) {
mobileAppSettingsDataValidator.validate(mobileAppSettings, s -> tenantId);
try {
MobileAppSettings savedMobileAppSettings = mobileAppSettingsDao.save(tenantId, mobileAppSettings);
publishEvictEvent(new MobileAppSettingsEvictEvent(tenantId));
return constructMobileAppSettings(savedMobileAppSettings);
} catch (Exception e) {
handleEvictEvent(new MobileAppSettingsEvictEvent(tenantId));
checkConstraintViolation(e, Map.of(
"mobile_app_settings_tenant_id_unq_key", "Mobile application for specified tenant already exists!"
));
throw e;
}
}
@Override
public MobileAppSettings getMobileAppSettings(TenantId tenantId) {
log.trace("Executing getMobileAppSettings for tenant [{}] ", tenantId);
MobileAppSettings mobileAppSettings = cache.getAndPutInTransaction(tenantId,
() -> mobileAppSettingsDao.findByTenantId(tenantId), true);
return constructMobileAppSettings(mobileAppSettings);
}
@Override
public void deleteByTenantId(TenantId tenantId) {
log.trace("Executing deleteByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
mobileAppSettingsDao.removeByTenantId(tenantId);
}
@TransactionalEventListener(classes = MobileAppSettingsEvictEvent.class)
@Override
public void handleEvictEvent(MobileAppSettingsEvictEvent event) {
cache.evict(event.getTenantId());
}
private MobileAppSettings constructMobileAppSettings(MobileAppSettings mobileAppSettings) {
if (mobileAppSettings == null) {
mobileAppSettings = new MobileAppSettings();
mobileAppSettings.setUseDefaultApp(true);
AndroidConfig androidConfig = AndroidConfig.builder()
.enabled(true)
.build();
IosConfig iosConfig = IosConfig.builder()
.enabled(true)
.build();
QRCodeConfig qrCodeConfig = QRCodeConfig.builder()
.showOnHomePage(true)
.qrCodeLabelEnabled(true)
.qrCodeLabel(DEFAULT_QR_CODE_LABEL)
.badgeEnabled(true)
.badgePosition(BadgePosition.RIGHT)
.badgeEnabled(true)
.build();
mobileAppSettings.setQrCodeConfig(qrCodeConfig);
mobileAppSettings.setAndroidConfig(androidConfig);
mobileAppSettings.setIosConfig(iosConfig);
}
if (mobileAppSettings.isUseDefaultApp()) {
mobileAppSettings.setDefaultGooglePlayLink(googlePlayLink);
mobileAppSettings.setDefaultAppStoreLink(appStoreLink);
}
return mobileAppSettings;
}
}

46
dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleDao.java

@ -0,0 +1,46 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.mobile;
import org.thingsboard.server.common.data.id.MobileAppBundleId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundle;
import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundleInfo;
import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundleOauth2Client;
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.dao.Dao;
import java.util.List;
public interface MobileAppBundleDao extends Dao<MobileAppBundle> {
PageData<MobileAppBundleInfo> findInfosByTenantId(TenantId tenantId, PageLink pageLink);
MobileAppBundleInfo findInfoById(TenantId tenantId, MobileAppBundleId mobileAppBundleId);
List<MobileAppBundleOauth2Client> findOauth2ClientsByMobileAppBundleId(TenantId tenantId, MobileAppBundleId mobileAppBundleId);
void addOauth2Client(TenantId tenantId, MobileAppBundleOauth2Client mobileAppBundleOauth2Client);
void removeOauth2Client(TenantId tenantId, MobileAppBundleOauth2Client mobileAppBundleOauth2Client);
MobileAppBundle findByPkgNameAndPlatform(TenantId tenantId, String pkgName, PlatformType platform);
void deleteByTenantId(TenantId tenantId);
}

170
dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppBundleServiceImpl.java

@ -0,0 +1,170 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.mobile;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.MobileAppBundleId;
import org.thingsboard.server.common.data.id.OAuth2ClientId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundle;
import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundleInfo;
import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundleOauth2Client;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
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.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
import org.thingsboard.server.dao.oauth2.OAuth2ClientDao;
import org.thingsboard.server.dao.service.DataValidator;
import java.util.Comparator;
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.server.dao.service.Validator.checkNotNull;
@Slf4j
@Service
public class MobileAppBundleServiceImpl extends AbstractEntityService implements MobileAppBundleService {
private static final String PLATFORM_TYPE_IS_REQUIRED = "Platform type is required if package name is specified";
@Autowired
private OAuth2ClientDao oauth2ClientDao;
@Autowired
private MobileAppBundleDao mobileAppBundleDao;
@Autowired
private DataValidator<MobileAppBundle> mobileAppBundleDataValidator;
@Override
public MobileAppBundle saveMobileAppBundle(TenantId tenantId, MobileAppBundle mobileAppBundle) {
log.trace("Executing saveMobileAppBundle [{}]", mobileAppBundle);
mobileAppBundleDataValidator.validate(mobileAppBundle, b -> tenantId);
try {
MobileAppBundle savedMobileApp = mobileAppBundleDao.save(tenantId, mobileAppBundle);
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId).entity(savedMobileApp).build());
return savedMobileApp;
} catch (Exception e) {
checkConstraintViolation(e,
Map.of("android_app_id_unq_key", "Android mobile app already exists in another bundle!",
"ios_app_id_unq_key", "IOS mobile app already exists in another bundle!"));
throw e;
}
}
@Override
public void updateOauth2Clients(TenantId tenantId, MobileAppBundleId mobileAppBundleId, List<OAuth2ClientId> oAuth2ClientIds) {
log.trace("Executing updateOauth2Clients, mobileAppId [{}], oAuth2ClientIds [{}]", mobileAppBundleId, oAuth2ClientIds);
Set<MobileAppBundleOauth2Client> newClientList = oAuth2ClientIds.stream()
.map(clientId -> new MobileAppBundleOauth2Client(mobileAppBundleId, clientId))
.collect(Collectors.toSet());
List<MobileAppBundleOauth2Client> existingClients = mobileAppBundleDao.findOauth2ClientsByMobileAppBundleId(tenantId, mobileAppBundleId);
List<MobileAppBundleOauth2Client> toRemoveList = existingClients.stream()
.filter(client -> !newClientList.contains(client))
.toList();
newClientList.removeIf(existingClients::contains);
for (MobileAppBundleOauth2Client client : toRemoveList) {
mobileAppBundleDao.removeOauth2Client(tenantId, client);
}
for (MobileAppBundleOauth2Client client : newClientList) {
mobileAppBundleDao.addOauth2Client(tenantId, client);
}
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId)
.entityId(mobileAppBundleId).created(false).build());
}
@Override
public MobileAppBundle findMobileAppBundleById(TenantId tenantId, MobileAppBundleId mobileAppBundleId) {
log.trace("Executing findMobileAppBundleById [{}] [{}]", tenantId, mobileAppBundleId);
return mobileAppBundleDao.findById(tenantId, mobileAppBundleId.getId());
}
@Override
public PageData<MobileAppBundleInfo> findMobileAppBundleInfosByTenantId(TenantId tenantId, PageLink pageLink) {
log.trace("Executing findMobileAppBundleInfosByTenantId [{}]", tenantId);
PageData<MobileAppBundleInfo> mobileBundles = mobileAppBundleDao.findInfosByTenantId(tenantId, pageLink);
mobileBundles.getData().forEach(this::fetchOauth2Clients);
return mobileBundles;
}
@Override
public MobileAppBundleInfo findMobileAppBundleInfoById(TenantId tenantId, MobileAppBundleId mobileAppIdBundle) {
log.trace("Executing findMobileAppBundleInfoById [{}] [{}]", tenantId, mobileAppIdBundle);
MobileAppBundleInfo mobileAppBundleInfo = mobileAppBundleDao.findInfoById(tenantId, mobileAppIdBundle);
if (mobileAppBundleInfo != null) {
fetchOauth2Clients(mobileAppBundleInfo);
}
return mobileAppBundleInfo;
}
@Override
public MobileAppBundle findMobileAppBundleByPkgNameAndPlatform(TenantId tenantId, String pkgName, PlatformType platform) {
log.trace("Executing findMobileAppBundleByPkgNameAndPlatform, tenantId [{}], pkgName [{}], platform [{}]", tenantId, pkgName, platform);
checkNotNull(platform, PLATFORM_TYPE_IS_REQUIRED);
return mobileAppBundleDao.findByPkgNameAndPlatform(tenantId, pkgName, platform);
}
@Override
public void deleteMobileAppBundleById(TenantId tenantId, MobileAppBundleId mobileAppBundleId) {
log.trace("Executing deleteMobileAppBundleById [{}]", mobileAppBundleId.getId());
mobileAppBundleDao.removeById(tenantId, mobileAppBundleId.getId());
eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(mobileAppBundleId).build());
}
@Override
public void deleteByTenantId(TenantId tenantId) {
log.trace("Executing deleteMobileAppsByTenantId, tenantId [{}]", tenantId);
mobileAppBundleDao.deleteByTenantId(tenantId);
}
@Override
public Optional<HasId<?>> findEntity(TenantId tenantId, EntityId entityId) {
return Optional.ofNullable(findMobileAppBundleById(tenantId, new MobileAppBundleId(entityId.getId())));
}
@Override
@Transactional
public void deleteEntity(TenantId tenantId, EntityId id, boolean force) {
deleteMobileAppBundleById(tenantId, (MobileAppBundleId) id);
}
@Override
public EntityType getEntityType() {
return EntityType.MOBILE_APP_BUNDLE;
}
private void fetchOauth2Clients(MobileAppBundleInfo mobileAppBundleInfo) {
List<OAuth2ClientInfo> clients = oauth2ClientDao.findByMobileAppBundleId(mobileAppBundleInfo.getUuidId()).stream()
.map(OAuth2ClientInfo::new)
.sorted(Comparator.comparing(OAuth2ClientInfo::getTitle))
.collect(Collectors.toList());
mobileAppBundleInfo.setOauth2ClientInfos(clients);
}
}

19
dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppDao.java

@ -15,26 +15,21 @@
*/
package org.thingsboard.server.dao.mobile;
import org.thingsboard.server.common.data.id.MobileAppId;
import org.thingsboard.server.common.data.id.OAuth2ClientId;
import org.thingsboard.server.common.data.id.MobileAppBundleId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.MobileApp;
import org.thingsboard.server.common.data.mobile.MobileAppOauth2Client;
import org.thingsboard.server.common.data.mobile.app.MobileApp;
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.dao.Dao;
import java.util.List;
public interface MobileAppDao extends Dao<MobileApp> {
PageData<MobileApp> findByTenantId(TenantId tenantId, PageLink pageLink);
List<MobileAppOauth2Client> findOauth2ClientsByMobileAppId(TenantId tenantId, MobileAppId mobileAppId);
MobileApp findByBundleIdAndPlatformType(TenantId tenantId, MobileAppBundleId mobileAppBundleId, PlatformType platformType);
void addOauth2Client(MobileAppOauth2Client mobileAppOauth2Client);
void removeOauth2Client(MobileAppOauth2Client mobileAppOauth2Client);
PageData<MobileApp> findByTenantId(TenantId tenantId, PlatformType platformType, PageLink pageLink);
void deleteByTenantId(TenantId tenantId);
MobileApp findByPkgNameAndPlatformType(TenantId tenantId, String pkgName, PlatformType platform);
}

96
dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppServiceImpl.java

@ -22,46 +22,45 @@ import org.springframework.transaction.annotation.Transactional;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.MobileAppBundleId;
import org.thingsboard.server.common.data.id.MobileAppId;
import org.thingsboard.server.common.data.id.OAuth2ClientId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.MobileApp;
import org.thingsboard.server.common.data.mobile.MobileAppInfo;
import org.thingsboard.server.common.data.mobile.MobileAppOauth2Client;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
import org.thingsboard.server.common.data.mobile.app.MobileApp;
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.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
import org.thingsboard.server.dao.oauth2.OAuth2ClientDao;
import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.Validator;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@Slf4j
@Service
public class MobileAppServiceImpl extends AbstractEntityService implements MobileAppService {
@Autowired
private OAuth2ClientDao oauth2ClientDao;
private static final String PLATFORM_TYPE_IS_REQUIRED = "Platform type is required if package name is specified";
private static final String MOBILE_APP_BUNDLE_CONSTRAINT = "The mobile app referenced by the mobile bundle cannot be deleted!";
@Autowired
private MobileAppDao mobileAppDao;
@Autowired
private DataValidator<MobileApp> mobileAppDataValidator;
@Override
public MobileApp saveMobileApp(TenantId tenantId, MobileApp mobileApp) {
log.trace("Executing saveMobileApp [{}]", mobileApp);
mobileAppDataValidator.validate(mobileApp, a -> tenantId);
try {
MobileApp savedMobileApp = mobileAppDao.save(tenantId, mobileApp);
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId).entity(savedMobileApp).build());
return savedMobileApp;
} catch (Exception e) {
checkConstraintViolation(e,
Map.of("mobile_app_unq_key", "Mobile app with such package already exists!"));
Map.of("mobile_app_pkg_name_platform_unq_key", "Mobile app with such package name and platform already exists!"));
throw e;
}
}
@ -69,8 +68,15 @@ public class MobileAppServiceImpl extends AbstractEntityService implements Mobil
@Override
public void deleteMobileAppById(TenantId tenantId, MobileAppId mobileAppId) {
log.trace("Executing deleteMobileAppById [{}]", mobileAppId.getId());
mobileAppDao.removeById(tenantId, mobileAppId.getId());
eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(mobileAppId).build());
try {
mobileAppDao.removeById(tenantId, mobileAppId.getId());
eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(mobileAppId).build());
} catch (Exception e) {
checkConstraintViolation(e,
Map.of("fk_android_app_id", MOBILE_APP_BUNDLE_CONSTRAINT,
"fk_ios_app_id", MOBILE_APP_BUNDLE_CONSTRAINT));
throw e;
}
}
@Override
@ -80,43 +86,9 @@ public class MobileAppServiceImpl extends AbstractEntityService implements Mobil
}
@Override
public PageData<MobileAppInfo> findMobileAppInfosByTenantId(TenantId tenantId, PageLink pageLink) {
public PageData<MobileApp> findMobileAppsByTenantId(TenantId tenantId, PlatformType platformType, PageLink pageLink) {
log.trace("Executing findMobileAppInfosByTenantId [{}]", tenantId);
PageData<MobileApp> mobiles = mobileAppDao.findByTenantId(tenantId, pageLink);
return mobiles.mapData(this::getMobileAppInfo);
}
@Override
public MobileAppInfo findMobileAppInfoById(TenantId tenantId, MobileAppId mobileAppId) {
log.trace("Executing findMobileAppInfoById [{}] [{}]", tenantId, mobileAppId);
MobileApp mobileApp = mobileAppDao.findById(tenantId, mobileAppId.getId());
if (mobileApp == null) {
return null;
}
return getMobileAppInfo(mobileApp);
}
@Override
public void updateOauth2Clients(TenantId tenantId, MobileAppId mobileAppId, List<OAuth2ClientId> oAuth2ClientIds) {
log.trace("Executing updateOauth2Clients, mobileAppId [{}], oAuth2ClientIds [{}]", mobileAppId, oAuth2ClientIds);
Set<MobileAppOauth2Client> newClientList = oAuth2ClientIds.stream()
.map(clientId -> new MobileAppOauth2Client(mobileAppId, clientId))
.collect(Collectors.toSet());
List<MobileAppOauth2Client> existingClients = mobileAppDao.findOauth2ClientsByMobileAppId(tenantId, mobileAppId);
List<MobileAppOauth2Client> toRemoveList = existingClients.stream()
.filter(client -> !newClientList.contains(client))
.toList();
newClientList.removeIf(existingClients::contains);
for (MobileAppOauth2Client client : toRemoveList) {
mobileAppDao.removeOauth2Client(client);
}
for (MobileAppOauth2Client client : newClientList) {
mobileAppDao.addOauth2Client(client);
}
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId)
.entityId(mobileAppId).created(false).build());
return mobileAppDao.findByTenantId(tenantId, platformType, pageLink);
}
@Override
@ -131,22 +103,22 @@ public class MobileAppServiceImpl extends AbstractEntityService implements Mobil
}
@Override
public void deleteMobileAppsByTenantId(TenantId tenantId) {
log.trace("Executing deleteMobileAppsByTenantId, tenantId [{}]", tenantId);
mobileAppDao.deleteByTenantId(tenantId);
public MobileApp findByBundleIdAndPlatformType(TenantId tenantId, MobileAppBundleId mobileAppBundleId, PlatformType platformType) {
log.trace("Executing findAndroidQrConfig, tenantId [{}], mobileAppBundleId [{}]", tenantId, mobileAppBundleId);
return mobileAppDao.findByBundleIdAndPlatformType(tenantId, mobileAppBundleId, platformType);
}
@Override
public void deleteByTenantId(TenantId tenantId) {
deleteMobileAppsByTenantId(tenantId);
public MobileApp findMobileAppByPkgNameAndPlatformType(String pkgName, PlatformType platformType) {
log.trace("Executing findMobileAppByPkgNameAndPlatformType, pkgName [{}], platform [{}]", pkgName, platformType);
Validator.checkNotNull(platformType, PLATFORM_TYPE_IS_REQUIRED);
return mobileAppDao.findByPkgNameAndPlatformType(TenantId.SYS_TENANT_ID, pkgName, platformType);
}
private MobileAppInfo getMobileAppInfo(MobileApp mobileApp) {
List<OAuth2ClientInfo> clients = oauth2ClientDao.findByMobileAppId(mobileApp.getUuidId()).stream()
.map(OAuth2ClientInfo::new)
.sorted(Comparator.comparing(OAuth2ClientInfo::getTitle))
.collect(Collectors.toList());
return new MobileAppInfo(mobileApp, clients);
@Override
public void deleteByTenantId(TenantId tenantId) {
log.trace("Executing deleteByTenantId, tenantId [{}]", tenantId);
mobileAppDao.deleteByTenantId(tenantId);
}
@Override

12
dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppSettingsService.java → dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingService.java

@ -16,13 +16,17 @@
package org.thingsboard.server.dao.mobile;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.MobileAppSettings;
import org.thingsboard.server.common.data.mobile.app.MobileApp;
import org.thingsboard.server.common.data.mobile.qrCodeSettings.QrCodeSettings;
import org.thingsboard.server.common.data.oauth2.PlatformType;
public interface MobileAppSettingsService {
public interface QrCodeSettingService {
MobileAppSettings saveMobileAppSettings(TenantId tenantId, MobileAppSettings settings);
QrCodeSettings saveQrCodeSettings(TenantId tenantId, QrCodeSettings qrCodeSettings);
MobileAppSettings getMobileAppSettings(TenantId tenantId);
QrCodeSettings findQrCodeSettings(TenantId tenantId);
MobileApp findAppFromQrCodeSettings(TenantId sysTenantId, PlatformType platformType);
void deleteByTenantId(TenantId tenantId);

134
dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingServiceImpl.java

@ -0,0 +1,134 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.mobile;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.event.TransactionalEventListener;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.qrCodeSettings.BadgePosition;
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.qrCodeSettings.QRCodeConfig;
import org.thingsboard.server.common.data.oauth2.PlatformType;
import org.thingsboard.server.dao.entity.AbstractCachedEntityService;
import org.thingsboard.server.dao.service.DataValidator;
import java.util.Map;
import static org.thingsboard.server.common.data.oauth2.PlatformType.ANDROID;
import static org.thingsboard.server.common.data.oauth2.PlatformType.IOS;
import static org.thingsboard.server.dao.service.Validator.validateId;
@Service
@Slf4j
@RequiredArgsConstructor
public class QrCodeSettingServiceImpl extends AbstractCachedEntityService<TenantId, QrCodeSettings, QrCodeSettingsEvictEvent> implements QrCodeSettingService {
public static final String INCORRECT_TENANT_ID = "Incorrect tenantId ";
private static final String DEFAULT_QR_CODE_LABEL = "Scan to connect or download mobile app";
@Value("${mobileApp.googlePlayLink:https://play.google.com/store/apps/details?id=org.thingsboard.demo.app}")
private String googlePlayLink;
@Value("${mobileApp.appStoreLink:https://apps.apple.com/us/app/thingsboard-live/id1594355695}")
private String appStoreLink;
private final QrCodeSettingsDao qrCodeSettingsDao;
private final MobileAppService mobileAppService;
private final DataValidator<QrCodeSettings> mobileAppSettingsDataValidator;
@Override
public QrCodeSettings saveQrCodeSettings(TenantId tenantId, QrCodeSettings qrCodeSettings) {
mobileAppSettingsDataValidator.validate(qrCodeSettings, s -> tenantId);
try {
QrCodeSettings savedQrCodeSettings = qrCodeSettingsDao.save(tenantId, qrCodeSettings);
publishEvictEvent(new QrCodeSettingsEvictEvent(tenantId));
return constructMobileAppSettings(savedQrCodeSettings);
} catch (Exception e) {
handleEvictEvent(new QrCodeSettingsEvictEvent(tenantId));
checkConstraintViolation(e, Map.of(
"qr_code_settings_tenant_id_unq_key", "Mobile application for specified tenant already exists!"
));
throw e;
}
}
@Override
public QrCodeSettings findQrCodeSettings(TenantId tenantId) {
log.trace("Executing getMobileAppSettings for tenant [{}] ", tenantId);
QrCodeSettings qrCodeSettings = cache.getAndPutInTransaction(tenantId,
() -> qrCodeSettingsDao.findByTenantId(tenantId), true);
return constructMobileAppSettings(qrCodeSettings);
}
@Override
public MobileApp findAppFromQrCodeSettings(TenantId tenantId, PlatformType platformType) {
log.trace("Executing findAppQrCodeConfig for tenant [{}] ", tenantId);
QrCodeSettings qrCodeSettings = findQrCodeSettings(tenantId);
return qrCodeSettings.getMobileAppBundleId() != null ? mobileAppService.findByBundleIdAndPlatformType(tenantId, qrCodeSettings.getMobileAppBundleId(), platformType) : null;
}
@Override
public void deleteByTenantId(TenantId tenantId) {
log.trace("Executing deleteByTenantId, tenantId [{}]", tenantId);
validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
qrCodeSettingsDao.removeByTenantId(tenantId);
}
@TransactionalEventListener(classes = QrCodeSettingsEvictEvent.class)
@Override
public void handleEvictEvent(QrCodeSettingsEvictEvent event) {
cache.evict(event.getTenantId());
}
private QrCodeSettings constructMobileAppSettings(QrCodeSettings qrCodeSettings) {
if (qrCodeSettings == null) {
qrCodeSettings = new QrCodeSettings();
qrCodeSettings.setUseDefaultApp(true);
qrCodeSettings.setAndroidEnabled(true);
qrCodeSettings.setIosEnabled(true);
QRCodeConfig qrCodeConfig = QRCodeConfig.builder()
.showOnHomePage(true)
.qrCodeLabelEnabled(true)
.qrCodeLabel(DEFAULT_QR_CODE_LABEL)
.badgeEnabled(true)
.badgePosition(BadgePosition.RIGHT)
.badgeEnabled(true)
.build();
qrCodeSettings.setQrCodeConfig(qrCodeConfig);
qrCodeSettings.setMobileAppBundleId(qrCodeSettings.getMobileAppBundleId());
}
if (qrCodeSettings.isUseDefaultApp()) {
qrCodeSettings.setGooglePlayLink(googlePlayLink);
qrCodeSettings.setAppStoreLink(appStoreLink);
} else {
MobileApp androidApp = mobileAppService.findByBundleIdAndPlatformType(qrCodeSettings.getTenantId(), qrCodeSettings.getMobileAppBundleId(), ANDROID);
MobileApp iosApp = mobileAppService.findByBundleIdAndPlatformType(qrCodeSettings.getTenantId(), qrCodeSettings.getMobileAppBundleId(), IOS);
if (androidApp != null && androidApp.getStoreInfo() != null) {
qrCodeSettings.setGooglePlayLink(androidApp.getStoreInfo().getStoreLink());
}
if (iosApp != null && iosApp.getStoreInfo() != null) {
qrCodeSettings.setAppStoreLink(iosApp.getStoreInfo().getStoreLink());
}
}
return qrCodeSettings;
}
}

10
dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppSettingsCaffeineCache.java → dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingsCaffeineCache.java

@ -21,14 +21,14 @@ import org.springframework.stereotype.Service;
import org.thingsboard.server.cache.CaffeineTbTransactionalCache;
import org.thingsboard.server.common.data.CacheConstants;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.MobileAppSettings;
import org.thingsboard.server.common.data.mobile.qrCodeSettings.QrCodeSettings;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true)
@Service("MobileAppCache")
public class MobileAppSettingsCaffeineCache extends CaffeineTbTransactionalCache<TenantId, MobileAppSettings> {
@Service("QrCodeSettingsCache")
public class QrCodeSettingsCaffeineCache extends CaffeineTbTransactionalCache<TenantId, QrCodeSettings> {
public MobileAppSettingsCaffeineCache(CacheManager cacheManager) {
super(cacheManager, CacheConstants.MOBILE_APP_SETTINGS_CACHE);
public QrCodeSettingsCaffeineCache(CacheManager cacheManager) {
super(cacheManager, CacheConstants.QR_CODE_SETTINGS_CACHE);
}
}

6
dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppSettingsDao.java → dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingsDao.java

@ -16,13 +16,13 @@
package org.thingsboard.server.dao.mobile;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.MobileAppSettings;
import org.thingsboard.server.common.data.mobile.qrCodeSettings.QrCodeSettings;
import org.thingsboard.server.dao.Dao;
public interface MobileAppSettingsDao extends Dao<MobileAppSettings> {
public interface QrCodeSettingsDao extends Dao<QrCodeSettings> {
MobileAppSettings findByTenantId(TenantId tenantId);
QrCodeSettings findByTenantId(TenantId tenantId);
void removeByTenantId(TenantId tenantId);
}

2
dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppSettingsEvictEvent.java → dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingsEvictEvent.java

@ -19,6 +19,6 @@ import lombok.Data;
import org.thingsboard.server.common.data.id.TenantId;
@Data
public class MobileAppSettingsEvictEvent {
public class QrCodeSettingsEvictEvent {
private final TenantId tenantId;
}

10
dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppSettingsRedisCache.java → dao/src/main/java/org/thingsboard/server/dao/mobile/QrCodeSettingsRedisCache.java

@ -24,13 +24,13 @@ 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.id.TenantId;
import org.thingsboard.server.common.data.mobile.MobileAppSettings;
import org.thingsboard.server.common.data.mobile.qrCodeSettings.QrCodeSettings;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
@Service("MobileAppCache")
public class MobileAppSettingsRedisCache extends RedisTbTransactionalCache<TenantId, MobileAppSettings> {
@Service("QrCodeSettingsCache")
public class QrCodeSettingsRedisCache extends RedisTbTransactionalCache<TenantId, QrCodeSettings> {
public MobileAppSettingsRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) {
super(CacheConstants.MOBILE_APP_SETTINGS_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbJsonRedisSerializer<>(MobileAppSettings.class));
public QrCodeSettingsRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) {
super(CacheConstants.QR_CODE_SETTINGS_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbJsonRedisSerializer<>(QrCodeSettings.class));
}
}

33
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -449,11 +449,25 @@ public class ModelConstants {
public static final String MOBILE_APP_TABLE_NAME = "mobile_app";
public static final String MOBILE_APP_PKG_NAME_PROPERTY = "pkg_name";
public static final String MOBILE_APP_APP_SECRET_PROPERTY = "app_secret";
public static final String MOBILE_APP_OAUTH2_ENABLED_PROPERTY = "oauth2_enabled";
public static final String MOBILE_APP_PLATFORM_TYPE_PROPERTY = "platform_type";
public static final String MOBILE_APP_STATUS_PROPERTY = "status";
public static final String MOBILE_APP_VERSION_INFO_PROPERTY = "version_info";
public static final String MOBILE_APP_STORE_INFO_PROPERTY = "store_info";
public static final String MOBILE_APP_OAUTH2_CLIENT_TABLE_NAME = "mobile_app_oauth2_client";
public static final String MOBILE_APP_OAUTH2_CLIENT_CLIENT_ID_PROPERTY = "oauth2_client_id";
public static final String MOBILE_APP_OAUTH2_CLIENT_MOBILE_APP_ID_PROPERTY = "mobile_app_id";
/**
* Mobile application bundle constants.
*/
public static final String MOBILE_APP_BUNDLE_TABLE_NAME = "mobile_app_bundle";
public static final String MOBILE_APP_BUNDLE_TITLE_PROPERTY = "title";
public static final String MOBILE_APP_BUNDLE_DESCRIPTION_PROPERTY = "description";
public static final String MOBILE_APP_BUNDLE_ANDROID_APP_ID_PROPERTY = "android_app_id";
public static final String MOBILE_APP_BUNDLE_IOS_APP_ID_PROPERTY = "ios_app_id";
public static final String MOBILE_APP_BUNDLE_LAYOUT_CONFIG_PROPERTY = "layout_config";
public static final String MOBILE_APP_BUNDLE_OAUTH2_ENABLED_PROPERTY = "oauth2_enabled";
public static final String MOBILE_APP_BUNDLE_OAUTH2_CLIENT_TABLE_NAME = "mobile_app_bundle_oauth2_client";
public static final String MOBILE_APP_BUNDLE_OAUTH2_CLIENT_CLIENT_ID_PROPERTY = "oauth2_client_id";
public static final String MOBILE_APP_BUNDLE_OAUTH2_CLIENT_MOBILE_APP_BUNDLE_ID_PROPERTY = "mobile_app_bundle_id";
/**
@ -685,11 +699,12 @@ public class ModelConstants {
/**
* Mobile application settings constants.
*/
public static final String MOBILE_APP_SETTINGS_TABLE_NAME = "mobile_app_settings";
public static final String MOBILE_APP_SETTINGS_USE_DEFAULT_APP_PROPERTY = "use_default_app";
public static final String MOBILE_APP_SETTINGS_ANDROID_CONFIG_PROPERTY = "android_config";
public static final String MOBILE_APP_SETTINGS_IOS_CONFIG_PROPERTY = "ios_config";
public static final String MOBILE_APP_SETTINGS_QR_CODE_CONFIG_PROPERTY = "qr_code_config";
public static final String QR_CODE_SETTINGS_TABLE_NAME = "qr_code_settings";
public static final String QR_CODE_SETTINGS_USE_DEFAULT_APP_PROPERTY = "use_default_app";
public static final String QR_CODE_SETTINGS_ANDROID_ENABLED_PROPERTY = "android_enabled";
public static final String QR_CODE_SETTINGS_IOS_ENABLED_PROPERTY = "ios_enabled";
public static final String QR_CODE_SETTINGS_BUNDLE_ID_PROPERTY = "mobile_app_bundle_id";
public static final String QR_CODE_SETTINGS_CONFIG_PROPERTY = "qr_code_config";
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};

114
dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractMobileAppBundleEntity.java

@ -0,0 +1,114 @@
/**
* Copyright © 2016-2024 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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 com.fasterxml.jackson.databind.JsonNode;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.MappedSuperclass;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.id.MobileAppBundleId;
import org.thingsboard.server.common.data.id.MobileAppId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.mobile.bundle.MobileAppBundle;
import org.thingsboard.server.common.data.mobile.layout.MobileLayoutConfig;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.util.mapping.JsonConverter;
import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.TENANT_ID_COLUMN;
@Data
@EqualsAndHashCode(callSuper = true)
@MappedSuperclass
public abstract class AbstractMobileAppBundleEntity<T extends MobileAppBundle> extends BaseSqlEntity<T> {
@Column(name = TENANT_ID_COLUMN)
private UUID tenantId;
@Column(name = ModelConstants.MOBILE_APP_BUNDLE_TITLE_PROPERTY)
private String title;
@Column(name = ModelConstants.MOBILE_APP_BUNDLE_DESCRIPTION_PROPERTY)
private String description;
@Column(name = ModelConstants.MOBILE_APP_BUNDLE_ANDROID_APP_ID_PROPERTY)
private UUID androidAppId;
@Column(name = ModelConstants.MOBILE_APP_BUNDLE_IOS_APP_ID_PROPERTY)
private UUID iosAppID;
@Convert(converter = JsonConverter.class)
@Column(name = ModelConstants.MOBILE_APP_BUNDLE_LAYOUT_CONFIG_PROPERTY)
private JsonNode layoutConfig;
@Column(name = ModelConstants.MOBILE_APP_BUNDLE_OAUTH2_ENABLED_PROPERTY)
private Boolean oauth2Enabled;
public AbstractMobileAppBundleEntity() {
super();
}
public AbstractMobileAppBundleEntity(MobileAppBundleEntity mobileAppBundleEntity) {
super(mobileAppBundleEntity);
this.tenantId = mobileAppBundleEntity.getTenantId();
this.title = mobileAppBundleEntity.getTitle();
this.description = mobileAppBundleEntity.getDescription();
this.androidAppId = mobileAppBundleEntity.getAndroidAppId();
this.iosAppID = mobileAppBundleEntity.getIosAppID();
this.layoutConfig = mobileAppBundleEntity.getLayoutConfig();
this.oauth2Enabled = mobileAppBundleEntity.getOauth2Enabled();
}
public AbstractMobileAppBundleEntity(T mobileAppBundle) {
super(mobileAppBundle);
if (mobileAppBundle.getTenantId() != null) {
this.tenantId = mobileAppBundle.getTenantId().getId();
}
this.title = mobileAppBundle.getTitle();
this.description = mobileAppBundle.getDescription();
if (mobileAppBundle.getAndroidAppId() != null) {
this.androidAppId = mobileAppBundle.getAndroidAppId().getId();
}
if (mobileAppBundle.getIosAppId() != null) {
this.iosAppID = mobileAppBundle.getIosAppId().getId();
}
this.layoutConfig = toJson(mobileAppBundle.getLayoutConfig());
this.oauth2Enabled = mobileAppBundle.getOauth2Enabled();
}
protected MobileAppBundle toMobileAppBundle() {
MobileAppBundle mobileAppBundle = new MobileAppBundle(new MobileAppBundleId(id));
mobileAppBundle.setCreatedTime(createdTime);
mobileAppBundle.setTitle(title);
mobileAppBundle.setDescription(description);
if (tenantId != null) {
mobileAppBundle.setTenantId(TenantId.fromUUID(tenantId));
}
if (androidAppId != null) {
mobileAppBundle.setAndroidAppId(new MobileAppId(androidAppId));
}
if (iosAppID != null) {
mobileAppBundle.setIosAppId(new MobileAppId(iosAppID));
}
mobileAppBundle.setLayoutConfig(fromJson(layoutConfig, MobileLayoutConfig.class));
mobileAppBundle.setOauth2Enabled(oauth2Enabled);
return mobileAppBundle;
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save