@ -0,0 +1,22 @@ |
|||
{ |
|||
"widgetsBundle": { |
|||
"alias": "liquid_level_tanks", |
|||
"title": "Liquid level tanks", |
|||
"image": "data:image/png;base64,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", |
|||
"description": "By visually representing the liquid's volume, these widgets help in monitoring, optimizing storage, and ensuring efficient operations. Suitable for a variety of storage setups, they are essential for industries requiring precise liquid management.", |
|||
"externalId": null, |
|||
"name": "Liquid level tanks" |
|||
}, |
|||
"widgetTypeFqns": [ |
|||
"vertical_cylinder_tank", |
|||
"rectangle_tank", |
|||
"horizontal_cylinder_tank", |
|||
"horizontal_ellipse_tank", |
|||
"horizontal_oval_tank", |
|||
"vertical_oval_tank", |
|||
"horizontal_capsule_tank", |
|||
"vertical_capsule_tank", |
|||
"horizontal_2_1_elliptical_tank", |
|||
"horizontal_dish_ends_tank" |
|||
] |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
{ |
|||
"fqn": "horizontal_capsule_tank", |
|||
"name": "Horizontal capsule tank", |
|||
"deprecated": false, |
|||
"image": "data:image/png;base64,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", |
|||
"description": "Widget indicates the level of liquid in Horizontal capsule tank.", |
|||
"descriptor": { |
|||
"type": "latest", |
|||
"sizeX": 4, |
|||
"sizeY": 4, |
|||
"resources": [], |
|||
"templateHtml": "<tb-liquid-level-widget \n [ctx]=\"ctx\"\n [widgetTitlePanel]=\"widgetTitlePanel\">\n</tb-liquid-level-widget>", |
|||
"templateCss": "", |
|||
"controllerScript": "self.onInit = function() {\n self.ctx.$scope.liquidLevelWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.liquidLevelWidget.update();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n}\n\nself.actionSources = function() { \n return { \n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false \n } \n };\n}", |
|||
"settingsSchema": "{}", |
|||
"dataKeySettingsSchema": "{}\n", |
|||
"settingsDirective": "tb-liquid-level-card-widget-settings", |
|||
"hasBasicMode": true, |
|||
"basicModeDirective": "tb-liquid-level-card-basic-config", |
|||
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"return Math.floor(Math.random() * 101);\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"tankSelectionType\":\"static\",\"selectedShape\":\"Horizontal Capsule\",\"shapeAttributeName\":\"tankShape\",\"tankColor\":{\"type\":\"range\",\"color\":\"#242770\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E73535DE\"},{\"from\":20,\"to\":null,\"color\":\"#242770\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E73535DE';\\n }\\n}\\nreturn '#242770';\"},\"datasourceUnits\":\"%\",\"layout\":\"percentage\",\"volumeSource\":\"static\",\"volumeConstant\":500,\"volumeAttributeName\":\"volume\",\"volumeUnits\":\"L\",\"volumeFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"volumeColor\":\"rgba(0, 0, 0, 0.18)\",\"units\":\"%\",\"widgetUnitsSource\":\"static\",\"widgetUnitsAttributeName\":\"units\",\"liquidColor\":{\"type\":\"range\",\"color\":\"#7A8BFF\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E27C7CDE\"},{\"from\":20,\"to\":null,\"color\":\"#7A8BFF\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FF0000DE\"},{\"from\":20,\"to\":null,\"color\":\"rgba(0,0,0,0.87)\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FF0000DE';\\n }\\n}\\nreturn '#000000DE';\"},\"showBackgroundOverlay\":true,\"backgroundOverlayColor\":{\"type\":\"range\",\"color\":\"#FFFFFFC2\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FFEFEFDE\"},{\"from\":20,\"to\":null,\"color\":\"#FFFFFFC2\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FFEFEFDE';\\n }\\n}\\nreturn '#FFFFFFC2';\"},\"showTooltip\":true,\"showTooltipLevel\":true,\"tooltipUnits\":\"%\",\"tooltipLevelDecimals\":0,\"tooltipLevelFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipLevelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.76)\",\"rangeList\":[],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"showTooltipDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":3,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Liquid level\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"titleFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1.5\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"showTitleIcon\":false,\"titleIcon\":\"water_drop\",\"iconColor\":\"#5469FF\",\"decimals\":0,\"enableDataExport\":false,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" |
|||
}, |
|||
"externalId": null, |
|||
"tags": null |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
{ |
|||
"fqn": "horizontal_cylinder_tank", |
|||
"name": "Horizontal cylinder tank", |
|||
"deprecated": false, |
|||
"image": "data:image/png;base64,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", |
|||
"description": "Widget indicates the level of liquid in Horizontal cylinder tank.", |
|||
"descriptor": { |
|||
"type": "latest", |
|||
"sizeX": 4, |
|||
"sizeY": 4, |
|||
"resources": [], |
|||
"templateHtml": "<tb-liquid-level-widget \n [ctx]=\"ctx\"\n [widgetTitlePanel]=\"widgetTitlePanel\">\n</tb-liquid-level-widget>", |
|||
"templateCss": "", |
|||
"controllerScript": "self.onInit = function() {\n self.ctx.$scope.liquidLevelWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.liquidLevelWidget.update();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n}\n\nself.actionSources = function() { \n return { \n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false \n } \n };\n}", |
|||
"settingsSchema": "{}", |
|||
"dataKeySettingsSchema": "{}\n", |
|||
"settingsDirective": "tb-liquid-level-card-widget-settings", |
|||
"hasBasicMode": true, |
|||
"basicModeDirective": "tb-liquid-level-card-basic-config", |
|||
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"return Math.floor(Math.random() * 101);\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"tankSelectionType\":\"static\",\"selectedShape\":\"Horizontal Cylinder\",\"shapeAttributeName\":\"tankShape\",\"tankColor\":{\"type\":\"range\",\"color\":\"#242770\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E73535DE\"},{\"from\":20,\"to\":null,\"color\":\"#242770\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E73535DE';\\n }\\n}\\nreturn '#242770';\"},\"datasourceUnits\":\"%\",\"layout\":\"percentage\",\"volumeSource\":\"static\",\"volumeConstant\":500,\"volumeAttributeName\":\"volume\",\"volumeUnits\":\"L\",\"volumeFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"volumeColor\":\"rgba(0, 0, 0, 0.18)\",\"units\":\"%\",\"widgetUnitsSource\":\"static\",\"widgetUnitsAttributeName\":\"units\",\"liquidColor\":{\"type\":\"range\",\"color\":\"#7A8BFF\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E27C7CDE\"},{\"from\":20,\"to\":null,\"color\":\"#7A8BFF\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FF0000DE\"},{\"from\":20,\"to\":null,\"color\":\"rgba(0,0,0,0.87)\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FF0000DE';\\n }\\n}\\nreturn '#000000DE';\"},\"showBackgroundOverlay\":true,\"backgroundOverlayColor\":{\"type\":\"range\",\"color\":\"#FFFFFFC2\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FFEFEFDE\"},{\"from\":20,\"to\":null,\"color\":\"#FFFFFFC2\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FFEFEFDE';\\n }\\n}\\nreturn '#FFFFFFC2';\"},\"showTooltip\":true,\"showTooltipLevel\":true,\"tooltipUnits\":\"%\",\"tooltipLevelDecimals\":0,\"tooltipLevelFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipLevelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.76)\",\"rangeList\":[],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"showTooltipDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":3,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Liquid level\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"titleFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1.5\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"showTitleIcon\":false,\"titleIcon\":\"water_drop\",\"iconColor\":\"#5469FF\",\"decimals\":0,\"enableDataExport\":false,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" |
|||
}, |
|||
"externalId": null, |
|||
"tags": null |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
{ |
|||
"fqn": "horizontal_dish_ends_tank", |
|||
"name": "Horizontal dish ends tank", |
|||
"deprecated": false, |
|||
"image": "data:image/png;base64,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", |
|||
"description": "Widget indicates the level of liquid in Horizontal dish ends tank.", |
|||
"descriptor": { |
|||
"type": "latest", |
|||
"sizeX": 4, |
|||
"sizeY": 4, |
|||
"resources": [], |
|||
"templateHtml": "<tb-liquid-level-widget \n [ctx]=\"ctx\"\n [widgetTitlePanel]=\"widgetTitlePanel\">\n</tb-liquid-level-widget>", |
|||
"templateCss": "", |
|||
"controllerScript": "self.onInit = function() {\n self.ctx.$scope.liquidLevelWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.liquidLevelWidget.update();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n}\n\nself.actionSources = function() { \n return { \n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false \n } \n };\n}", |
|||
"settingsSchema": "{}", |
|||
"dataKeySettingsSchema": "{}\n", |
|||
"settingsDirective": "tb-liquid-level-card-widget-settings", |
|||
"hasBasicMode": true, |
|||
"basicModeDirective": "tb-liquid-level-card-basic-config", |
|||
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"return Math.floor(Math.random() * 101);\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"tankSelectionType\":\"static\",\"selectedShape\":\"Horizontal Dish Ends\",\"shapeAttributeName\":\"tankShape\",\"tankColor\":{\"type\":\"range\",\"color\":\"#242770\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E73535DE\"},{\"from\":20,\"to\":null,\"color\":\"#242770\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E73535DE';\\n }\\n}\\nreturn '#242770';\"},\"datasourceUnits\":\"%\",\"layout\":\"percentage\",\"volumeSource\":\"static\",\"volumeConstant\":500,\"volumeAttributeName\":\"volume\",\"volumeUnits\":\"L\",\"volumeFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"volumeColor\":\"rgba(0, 0, 0, 0.18)\",\"units\":\"%\",\"widgetUnitsSource\":\"static\",\"widgetUnitsAttributeName\":\"units\",\"liquidColor\":{\"type\":\"range\",\"color\":\"#7A8BFF\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E27C7CDE\"},{\"from\":20,\"to\":null,\"color\":\"#7A8BFF\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FF0000DE\"},{\"from\":20,\"to\":null,\"color\":\"rgba(0,0,0,0.87)\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FF0000DE';\\n }\\n}\\nreturn '#000000DE';\"},\"showBackgroundOverlay\":true,\"backgroundOverlayColor\":{\"type\":\"range\",\"color\":\"rgba(255, 255, 255, 0.76)\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FFEFEFDE\"},{\"from\":20,\"to\":null,\"color\":\"#FFFFFFC2\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FFEFEFDE';\\n }\\n}\\nreturn '#FFFFFFC2';\"},\"showTooltip\":true,\"showTooltipLevel\":true,\"tooltipUnits\":\"%\",\"tooltipLevelDecimals\":0,\"tooltipLevelFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipLevelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.76)\",\"rangeList\":[],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"showTooltipDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":3,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Liquid level\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"titleFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1.5\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"showTitleIcon\":false,\"titleIcon\":\"water_drop\",\"iconColor\":\"#5469FF\",\"decimals\":0,\"enableDataExport\":false,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" |
|||
}, |
|||
"externalId": null, |
|||
"tags": null |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
{ |
|||
"fqn": "horizontal_ellipse_tank", |
|||
"name": "Horizontal ellipse tank", |
|||
"deprecated": false, |
|||
"image": "data:image/png;base64,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", |
|||
"description": "Widget indicates the level of liquid in Horizontal ellipse tank.", |
|||
"descriptor": { |
|||
"type": "latest", |
|||
"sizeX": 4, |
|||
"sizeY": 4, |
|||
"resources": [], |
|||
"templateHtml": "<tb-liquid-level-widget \n [ctx]=\"ctx\"\n [widgetTitlePanel]=\"widgetTitlePanel\">\n</tb-liquid-level-widget>", |
|||
"templateCss": "", |
|||
"controllerScript": "self.onInit = function() {\n self.ctx.$scope.liquidLevelWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.liquidLevelWidget.update();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n}\n\nself.actionSources = function() { \n return { \n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false \n } \n };\n}", |
|||
"settingsSchema": "{}", |
|||
"dataKeySettingsSchema": "{}\n", |
|||
"settingsDirective": "tb-liquid-level-card-widget-settings", |
|||
"hasBasicMode": true, |
|||
"basicModeDirective": "tb-liquid-level-card-basic-config", |
|||
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"return Math.floor(Math.random() * 101);\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"tankSelectionType\":\"Static\",\"selectedShape\":\"Horizontal Ellipse\",\"shapeAttributeName\":\"tankShape\",\"tankColor\":{\"type\":\"range\",\"color\":\"#242770\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E73535DE\"},{\"from\":20,\"to\":null,\"color\":\"#242770\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E73535DE';\\n }\\n}\\nreturn '#242770';\"},\"datasourceUnits\":\"%\",\"layout\":\"percentage\",\"volumeSource\":\"static\",\"volumeConstant\":500,\"volumeAttributeName\":\"volume\",\"volumeUnits\":\"L\",\"volumeFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"volumeColor\":\"rgba(0, 0, 0, 0.18)\",\"units\":\"%\",\"widgetUnitsSource\":\"static\",\"widgetUnitsAttributeName\":\"units\",\"liquidColor\":{\"type\":\"range\",\"color\":\"#7A8BFF\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E27C7CDE\"},{\"from\":20,\"to\":null,\"color\":\"#7A8BFF\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FF0000DE\"},{\"from\":20,\"to\":null,\"color\":\"rgba(0,0,0,0.87)\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FF0000DE';\\n }\\n}\\nreturn '#000000DE';\"},\"showBackgroundOverlay\":true,\"backgroundOverlayColor\":{\"type\":\"range\",\"color\":\"#FFFFFFC2\",\"rangeList\":[{\"from\":0,\"to\":20,\"color\":\"#FFEFEFDE\"},{\"from\":20,\"to\":null,\"color\":\"#FFFFFFC2\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FFEFEFDE';\\n }\\n}\\nreturn '#FFFFFFC2';\"},\"showTooltip\":true,\"showTooltipLevel\":true,\"tooltipUnits\":\"%\",\"tooltipLevelDecimals\":0,\"tooltipLevelFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipLevelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.76)\",\"rangeList\":[],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"showTooltipDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":3,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Liquid level\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"titleFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1.5\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"showTitleIcon\":false,\"titleIcon\":\"water_drop\",\"iconColor\":\"#5469FF\",\"decimals\":0,\"enableDataExport\":false,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" |
|||
}, |
|||
"externalId": null, |
|||
"tags": null |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
{ |
|||
"fqn": "horizontal_oval_tank", |
|||
"name": "Horizontal oval tank", |
|||
"deprecated": false, |
|||
"image": "data:image/png;base64,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", |
|||
"description": "Widget indicates the level of liquid in Horizontal oval tank.", |
|||
"descriptor": { |
|||
"type": "latest", |
|||
"sizeX": 4, |
|||
"sizeY": 4, |
|||
"resources": [], |
|||
"templateHtml": "<tb-liquid-level-widget \n [ctx]=\"ctx\"\n [widgetTitlePanel]=\"widgetTitlePanel\">\n</tb-liquid-level-widget>", |
|||
"templateCss": "", |
|||
"controllerScript": "self.onInit = function() {\n self.ctx.$scope.liquidLevelWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.liquidLevelWidget.update();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n}\n\nself.actionSources = function() { \n return { \n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false \n } \n };\n}", |
|||
"settingsSchema": "{}", |
|||
"dataKeySettingsSchema": "{}\n", |
|||
"settingsDirective": "tb-liquid-level-card-widget-settings", |
|||
"hasBasicMode": true, |
|||
"basicModeDirective": "tb-liquid-level-card-basic-config", |
|||
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"return Math.floor(Math.random() * 101);\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"tankSelectionType\":\"static\",\"selectedShape\":\"Horizontal Oval\",\"shapeAttributeName\":\"tankShape\",\"tankColor\":{\"type\":\"range\",\"color\":\"#242770\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E73535DE\"},{\"from\":20,\"to\":null,\"color\":\"#242770\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E73535DE';\\n }\\n}\\nreturn '#242770';\"},\"datasourceUnits\":\"%\",\"layout\":\"percentage\",\"volumeSource\":\"static\",\"volumeConstant\":500,\"volumeAttributeName\":\"volume\",\"volumeUnits\":\"L\",\"volumeFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"volumeColor\":\"rgba(0, 0, 0, 0.18)\",\"units\":\"%\",\"widgetUnitsSource\":\"static\",\"widgetUnitsAttributeName\":\"units\",\"liquidColor\":{\"type\":\"range\",\"color\":\"#7A8BFF\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E27C7CDE\"},{\"from\":20,\"to\":null,\"color\":\"#7A8BFF\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FF0000DE\"},{\"from\":20,\"to\":null,\"color\":\"rgba(0,0,0,0.87)\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FF0000DE';\\n }\\n}\\nreturn '#000000DE';\"},\"showBackgroundOverlay\":true,\"backgroundOverlayColor\":{\"type\":\"range\",\"color\":\"rgba(255, 255, 255, 0.76)\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FFEFEFDE\"},{\"from\":20,\"to\":null,\"color\":\"#FFFFFFC2\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FFEFEFDE';\\n }\\n}\\nreturn '#FFFFFFC2';\"},\"showTooltip\":true,\"showTooltipLevel\":true,\"tooltipUnits\":\"%\",\"tooltipLevelDecimals\":0,\"tooltipLevelFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipLevelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.76)\",\"rangeList\":[],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"showTooltipDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":3,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Liquid level\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"titleFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1.5\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"showTitleIcon\":false,\"titleIcon\":\"water_drop\",\"iconColor\":\"#5469FF\",\"decimals\":0,\"enableDataExport\":false,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" |
|||
}, |
|||
"externalId": null, |
|||
"tags": null |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
{ |
|||
"fqn": "rectangle_tank", |
|||
"name": "Rectangle tank", |
|||
"deprecated": false, |
|||
"image": "data:image/png;base64,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", |
|||
"description": "Widget indicates the level of liquid in Rectangle tank.", |
|||
"descriptor": { |
|||
"type": "latest", |
|||
"sizeX": 4, |
|||
"sizeY": 4, |
|||
"resources": [], |
|||
"templateHtml": "<tb-liquid-level-widget \n [ctx]=\"ctx\"\n [widgetTitlePanel]=\"widgetTitlePanel\">\n</tb-liquid-level-widget>", |
|||
"templateCss": "", |
|||
"controllerScript": "self.onInit = function() {\n self.ctx.$scope.liquidLevelWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.liquidLevelWidget.update();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n}\n\nself.actionSources = function() { \n return { \n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false \n } \n };\n}", |
|||
"settingsSchema": "{}", |
|||
"dataKeySettingsSchema": "{}\n", |
|||
"settingsDirective": "tb-liquid-level-card-widget-settings", |
|||
"hasBasicMode": true, |
|||
"basicModeDirective": "tb-liquid-level-card-basic-config", |
|||
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"return Math.floor(Math.random() * 101);\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"tankSelectionType\":\"static\",\"selectedShape\":\"Rectangle\",\"shapeAttributeName\":\"tankShape\",\"tankColor\":{\"type\":\"range\",\"color\":\"#242770\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E73535DE\"},{\"from\":20,\"to\":null,\"color\":\"#242770\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E73535DE';\\n }\\n}\\nreturn '#242770';\"},\"datasourceUnits\":\"%\",\"layout\":\"percentage\",\"volumeSource\":\"static\",\"volumeConstant\":500,\"volumeAttributeName\":\"volume\",\"volumeUnits\":\"L\",\"volumeFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"volumeColor\":\"rgba(0, 0, 0, 0.18)\",\"units\":\"%\",\"widgetUnitsSource\":\"static\",\"widgetUnitsAttributeName\":\"units\",\"liquidColor\":{\"type\":\"range\",\"color\":\"#7A8BFF\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E27C7CDE\"},{\"from\":20,\"to\":null,\"color\":\"#7A8BFF\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FF0000DE\"},{\"from\":20,\"to\":null,\"color\":\"rgba(0,0,0,0.87)\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FF0000DE';\\n }\\n}\\nreturn '#000000DE';\"},\"showBackgroundOverlay\":true,\"backgroundOverlayColor\":{\"type\":\"range\",\"color\":\"#FFFFFFC2\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FFEFEFDE\"},{\"from\":20,\"to\":null,\"color\":\"#FFFFFFC2\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FFEFEFDE';\\n }\\n}\\nreturn '#FFFFFFC2';\"},\"showTooltip\":true,\"showTooltipLevel\":true,\"tooltipUnits\":\"%\",\"tooltipLevelDecimals\":0,\"tooltipLevelFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipLevelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.76)\",\"rangeList\":[],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"showTooltipDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":3,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Liquid level\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"titleFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1.5\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"showTitleIcon\":false,\"titleIcon\":\"water_drop\",\"iconColor\":\"#5469FF\",\"decimals\":0,\"enableDataExport\":false,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" |
|||
}, |
|||
"externalId": null, |
|||
"tags": null |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
{ |
|||
"fqn": "vertical_capsule_tank", |
|||
"name": "Vertical capsule tank", |
|||
"deprecated": false, |
|||
"image": "data:image/png;base64,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", |
|||
"description": "Widget indicates the level of liquid in Vertical capsule tank.", |
|||
"descriptor": { |
|||
"type": "latest", |
|||
"sizeX": 4, |
|||
"sizeY": 4, |
|||
"resources": [], |
|||
"templateHtml": "<tb-liquid-level-widget \n [ctx]=\"ctx\"\n [widgetTitlePanel]=\"widgetTitlePanel\">\n</tb-liquid-level-widget>", |
|||
"templateCss": "", |
|||
"controllerScript": "self.onInit = function() {\n self.ctx.$scope.liquidLevelWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.liquidLevelWidget.update();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n}\n\nself.actionSources = function() { \n return { \n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false \n } \n };\n}", |
|||
"settingsSchema": "{}", |
|||
"dataKeySettingsSchema": "{}\n", |
|||
"settingsDirective": "tb-liquid-level-card-widget-settings", |
|||
"hasBasicMode": true, |
|||
"basicModeDirective": "tb-liquid-level-card-basic-config", |
|||
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"return Math.floor(Math.random() * 101);\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"tankSelectionType\":\"static\",\"selectedShape\":\"Vertical Capsule\",\"shapeAttributeName\":\"tankShape\",\"tankColor\":{\"type\":\"range\",\"color\":\"#242770\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E73535DE\"},{\"from\":20,\"to\":null,\"color\":\"#242770\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E73535DE';\\n }\\n}\\nreturn '#242770';\"},\"datasourceUnits\":\"%\",\"layout\":\"percentage\",\"volumeSource\":\"static\",\"volumeConstant\":500,\"volumeAttributeName\":\"volume\",\"volumeUnits\":\"L\",\"volumeFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"volumeColor\":\"rgba(0, 0, 0, 0.18)\",\"units\":\"%\",\"widgetUnitsSource\":\"static\",\"widgetUnitsAttributeName\":\"units\",\"liquidColor\":{\"type\":\"range\",\"color\":\"#7A8BFF\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E27C7CDE\"},{\"from\":20,\"to\":null,\"color\":\"#7A8BFF\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FF0000DE\"},{\"from\":20,\"to\":null,\"color\":\"rgba(0,0,0,0.87)\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FF0000DE';\\n }\\n}\\nreturn '#000000DE';\"},\"showBackgroundOverlay\":true,\"backgroundOverlayColor\":{\"type\":\"range\",\"color\":\"#FFFFFFC2\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FFEFEFDE\"},{\"from\":20,\"to\":null,\"color\":\"#FFFFFFC2\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FFEFEFDE';\\n }\\n}\\nreturn '#FFFFFFC2';\"},\"showTooltip\":true,\"showTooltipLevel\":true,\"tooltipUnits\":\"%\",\"tooltipLevelDecimals\":0,\"tooltipLevelFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipLevelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.76)\",\"rangeList\":[],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"showTooltipDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":3,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Liquid level\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"titleFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1.5\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"showTitleIcon\":false,\"titleIcon\":\"water_drop\",\"iconColor\":\"#5469FF\",\"decimals\":0,\"enableDataExport\":false,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" |
|||
}, |
|||
"externalId": null, |
|||
"tags": null |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
{ |
|||
"fqn": "vertical_cylinder_tank", |
|||
"name": "Vertical cylinder tank", |
|||
"deprecated": false, |
|||
"image": "data:image/png;base64,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", |
|||
"description": "Widget indicates the level of liquid in Vertical cylinder tank.", |
|||
"descriptor": { |
|||
"type": "latest", |
|||
"sizeX": 4, |
|||
"sizeY": 4, |
|||
"resources": [], |
|||
"templateHtml": "<tb-liquid-level-widget \n [ctx]=\"ctx\"\n [widgetTitlePanel]=\"widgetTitlePanel\">\n</tb-liquid-level-widget>", |
|||
"templateCss": "", |
|||
"controllerScript": "self.onInit = function() {\n self.ctx.$scope.liquidLevelWidget.onInit();\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.liquidLevelWidget.update();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n}\n\nself.actionSources = function() { \n return { \n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false \n } \n };\n}", |
|||
"settingsSchema": "{}", |
|||
"dataKeySettingsSchema": "{}\n", |
|||
"settingsDirective": "tb-liquid-level-card-widget-settings", |
|||
"hasBasicMode": true, |
|||
"basicModeDirective": "tb-liquid-level-card-basic-config", |
|||
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"return Math.floor(Math.random() * 101);\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"tankSelectionType\":\"static\",\"selectedShape\":\"Vertical Cylinder\",\"shapeAttributeName\":\"tankShape\",\"tankColor\":{\"type\":\"range\",\"color\":\"#242770\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E73535DE\"},{\"from\":20,\"to\":null,\"color\":\"#242770\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E73535DE';\\n }\\n}\\nreturn '#242770';\"},\"datasourceUnits\":\"%\",\"layout\":\"percentage\",\"volumeSource\":\"static\",\"volumeConstant\":500,\"volumeAttributeName\":\"volume\",\"volumeUnits\":\"L\",\"volumeFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"volumeColor\":\"rgba(255, 255, 255, 0.18)\",\"units\":\"%\",\"widgetUnitsSource\":\"static\",\"widgetUnitsAttributeName\":\"units\",\"liquidColor\":{\"type\":\"range\",\"color\":\"#7A8BFF\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E27C7CDE\"},{\"from\":20,\"to\":null,\"color\":\"#7A8BFF\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FF0000DE\"},{\"from\":20,\"to\":null,\"color\":\"rgba(0,0,0,0.87)\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FF0000DE';\\n }\\n}\\nreturn '#000000DE';\"},\"showBackgroundOverlay\":true,\"backgroundOverlayColor\":{\"type\":\"range\",\"color\":\"rgba(255, 255, 255, 0.76)\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FFEFEFDE\"},{\"from\":20,\"to\":null,\"color\":\"#FFFFFFC2\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FFEFEFDE';\\n }\\n}\\nreturn '#FFFFFFC2';\"},\"showTooltip\":true,\"showTooltipLevel\":true,\"tooltipUnits\":\"%\",\"tooltipLevelDecimals\":0,\"tooltipLevelFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipLevelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.76)\",\"rangeList\":[],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"showTooltipDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":3,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Liquid level\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"titleFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1.5\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"showTitleIcon\":false,\"titleIcon\":\"water_drop\",\"iconColor\":\"#5469FF\",\"decimals\":0,\"enableDataExport\":false,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" |
|||
}, |
|||
"externalId": null, |
|||
"tags": null |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
{ |
|||
"fqn": "vertical_oval_tank", |
|||
"name": "Vertical oval tank", |
|||
"deprecated": false, |
|||
"image": "data:image/png;base64,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", |
|||
"description": "Widget indicates the level of liquid in Vertical oval tank.", |
|||
"descriptor": { |
|||
"type": "latest", |
|||
"sizeX": 4, |
|||
"sizeY": 4, |
|||
"resources": [], |
|||
"templateHtml": "<tb-liquid-level-widget \n [ctx]=\"ctx\"\n [widgetTitlePanel]=\"widgetTitlePanel\">\n</tb-liquid-level-widget>", |
|||
"templateCss": "", |
|||
"controllerScript": "self.onInit = function() {\n self.ctx.$scope.liquidLevelWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.liquidLevelWidget.update();\n}\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n maxDataKeys: 1,\n singleEntity: true,\n previewWidth: '250px',\n previewHeight: '250px',\n embedTitlePanel: true\n };\n};\n\nself.onDestroy = function() {\n}\n\nself.actionSources = function() { \n return { \n 'cardClick': {\n name: 'widget-action.card-click',\n multiple: false \n } \n };\n}", |
|||
"settingsSchema": "{}", |
|||
"dataKeySettingsSchema": "{}\n", |
|||
"settingsDirective": "tb-liquid-level-card-widget-settings", |
|||
"hasBasicMode": true, |
|||
"basicModeDirective": "tb-liquid-level-card-basic-config", |
|||
"defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"return Math.floor(Math.random() * 101);\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]}}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"tankSelectionType\":\"static\",\"selectedShape\":\"Vertical Oval\",\"shapeAttributeName\":\"tankShape\",\"tankColor\":{\"type\":\"range\",\"color\":\"#242770\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E73535DE\"},{\"from\":20,\"to\":null,\"color\":\"#242770\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E73535DE';\\n }\\n}\\nreturn '#242770';\"},\"datasourceUnits\":\"%\",\"layout\":\"percentage\",\"volumeSource\":\"static\",\"volumeConstant\":500,\"volumeAttributeName\":\"volume\",\"volumeUnits\":\"L\",\"volumeFont\":{\"family\":\"Roboto\",\"size\":14,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"volumeColor\":\"rgba(0, 0, 0, 0.18)\",\"units\":\"%\",\"widgetUnitsSource\":\"static\",\"widgetUnitsAttributeName\":\"units\",\"liquidColor\":{\"type\":\"range\",\"color\":\"#7A8BFF\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#E27C7CDE\"},{\"from\":20,\"to\":null,\"color\":\"#7A8BFF\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"valueFont\":{\"family\":\"Roboto\",\"size\":24,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"valueColor\":{\"type\":\"range\",\"color\":\"#000000DE\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FF0000DE\"},{\"from\":20,\"to\":null,\"color\":\"rgba(0,0,0,0.87)\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FF0000DE';\\n }\\n}\\nreturn '#000000DE';\"},\"showBackgroundOverlay\":true,\"backgroundOverlayColor\":{\"type\":\"range\",\"color\":\"rgba(255, 255, 255, 0.76)\",\"rangeList\":[{\"from\":null,\"to\":20,\"color\":\"#FFEFEFDE\"},{\"from\":20,\"to\":null,\"color\":\"#FFFFFFC2\"}],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#FFEFEFDE';\\n }\\n}\\nreturn '#FFFFFFC2';\"},\"showTooltip\":true,\"showTooltipLevel\":true,\"tooltipUnits\":\"%\",\"tooltipLevelDecimals\":0,\"tooltipLevelFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipLevelColor\":{\"type\":\"constant\",\"color\":\"rgba(0, 0, 0, 0.76)\",\"rangeList\":[],\"colorFunction\":\"var percent = value;\\nif (typeof percent !== undefined) {\\n if (percent < 20) {\\n return '#E27C7CDE';\\n }\\n}\\nreturn '#7A8BFF';\"},\"showTooltipDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":true,\"custom\":false},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":13,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"100%\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":3,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Liquid level\",\"useDashboardTimewindow\":true,\"displayTimewindow\":true,\"configMode\":\"basic\",\"titleFont\":{\"family\":\"Roboto\",\"size\":16,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"1.5\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"showTitleIcon\":false,\"titleIcon\":\"water_drop\",\"iconColor\":\"#5469FF\",\"decimals\":0,\"enableDataExport\":false,\"enableFullscreen\":false,\"borderRadius\":\"0px\",\"actions\":{},\"iconSize\":\"24px\",\"titleTooltip\":\"\",\"dropShadow\":true,\"margin\":\"0px\",\"widgetStyle\":{},\"widgetCss\":\"\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"pageSize\":1024,\"noDataDisplayMessage\":\"\"}" |
|||
}, |
|||
"externalId": null, |
|||
"tags": null |
|||
} |
|||
@ -0,0 +1,318 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2023 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<ng-container *ngIf="levelCardWidgetConfigForm" [formGroup]="levelCardWidgetConfigForm"> |
|||
<tb-timewindow-config-panel *ngIf="displayTimewindowConfig" |
|||
[onlyHistoryTimewindow]="onlyHistoryTimewindow()" |
|||
formControlName="timewindowConfig"> |
|||
</tb-timewindow-config-panel> |
|||
<tb-datasources |
|||
[configMode]="basicMode" |
|||
hideDataKeyLabel |
|||
hideDataKeyColor |
|||
hideDataKeyUnits |
|||
hideDataKeyDecimals |
|||
formControlName="datasources"> |
|||
</tb-datasources> |
|||
<div class="tb-form-panel"> |
|||
<div class="tb-form-panel-title" translate>widget-config.appearance</div> |
|||
<div class="tb-form-row column-xs"> |
|||
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTitle"> |
|||
{{ 'widgets.liquid-level-card.title' | translate }} |
|||
</mat-slide-toggle> |
|||
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
|||
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic"> |
|||
<input matInput formControlName="title" placeholder="{{ 'widget-config.set' | translate }}"> |
|||
</mat-form-field> |
|||
<tb-font-settings formControlName="titleFont" |
|||
[previewText]="levelCardWidgetConfigForm.get('title').value"> |
|||
</tb-font-settings> |
|||
<tb-color-input asBoxInput colorClearButton formControlName="titleColor"> |
|||
</tb-color-input> |
|||
</div> |
|||
</div> |
|||
<div class="tb-form-row column-xs"> |
|||
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTitleIcon"> |
|||
{{ 'widgets.liquid-level-card.icon' | translate }} |
|||
</mat-slide-toggle> |
|||
<div fxFlex fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px"> |
|||
<mat-form-field appearance="outline" class="flex number" subscriptSizing="dynamic"> |
|||
<input matInput type="number" min="0" formControlName="iconSize" placeholder="{{ 'widget-config.set' | translate }}"> |
|||
</mat-form-field> |
|||
<tb-css-unit-select fxFlex formControlName="iconSizeUnit"></tb-css-unit-select> |
|||
<tb-material-icon-select asBoxInput |
|||
iconClearButton |
|||
[color]="levelCardWidgetConfigForm.get('iconColor').value" |
|||
formControlName="titleIcon"> |
|||
</tb-material-icon-select> |
|||
<tb-color-input asBoxInput colorClearButton formControlName="iconColor"> |
|||
</tb-color-input> |
|||
</div> |
|||
</div> |
|||
<div fxFlex fxLayout="column" class="tb-form-row space-between"> |
|||
<div fxFlex fxLayout="row" style="width: 100%;" fxLayoutAlign="space-between center"> |
|||
<div class="fixed-title-width" translate>widgets.liquid-level-card.shape</div> |
|||
<tb-toggle-select formControlName="tankSelectionType"> |
|||
<tb-toggle-option *ngFor="let type of DataSourceTypes" [value]="type"> |
|||
{{ DataSourceTypeTranslations.get(type) | translate }} |
|||
</tb-toggle-option> |
|||
</tb-toggle-select> |
|||
</div> |
|||
<tb-image-cards-select #shapesImageCardsSelect |
|||
[fxShow]="levelCardWidgetConfigForm.get('tankSelectionType').value === DataSourceType.static" |
|||
rowHeight="{{ '1:1' }}" |
|||
[cols]="5" |
|||
[colsLtMd]="2" |
|||
style="width: 100%;" |
|||
label="{{ 'widgets.liquid-level-card.shape-type' | translate }}" formControlName="selectedShape"> |
|||
<tb-image-cards-select-option *ngFor="let shape of shapes" |
|||
[value]="shape" |
|||
[image]="createShape(shapesImageMap.get(shape), LevelCardLayout.simple)"> |
|||
{{ ShapesTranslationMap.get(shape) | translate }} |
|||
</tb-image-cards-select-option> |
|||
</tb-image-cards-select> |
|||
<ng-container *ngIf="levelCardWidgetConfigForm.get('tankSelectionType').value === DataSourceType.attribute"> |
|||
<div class="tb-form-row space-between" style="width: 100%;"> |
|||
<div class="fixed-title-width tb-required" translate>widgets.liquid-level-card.shape-attribute-name</div> |
|||
<tb-string-autocomplete [fetchOptionsFn]="fetchOptions.bind(this)" |
|||
required style="flex: 1" |
|||
[errorText]="'widgets.liquid-level-card.attribute-name-required' | translate" |
|||
formControlName="shapeAttributeName"> |
|||
</tb-string-autocomplete> |
|||
</div> |
|||
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center" style="width: 100%;"> |
|||
<div class="tb-hint" style="padding: 0;" translate>widgets.liquid-level-card.shape-by-attribute</div> |
|||
<div class="see-example" |
|||
[tb-help-popup]="'widget/lib/indicator/shape_attribute_fn'" |
|||
tb-help-popup-placement="left" |
|||
trigger-style="font-size: 12px" |
|||
[tb-help-popup-style]="{maxWidth: '820px'}" |
|||
trigger-text="{{ 'widgets.liquid-level-card.see-examples' | translate }}"> |
|||
</div> |
|||
</div> |
|||
</ng-container> |
|||
</div> |
|||
<tb-image-cards-select #layoutsImageCardsSelect rowHeight="{{ '1:1' }}" |
|||
[cols]="3" |
|||
[colsLtMd]="3" |
|||
label="{{ 'widgets.liquid-level-card.layout' | translate }}" formControlName="layout"> |
|||
<tb-image-cards-select-option |
|||
*ngFor="let layout of LevelCardLayouts" |
|||
[value]="layout" |
|||
[image]="createShape(shapesImageMap.get(levelCardWidgetConfigForm.get('selectedShape').value), layout)"> |
|||
{{ LevelCardLayoutTranslationMap.get(layout) | translate }} |
|||
</tb-image-cards-select-option> |
|||
</tb-image-cards-select> |
|||
</div> |
|||
<div class="tb-form-panel"> |
|||
<div class="tb-form-panel-title" translate>widgets.liquid-level-card.units</div> |
|||
<div class="tb-form-row space-between"> |
|||
<div class="fixed-title-width tb-required" translate>widgets.liquid-level-card.datasource-units</div> |
|||
<div fxFlex fxLayout="row" fxLayoutAlign="end center"> |
|||
<tb-unit-input required style="max-width: 25%;" class="flex" |
|||
[tagFilter]="unitsType.capacity" |
|||
formControlName="datasourceUnits"> |
|||
</tb-unit-input> |
|||
</div> |
|||
</div> |
|||
<div class="tb-form-row space-between" *ngIf="levelCardWidgetConfigForm.get('layout').value === LevelCardLayout.absolute"> |
|||
<div class="fixed-title-width tb-required" translate>widgets.liquid-level-card.widget-units</div> |
|||
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
|||
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic" style="max-width: 25%"> |
|||
<mat-select formControlName="widgetUnitsSource" placeholder="{{ 'widget-config.set' | translate }}"> |
|||
<mat-option *ngFor="let type of DataSourceTypes" [value]="type"> |
|||
{{ DataSourceTypeTranslations.get(type) | translate }} |
|||
</mat-option> |
|||
</mat-select> |
|||
</mat-form-field> |
|||
<tb-unit-input *ngIf="levelCardWidgetConfigForm.get('widgetUnitsSource').value === DataSourceType.static; else selectUnitsAttributes" |
|||
class="flex" required |
|||
[tagFilter]="unitsType.capacity" |
|||
formControlName="units"> |
|||
</tb-unit-input> |
|||
<ng-template #selectUnitsAttributes> |
|||
<tb-string-autocomplete [fetchOptionsFn]="fetchOptions.bind(this)" |
|||
required style="flex: 1" |
|||
[errorText]="'widgets.liquid-level-card.attribute-name-required' | translate" |
|||
formControlName="widgetUnitsAttributeName"> |
|||
</tb-string-autocomplete> |
|||
</ng-template> |
|||
</div> |
|||
</div> |
|||
<div class="tb-form-row" [fxShow]="volumeInput"> |
|||
<div class="fixed-title-width tb-required" translate>widgets.liquid-level-card.total-volume</div> |
|||
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
|||
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic" style="max-width: 25%"> |
|||
<mat-select formControlName="volumeSource" placeholder="{{ 'widget-config.set' | translate }}"> |
|||
<mat-option *ngFor="let type of DataSourceTypes" [value]="type"> |
|||
{{ DataSourceTypeTranslations.get(type) | translate }} |
|||
</mat-option> |
|||
</mat-select> |
|||
</mat-form-field> |
|||
<mat-form-field *ngIf="levelCardWidgetConfigForm.get('volumeSource').value === DataSourceType.static; else volumeAttributes" |
|||
appearance="outline" class="flex number" subscriptSizing="dynamic"> |
|||
<input matInput formControlName="volumeConstant" type="number" |
|||
min="0.1" step="1" placeholder="{{ 'widget-config.set' | translate }}"> |
|||
<mat-icon matSuffix |
|||
matTooltipPosition="above" |
|||
matTooltipClass="tb-error-tooltip" |
|||
[matTooltip]="'widgets.liquid-level-card.total-volume-required' | translate" |
|||
*ngIf="levelCardWidgetConfigForm.get('volumeConstant').hasError('required')" |
|||
class="material-icons tb-suffix-show-always tb-error"> |
|||
warning |
|||
</mat-icon> |
|||
</mat-form-field> |
|||
<ng-template #volumeAttributes> |
|||
<tb-string-autocomplete [fetchOptionsFn]="fetchOptions.bind(this)" |
|||
[errorText]="'widgets.liquid-level-card.attribute-name-required' | translate" |
|||
required style="flex: 1" |
|||
formControlName="volumeAttributeName"> |
|||
</tb-string-autocomplete> |
|||
</ng-template> |
|||
<tb-unit-input [tagFilter]="unitsType.capacity" |
|||
required style="max-width: 25%" class="flex" |
|||
formControlName="volumeUnits"> |
|||
</tb-unit-input> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="tb-form-panel"> |
|||
<div class="tb-form-panel-title" translate>widgets.liquid-level-card.color-and-font</div> |
|||
<div class="tb-form-row space-between"> |
|||
<div class="fixed-title-width" translate>widgets.liquid-level-card.tank-color</div> |
|||
<tb-color-settings formControlName="tankColor" settingsKey="{{'widgets.liquid-level-card.tank-color' | translate }}"> |
|||
</tb-color-settings> |
|||
</div> |
|||
<div class="tb-form-row space-between"> |
|||
<div class="fixed-title-width" translate>widgets.liquid-level-card.liquid-color</div> |
|||
<tb-color-settings formControlName="liquidColor" settingsKey="{{'widgets.liquid-level-card.liquid-color' | translate }}"> |
|||
</tb-color-settings> |
|||
</div> |
|||
<div class="tb-form-row space-between" |
|||
*ngIf="levelCardWidgetConfigForm.get('layout').value !== LevelCardLayout.simple"> |
|||
<div class="fixed-title-width" translate>widgets.liquid-level-card.value</div> |
|||
<div fxFlex fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px"> |
|||
<mat-form-field style="max-width: 40%;" appearance="outline" class="flex number" subscriptSizing="dynamic"> |
|||
<input matInput formControlName="decimals" type="number" |
|||
min="0" max="15" step="1" placeholder="{{ 'widget-config.set' | translate }}"> |
|||
<div matSuffix fxHide.lt-md translate>widget-config.decimals-suffix</div> |
|||
</mat-form-field> |
|||
<tb-font-settings formControlName="valueFont" |
|||
[previewText]="valuePreviewFn"> |
|||
</tb-font-settings> |
|||
<tb-color-settings formControlName="valueColor" settingsKey="{{'widgets.liquid-level-card.value' | translate }}"> |
|||
</tb-color-settings> |
|||
</div> |
|||
</div> |
|||
<div class="tb-form-row" *ngIf="levelCardWidgetConfigForm.get('layout').value === LevelCardLayout.absolute"> |
|||
<div class="fixed-title-width" translate>widgets.liquid-level-card.total-volume</div> |
|||
<div fxFlex fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px"> |
|||
<tb-font-settings formControlName="volumeFont" |
|||
[previewText]="totalVolumeValuePreviewFn"> |
|||
</tb-font-settings> |
|||
<tb-color-input asBoxInput colorClearButton formControlName="volumeColor"> |
|||
</tb-color-input> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="tb-form-panel tb-slide-toggle"> |
|||
<mat-expansion-panel class="tb-settings" |
|||
[expanded]="levelCardWidgetConfigForm.get('showTooltip').value" |
|||
[disabled]="!levelCardWidgetConfigForm.get('showTooltip').value"> |
|||
<mat-expansion-panel-header fxLayout="row wrap"> |
|||
<mat-panel-title> |
|||
<mat-slide-toggle class="mat-slide" formControlName="showTooltip" (click)="$event.stopPropagation()" |
|||
fxLayoutAlign="center"> |
|||
{{ 'widget-config.tooltip' | translate }} |
|||
</mat-slide-toggle> |
|||
</mat-panel-title> |
|||
</mat-expansion-panel-header> |
|||
<ng-template matExpansionPanelContent> |
|||
<div class="tb-form-row column-xs"> |
|||
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTooltipLevel"> |
|||
{{ 'widgets.liquid-level-card.level' | translate }} |
|||
</mat-slide-toggle> |
|||
<div fxFlex fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px"> |
|||
<tb-unit-input required class="flex" |
|||
[tagFilter]="unitsType.capacity" |
|||
formControlName="tooltipUnits"> |
|||
</tb-unit-input> |
|||
<mat-form-field style="width: 40%;" appearance="outline" class="flex number" subscriptSizing="dynamic"> |
|||
<input matInput formControlName="tooltipLevelDecimals" type="number" min="0" |
|||
max="15" step="1" placeholder="{{ 'widget-config.set' | translate }}"> |
|||
<div matSuffix fxHide.lt-md translate>widget-config.decimals-suffix</div> |
|||
</mat-form-field> |
|||
<tb-font-settings formControlName="tooltipLevelFont" |
|||
[previewText]="tooltipValuePreviewFn"> |
|||
</tb-font-settings> |
|||
<tb-color-settings formControlName="tooltipLevelColor" settingsKey="{{'widgets.liquid-level-card.background-overlay' | translate }}"> |
|||
</tb-color-settings> |
|||
</div> |
|||
</div> |
|||
<div class="tb-form-row column-xs"> |
|||
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTooltipDate"> |
|||
{{ 'widgets.value-card.date' | translate }} |
|||
</mat-slide-toggle> |
|||
<div fxFlex.gt-xs fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
|||
<tb-date-format-select fxFlex formControlName="tooltipDateFormat"></tb-date-format-select> |
|||
<tb-font-settings formControlName="tooltipDateFont" |
|||
[previewText]="datePreviewFn"> |
|||
</tb-font-settings> |
|||
<tb-color-input asBoxInput colorClearButton formControlName="tooltipDateColor"> |
|||
</tb-color-input> |
|||
</div> |
|||
</div> |
|||
<div class="tb-form-row space-between"> |
|||
<div class="fixed-title-width" translate>widgets.liquid-level-card.tooltip-background</div> |
|||
<tb-color-input asBoxInput colorClearButton formControlName="tooltipBackgroundColor"> |
|||
</tb-color-input> |
|||
</div> |
|||
<div class="tb-form-row space-between"> |
|||
<div class="fixed-title-width" translate>widgets.liquid-level-card.background-blur</div> |
|||
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
|||
<input matInput formControlName="tooltipBackgroundBlur" type="number" min="0" step="1" |
|||
placeholder="{{ 'widget-config.set' | translate }}"> |
|||
<div matSuffix>px</div> |
|||
</mat-form-field> |
|||
</div> |
|||
</ng-template> |
|||
</mat-expansion-panel> |
|||
</div> |
|||
<div class="tb-form-panel"> |
|||
<div class="tb-form-panel-title" translate>widget-config.card-appearance</div> |
|||
<div class="tb-form-row space-between"> |
|||
<div>{{ 'widgets.background.background' | translate }}</div> |
|||
<tb-background-settings formControlName="background"> |
|||
</tb-background-settings> |
|||
</div> |
|||
<div class="tb-form-row space-between column-lt-md"> |
|||
<div translate>widget-config.show-card-buttons</div> |
|||
<mat-chip-listbox multiple formControlName="cardButtons"> |
|||
<mat-chip-option value="fullscreen">{{ 'fullscreen.fullscreen' | translate }}</mat-chip-option> |
|||
</mat-chip-listbox> |
|||
</div> |
|||
<div class="tb-form-row space-between"> |
|||
<div>{{ 'widget-config.card-border-radius' | translate }}</div> |
|||
<mat-form-field appearance="outline" subscriptSizing="dynamic"> |
|||
<input matInput formControlName="borderRadius" placeholder="{{ 'widget-config.set' | translate }}"> |
|||
</mat-form-field> |
|||
</div> |
|||
</div> |
|||
<tb-widget-actions-panel |
|||
formControlName="actions"> |
|||
</tb-widget-actions-panel> |
|||
</ng-container> |
|||
@ -0,0 +1,444 @@ |
|||
///
|
|||
/// Copyright © 2016-2023 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { ChangeDetectorRef, Component, Injector, ViewChild } from '@angular/core'; |
|||
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; |
|||
import { WidgetConfigComponentData } from '@home/models/widget-component.models'; |
|||
import { |
|||
DataKey, |
|||
Datasource, |
|||
datasourcesHasAggregation, |
|||
datasourcesHasOnlyComparisonAggregation, |
|||
DatasourceType, |
|||
WidgetConfig, |
|||
} from '@shared/models/widget.models'; |
|||
import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; |
|||
import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; |
|||
import { |
|||
getTimewindowConfig, |
|||
setTimewindowConfig |
|||
} from '@home/components/widget/config/timewindow-config-panel.component'; |
|||
import { formatValue, isDefined, isUndefined } from '@core/utils'; |
|||
import { |
|||
cssSizeToStrSize, |
|||
DateFormatProcessor, |
|||
DateFormatSettings, |
|||
resolveCssSize |
|||
} from '@shared/models/widget-settings.models'; |
|||
import { |
|||
CapacityUnits, |
|||
createShapeLayout, |
|||
levelCardDefaultSettings, |
|||
LevelCardLayout, |
|||
levelCardLayoutTranslations, |
|||
LevelCardWidgetSettings, |
|||
LiquidWidgetDataSourceType, |
|||
LiquidWidgetDataSourceTypeTranslations, |
|||
loadSvgShapesMapping, |
|||
optionsFilter, |
|||
Shapes, |
|||
ShapesTranslations, |
|||
updatedFormSettingsValidators |
|||
} from '@home/components/widget/lib/indicator/liquid-level-widget.models'; |
|||
import { UnitsType } from '@shared/models/unit.models'; |
|||
import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; |
|||
import { ImageCardsSelectComponent } from '@home/components/widget/lib/settings/common/image-cards-select.component'; |
|||
import { map, share, tap } from 'rxjs/operators'; |
|||
import { Observable, of, ReplaySubject } from 'rxjs'; |
|||
import { ResourcesService } from '@core/services/resources.service'; |
|||
import { UtilsService } from '@core/services/utils.service'; |
|||
|
|||
@Component({ |
|||
selector: 'liquid-level-card-basic-config', |
|||
templateUrl: './liquid-level-card-basic-config.component.html', |
|||
styleUrls: ['../basic-config.scss'] |
|||
}) |
|||
export class LiquidLevelCardBasicConfigComponent extends BasicWidgetConfigComponent { |
|||
|
|||
@ViewChild('layoutsImageCardsSelect', { static: false }) layoutsImageCardsSelect: ImageCardsSelectComponent; |
|||
|
|||
@ViewChild('shapesImageCardsSelect', { static: false }) shapesImageCardsSelect: ImageCardsSelectComponent; |
|||
|
|||
private get datasource(): Datasource { |
|||
if (this.widgetConfig.config.datasources && this.widgetConfig.config.datasources) { |
|||
return this.widgetConfig.config.datasources[0]; |
|||
} else { |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public get volumeInput(): boolean { |
|||
const datasourceUnits = this.levelCardWidgetConfigForm.get('datasourceUnits').value; |
|||
const layout: LevelCardLayout = this.levelCardWidgetConfigForm.get('layout').value; |
|||
|
|||
if (layout === LevelCardLayout.absolute) { |
|||
return true; |
|||
} |
|||
return datasourceUnits !== CapacityUnits.percent; |
|||
} |
|||
|
|||
public get displayTimewindowConfig(): boolean { |
|||
const datasources = this.levelCardWidgetConfigForm.get('datasources').value; |
|||
return datasourcesHasAggregation(datasources); |
|||
} |
|||
|
|||
public onlyHistoryTimewindow(): boolean { |
|||
const datasources = this.levelCardWidgetConfigForm.get('datasources').value; |
|||
return datasourcesHasOnlyComparisonAggregation(datasources); |
|||
} |
|||
|
|||
LevelCardLayout = LevelCardLayout; |
|||
LevelCardLayouts = Object.values(LevelCardLayout) as LevelCardLayout[]; |
|||
LevelCardLayoutTranslationMap = levelCardLayoutTranslations; |
|||
|
|||
DataSourceType = LiquidWidgetDataSourceType; |
|||
DataSourceTypes = Object.values(LiquidWidgetDataSourceType) as LiquidWidgetDataSourceType[]; |
|||
DataSourceTypeTranslations = LiquidWidgetDataSourceTypeTranslations; |
|||
|
|||
shapes = Object.values(Shapes) as Shapes[]; |
|||
shapesImageMap: Map<Shapes, string> = new Map(); |
|||
ShapesTranslationMap = ShapesTranslations; |
|||
|
|||
unitsType = UnitsType; |
|||
|
|||
levelCardWidgetConfigForm: FormGroup; |
|||
|
|||
valuePreviewFn = this._valuePreviewFn.bind(this); |
|||
|
|||
tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); |
|||
|
|||
totalVolumeValuePreviewFn = this._totalVolumeValuePreviewFn.bind(this); |
|||
|
|||
datePreviewFn = this._datePreviewFn.bind(this); |
|||
|
|||
private keySearchText: string; |
|||
private latestKeySearchTextResult: Array<string>; |
|||
|
|||
private functionTypeKeys: Array<DataKey> = []; |
|||
|
|||
private lastKeysId: string; |
|||
private lastFetchedKeys: Array<DataKey>; |
|||
|
|||
constructor(protected store: Store<AppState>, |
|||
protected widgetConfigComponent: WidgetConfigComponent, |
|||
private cd: ChangeDetectorRef, |
|||
private $injector: Injector, |
|||
private fb: FormBuilder, |
|||
private resourcesService: ResourcesService, |
|||
private sanitizer: DomSanitizer, |
|||
private utils: UtilsService) { |
|||
super(store, widgetConfigComponent); |
|||
} |
|||
|
|||
protected configForm(): FormGroup { |
|||
return this.levelCardWidgetConfigForm; |
|||
} |
|||
|
|||
protected setupConfig(widgetConfig: WidgetConfigComponentData) { |
|||
this.createSvgShapesMapping(); |
|||
|
|||
for (const type of this.utils.getPredefinedFunctionsList()) { |
|||
this.functionTypeKeys.push({ |
|||
name: type, |
|||
type: DataKeyType.function |
|||
}); |
|||
} |
|||
super.setupConfig(widgetConfig); |
|||
} |
|||
|
|||
protected setupDefaults(configData: WidgetConfigComponentData) { |
|||
this.setupDefaultDatasource(configData, [{ name: 'fuelLevel', label: 'Fuel Level', type: DataKeyType.timeseries }]); |
|||
} |
|||
|
|||
protected onConfigSet(configData: WidgetConfigComponentData) { |
|||
const settings: LevelCardWidgetSettings = {...levelCardDefaultSettings, ...(configData.config.settings || {})}; |
|||
const iconSize = resolveCssSize(configData.config.iconSize); |
|||
|
|||
this.levelCardWidgetConfigForm = this.fb.group({ |
|||
timewindowConfig: [getTimewindowConfig(configData.config), []], |
|||
datasources: [configData.config.datasources, []], |
|||
|
|||
layout: [settings.layout, []], |
|||
tankSelectionType: [settings.tankSelectionType, []], |
|||
selectedShape: [settings.selectedShape, [Validators.required]], |
|||
shapeAttributeName: [settings.shapeAttributeName, [Validators.required]], |
|||
tankColor: [settings.tankColor, []], |
|||
datasourceUnits: [settings.datasourceUnits, [Validators.required]], |
|||
|
|||
showTitle: [configData.config.showTitle, []], |
|||
title: [configData.config.title, []], |
|||
titleFont: [configData.config.titleFont, []], |
|||
titleColor: [configData.config.titleColor, []], |
|||
showTitleIcon: [configData.config.showTitleIcon, []], |
|||
iconSize: [iconSize[0], [Validators.min(0)]], |
|||
iconSizeUnit: [iconSize[1], []], |
|||
titleIcon: [configData.config.titleIcon, []], |
|||
iconColor: [configData.config.iconColor, []], |
|||
|
|||
volumeSource: [settings.volumeSource, []], |
|||
volumeConstant: [settings.volumeConstant, [Validators.required, Validators.min(0.1)]], |
|||
volumeAttributeName: [settings.volumeAttributeName, [Validators.required]], |
|||
volumeUnits: [settings.volumeUnits, [Validators.required]], |
|||
volumeFont: [settings.volumeFont, []], |
|||
volumeColor: [settings.volumeColor, []], |
|||
units: [settings.units, [Validators.required]], |
|||
widgetUnitsSource: [settings.widgetUnitsSource, [Validators.required]], |
|||
widgetUnitsAttributeName: [settings.widgetUnitsAttributeName, [Validators.required]], |
|||
decimals: [configData.config.decimals, []], |
|||
liquidColor: [settings.liquidColor, []], |
|||
valueFont: [settings.valueFont, []], |
|||
valueColor: [settings.valueColor, []], |
|||
|
|||
showTooltip: [settings.showTooltip, []], |
|||
showTooltipLevel: [settings.showTooltipLevel, []], |
|||
tooltipUnits: [settings.tooltipUnits, []], |
|||
tooltipLevelDecimals: [settings.tooltipLevelDecimals, []], |
|||
tooltipLevelFont: [settings.tooltipLevelFont, []], |
|||
tooltipLevelColor: [settings.tooltipLevelColor, []], |
|||
showTooltipDate: [settings.showTooltipDate, []], |
|||
tooltipDateFormat: [settings.tooltipDateFormat, []], |
|||
tooltipDateFont: [settings.tooltipDateFont, []], |
|||
tooltipDateColor: [settings.tooltipDateColor, []], |
|||
tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], |
|||
tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], |
|||
|
|||
background: [settings.background], |
|||
cardButtons: [this.getCardButtons(configData.config), []], |
|||
borderRadius: [configData.config.borderRadius, []], |
|||
|
|||
actions: [configData.config.actions || {}, []] |
|||
}); |
|||
|
|||
this.levelCardWidgetConfigForm.get('selectedShape').valueChanges.subscribe(() => { |
|||
this.cd.detectChanges(); |
|||
this.layoutsImageCardsSelect?.imageCardsSelectOptions.notifyOnChanges(); |
|||
}); |
|||
} |
|||
|
|||
protected prepareOutputConfig(config: any): WidgetConfigComponentData { |
|||
setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig); |
|||
this.widgetConfig.config.datasources = config.datasources; |
|||
|
|||
this.widgetConfig.config.showTitle = config.showTitle; |
|||
this.widgetConfig.config.title = config.title; |
|||
this.widgetConfig.config.titleFont = config.titleFont; |
|||
this.widgetConfig.config.titleColor = config.titleColor; |
|||
|
|||
this.widgetConfig.config.showTitleIcon = config.showTitleIcon; |
|||
this.widgetConfig.config.iconSize = cssSizeToStrSize(config.iconSize, config.iconSizeUnit); |
|||
this.widgetConfig.config.titleIcon = config.titleIcon; |
|||
this.widgetConfig.config.iconColor = config.iconColor; |
|||
|
|||
this.widgetConfig.config.decimals = config.decimals; |
|||
|
|||
this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; |
|||
|
|||
this.widgetConfig.config.settings.layout = config.layout; |
|||
|
|||
this.widgetConfig.config.settings.tankSelectionType = config.tankSelectionType; |
|||
this.widgetConfig.config.settings.selectedShape = config.selectedShape; |
|||
this.widgetConfig.config.settings.shapeAttributeName = config.shapeAttributeName; |
|||
this.widgetConfig.config.settings.tankColor = config.tankColor; |
|||
this.widgetConfig.config.settings.datasourceUnits = config.datasourceUnits; |
|||
|
|||
this.widgetConfig.config.settings.volumeSource = config.volumeSource; |
|||
this.widgetConfig.config.settings.volumeConstant = config.volumeConstant; |
|||
this.widgetConfig.config.settings.volumeAttributeName = config.volumeAttributeName; |
|||
this.widgetConfig.config.settings.volumeUnits = config.volumeUnits; |
|||
this.widgetConfig.config.settings.volumeFont = config.volumeFont; |
|||
this.widgetConfig.config.settings.volumeColor = config.volumeColor; |
|||
this.widgetConfig.config.settings.units = config.units; |
|||
this.widgetConfig.config.settings.widgetUnitsSource = config.widgetUnitsSource; |
|||
this.widgetConfig.config.settings.widgetUnitsAttributeName = config.widgetUnitsAttributeName; |
|||
this.widgetConfig.config.settings.liquidColor = config.liquidColor; |
|||
this.widgetConfig.config.settings.valueFont = config.valueFont; |
|||
this.widgetConfig.config.settings.valueColor = config.valueColor; |
|||
|
|||
this.widgetConfig.config.settings.showTooltip = config.showTooltip; |
|||
this.widgetConfig.config.settings.showTooltipLevel = config.showTooltipLevel; |
|||
this.widgetConfig.config.settings.tooltipUnits = config.tooltipUnits; |
|||
this.widgetConfig.config.settings.tooltipLevelDecimals = config.tooltipLevelDecimals; |
|||
this.widgetConfig.config.settings.tooltipLevelFont = config.tooltipLevelFont; |
|||
this.widgetConfig.config.settings.tooltipLevelColor = config.tooltipLevelColor; |
|||
this.widgetConfig.config.settings.showTooltipDate = config.showTooltipDate; |
|||
this.widgetConfig.config.settings.tooltipDateFormat = config.tooltipDateFormat; |
|||
this.widgetConfig.config.settings.tooltipDateFont = config.tooltipDateFont; |
|||
this.widgetConfig.config.settings.tooltipDateColor = config.tooltipDateColor; |
|||
this.widgetConfig.config.settings.tooltipBackgroundColor = config.tooltipBackgroundColor; |
|||
this.widgetConfig.config.settings.tooltipBackgroundBlur = config.tooltipBackgroundBlur; |
|||
|
|||
this.widgetConfig.config.settings.background = config.background; |
|||
this.setCardButtons(config.cardButtons, this.widgetConfig.config); |
|||
this.widgetConfig.config.borderRadius = config.borderRadius; |
|||
|
|||
this.widgetConfig.config.actions = config.actions; |
|||
return this.widgetConfig; |
|||
} |
|||
|
|||
protected validatorTriggers(): string[] { |
|||
return [ |
|||
'showTooltip', 'showTooltipLevel', 'tankSelectionType', 'datasourceUnits', 'showTitleIcon', 'volumeSource', |
|||
'showTooltipDate', 'layout', 'showTitle', 'widgetUnitsSource' |
|||
]; |
|||
} |
|||
|
|||
protected updateValidators(emitEvent: boolean, trigger?: string) { |
|||
updatedFormSettingsValidators(this.levelCardWidgetConfigForm); |
|||
|
|||
const showTitleIcon: boolean = this.levelCardWidgetConfigForm.get('showTitleIcon').value; |
|||
const showTitle: boolean = this.levelCardWidgetConfigForm.get('showTitle').value; |
|||
if (showTitle) { |
|||
this.levelCardWidgetConfigForm.get('title').enable({emitEvent: false}); |
|||
this.levelCardWidgetConfigForm.get('titleFont').enable({emitEvent: false}); |
|||
this.levelCardWidgetConfigForm.get('titleColor').enable({emitEvent: false}); |
|||
this.levelCardWidgetConfigForm.get('showTitleIcon').enable({emitEvent: false}); |
|||
if (showTitleIcon) { |
|||
this.levelCardWidgetConfigForm.get('titleIcon').enable({emitEvent: false}); |
|||
this.levelCardWidgetConfigForm.get('iconColor').enable({emitEvent: false}); |
|||
this.levelCardWidgetConfigForm.get('iconSize').enable({emitEvent: false}); |
|||
this.levelCardWidgetConfigForm.get('iconSizeUnit').enable({emitEvent: false}); |
|||
} else { |
|||
this.levelCardWidgetConfigForm.get('titleIcon').disable({emitEvent: false}); |
|||
this.levelCardWidgetConfigForm.get('iconColor').disable({emitEvent: false}); |
|||
this.levelCardWidgetConfigForm.get('iconSize').disable({emitEvent: false}); |
|||
this.levelCardWidgetConfigForm.get('iconSizeUnit').disable({emitEvent: false}); |
|||
} |
|||
} else { |
|||
this.levelCardWidgetConfigForm.get('title').disable({emitEvent: false}); |
|||
this.levelCardWidgetConfigForm.get('titleFont').disable({emitEvent: false}); |
|||
this.levelCardWidgetConfigForm.get('titleColor').disable({emitEvent: false}); |
|||
this.levelCardWidgetConfigForm.get('showTitleIcon').disable({emitEvent: false}); |
|||
this.levelCardWidgetConfigForm.get('titleIcon').disable({emitEvent: false}); |
|||
this.levelCardWidgetConfigForm.get('iconColor').disable({emitEvent: false}); |
|||
this.levelCardWidgetConfigForm.get('iconSize').disable({emitEvent: false}); |
|||
this.levelCardWidgetConfigForm.get('iconSizeUnit').disable({emitEvent: false}); |
|||
} |
|||
} |
|||
|
|||
private getCardButtons(config: WidgetConfig): string[] { |
|||
const buttons: string[] = []; |
|||
if (isUndefined(config.enableDataExport) || config.enableDataExport) { |
|||
buttons.push('dataExport'); |
|||
} |
|||
if (isUndefined(config.enableFullscreen) || config.enableFullscreen) { |
|||
buttons.push('fullscreen'); |
|||
} |
|||
return buttons; |
|||
} |
|||
|
|||
private setCardButtons(buttons: string[], config: WidgetConfig) { |
|||
config.enableDataExport = buttons.includes('dataExport'); |
|||
config.enableFullscreen = buttons.includes('fullscreen'); |
|||
} |
|||
|
|||
private _valuePreviewFn(): string { |
|||
const units: string = this.levelCardWidgetConfigForm.get('units').value; |
|||
const decimals: number = this.levelCardWidgetConfigForm.get('decimals').value; |
|||
return formatValue(22, decimals, units, true); |
|||
} |
|||
|
|||
private _tooltipValuePreviewFn() { |
|||
const units: string = this.levelCardWidgetConfigForm.get('tooltipUnits').value; |
|||
const decimals: number = this.levelCardWidgetConfigForm.get('tooltipLevelDecimals').value; |
|||
return formatValue(32, decimals, units, true); |
|||
} |
|||
|
|||
private _totalVolumeValuePreviewFn() { |
|||
const value = this.levelCardWidgetConfigForm.get('volumeConstant').value; |
|||
const datasourceUnits = this.levelCardWidgetConfigForm.get('datasourceUnits').value; |
|||
const decimals: number = this.widgetConfig.config.decimals; |
|||
let units: string = this.widgetConfig.config.units; |
|||
|
|||
if (datasourceUnits !== CapacityUnits.percent) { |
|||
units = datasourceUnits; |
|||
} |
|||
|
|||
return formatValue((isDefined(value) ? value : 500), decimals, units, true); |
|||
} |
|||
|
|||
private _datePreviewFn(): string { |
|||
const dateFormat: DateFormatSettings = this.levelCardWidgetConfigForm.get('tooltipDateFormat').value; |
|||
const processor = DateFormatProcessor.fromSettings(this.$injector, dateFormat); |
|||
processor.update(Date.now()); |
|||
return processor.formatted; |
|||
} |
|||
|
|||
private createSvgShapesMapping(): void { |
|||
loadSvgShapesMapping(this.resourcesService).subscribe(shapeMap => { |
|||
this.shapesImageMap = shapeMap; |
|||
|
|||
this.cd.detectChanges(); |
|||
this.layoutsImageCardsSelect?.imageCardsSelectOptions.notifyOnChanges(); |
|||
this.shapesImageCardsSelect?.imageCardsSelectOptions.notifyOnChanges(); |
|||
}); |
|||
} |
|||
|
|||
createShape(svg: string, layout: LevelCardLayout): SafeUrl { |
|||
return createShapeLayout(svg, layout, this.sanitizer); |
|||
} |
|||
|
|||
public fetchOptions(searchText: string): Observable<Array<string>> { |
|||
if (this.keySearchText !== searchText || !this.lastFetchedKeys) { |
|||
this.keySearchText = searchText; |
|||
const dataKeyFilter = optionsFilter(this.keySearchText); |
|||
return this.getKeys().pipe( |
|||
tap(res => this.lastFetchedKeys !== res ? this.lastFetchedKeys = res : []), |
|||
map(name => name?.filter(dataKeyFilter).map(key => key.name)), |
|||
tap(res => this.latestKeySearchTextResult = res) |
|||
); |
|||
} |
|||
return of(this.latestKeySearchTextResult); |
|||
} |
|||
|
|||
private getKeys(): Observable<Array<DataKey>> { |
|||
let fetchObservable: Observable<Array<DataKey>>; |
|||
const callbacks = this.widgetConfigComponent.widgetConfigCallbacks; |
|||
if (this.datasource?.type === DatasourceType.function) { |
|||
fetchObservable = of(this.functionTypeKeys); |
|||
} else if (this.datasource?.type === DatasourceType.entity && this.datasource?.entityAliasId || |
|||
this.datasource?.type === DatasourceType.device && this.datasource?.deviceId) { |
|||
if (this.datasource?.type === DatasourceType.device) { |
|||
if (this.lastKeysId !== this.datasource?.deviceId || !this.lastFetchedKeys) { |
|||
this.lastKeysId = this.datasource.deviceId; |
|||
fetchObservable = callbacks.fetchEntityKeysForDevice(this.datasource?.deviceId, [DataKeyType.attribute]); |
|||
} else { |
|||
fetchObservable = of(this.lastFetchedKeys); |
|||
} |
|||
} else { |
|||
if (this.lastKeysId !== this.datasource?.entityAliasId || !this.lastFetchedKeys) { |
|||
this.lastKeysId = this.datasource.entityAliasId; |
|||
fetchObservable = callbacks.fetchEntityKeys(this.datasource?.entityAliasId, [DataKeyType.attribute]); |
|||
} else { |
|||
fetchObservable = of(this.lastFetchedKeys); |
|||
} |
|||
} |
|||
} else { |
|||
this.lastKeysId = null; |
|||
fetchObservable = of([]); |
|||
} |
|||
return fetchObservable.pipe( |
|||
share({ |
|||
connector: () => new ReplaySubject(1), |
|||
resetOnError: false, |
|||
resetOnComplete: false, |
|||
resetOnRefCountZero: false |
|||
}) |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2023 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<div class="tb-liquid-level-panel" [style]="backgroundStyle" [class.tb-liquid-level-pointer]="hasCardClickAction" (click)="cardClick($event)"> |
|||
<div class="tb-liquid-level-overlay" [style]="overlayStyle"></div> |
|||
<ng-container *ngTemplateOutlet="widgetTitlePanel"></ng-container> |
|||
<div #liquidLevelContent class="tb-liquid-level-content"></div> |
|||
<div *ngIf="errorsMsg.length" class="tb-liquid-level-error"> |
|||
<div class="tb-liquid-level-error-container"> |
|||
<div *ngFor="let error of errorsMsg"> |
|||
{{ error }} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,72 @@ |
|||
/** |
|||
* Copyright © 2016-2023 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
.tb-liquid-level-panel { |
|||
width: 100%; |
|||
height: 100%; |
|||
position: relative; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 16px; |
|||
padding: 20px 24px 24px 24px; |
|||
&.tb-liquid-level-pointer { |
|||
cursor: pointer; |
|||
} |
|||
> div:not(.tb-liquid-level-overlay) { |
|||
z-index: 1; |
|||
} |
|||
.tb-liquid-level-overlay { |
|||
position: absolute; |
|||
top: 12px; |
|||
left: 12px; |
|||
bottom: 12px; |
|||
right: 12px; |
|||
} |
|||
div.tb-widget-title { |
|||
padding: 0; |
|||
} |
|||
.tb-liquid-level-content { |
|||
min-height: 0; |
|||
flex: 1; |
|||
display: flex; |
|||
justify-content: center; |
|||
} |
|||
.tb-liquid-level-error { |
|||
position: absolute; |
|||
display: flex; |
|||
inset: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
align-items: center; |
|||
justify-content: center; |
|||
|
|||
&-container { |
|||
border-radius: 4px; |
|||
margin: 16px; |
|||
padding: 8px 12px; |
|||
background: #848484; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 8px; |
|||
|
|||
& > * { |
|||
font-weight: 500; |
|||
letter-spacing: .25px; |
|||
text-align: center; |
|||
color: #fff; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,575 @@ |
|||
///
|
|||
/// Copyright © 2016-2023 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { |
|||
ChangeDetectorRef, |
|||
Component, |
|||
ElementRef, |
|||
Input, |
|||
OnInit, |
|||
TemplateRef, |
|||
ViewChild, |
|||
ViewEncapsulation |
|||
} from '@angular/core'; |
|||
import { WidgetContext } from '@home/models/widget-component.models'; |
|||
import { isDefined, isDefinedAndNotNull, isEmptyStr, isNotEmptyStr, isNumeric, isUndefinedOrNull } from '@core/utils'; |
|||
import { |
|||
CapacityUnits, |
|||
ConversionType, |
|||
convertLiters, |
|||
createAbsoluteLayout, |
|||
createPercentLayout, |
|||
extractValue, |
|||
levelCardDefaultSettings, |
|||
LevelCardLayout, |
|||
LevelCardWidgetSettings, |
|||
LiquidWidgetDataSourceType, |
|||
Shapes, |
|||
SvgInfo, |
|||
SvgLimits, |
|||
svgMapping |
|||
} from '@home/components/widget/lib/indicator/liquid-level-widget.models'; |
|||
import { forkJoin, Observable, of } from 'rxjs'; |
|||
import { map, switchMap } from 'rxjs/operators'; |
|||
import { EntityId } from '@shared/models/id/entity-id'; |
|||
import { |
|||
backgroundStyle, |
|||
ColorProcessor, |
|||
ComponentStyle, |
|||
cssTextFromInlineStyle, |
|||
DateFormatProcessor, |
|||
inlineTextStyle, |
|||
overlayStyle |
|||
} from '@shared/models/widget-settings.models'; |
|||
import { ResourcesService } from '@core/services/resources.service'; |
|||
import { NULL_UUID } from '@shared/models/id/has-uuid'; |
|||
import { TranslateService } from '@ngx-translate/core'; |
|||
import ITooltipsterInstance = JQueryTooltipster.ITooltipsterInstance; |
|||
|
|||
@Component({ |
|||
selector: 'tb-liquid-level-widget', |
|||
templateUrl: './liquid-level-widget.component.html', |
|||
styleUrls: ['./liquid-level-widget.component.scss'], |
|||
encapsulation: ViewEncapsulation.None |
|||
}) |
|||
export class LiquidLevelWidgetComponent implements OnInit { |
|||
|
|||
@ViewChild('liquidLevelContent', {static: true}) |
|||
liquidLevelContent: ElementRef<HTMLElement>; |
|||
|
|||
@Input() |
|||
ctx: WidgetContext; |
|||
|
|||
@Input() |
|||
widgetTitlePanel: TemplateRef<any>; |
|||
|
|||
backgroundStyle: ComponentStyle = {}; |
|||
overlayStyle: ComponentStyle = {}; |
|||
|
|||
hasCardClickAction = false; |
|||
|
|||
errorsMsg: string[] = []; |
|||
|
|||
private svgParams: SvgInfo; |
|||
|
|||
private svg: JQuery<SVGElement>; |
|||
private tooltip: ITooltipsterInstance; |
|||
private overlayContainer: JQuery<HTMLElement>; |
|||
private shape: Shapes; |
|||
|
|||
private settings: LevelCardWidgetSettings; |
|||
|
|||
private tankColor: ColorProcessor; |
|||
private valueColor: ColorProcessor; |
|||
private liquidColor: ColorProcessor; |
|||
private backgroundOverlayColor: ColorProcessor; |
|||
private tooltipLevelColor: ColorProcessor; |
|||
|
|||
private tooltipDateFormat: DateFormatProcessor; |
|||
|
|||
private volume: number; |
|||
private tooltipContent: string; |
|||
private widgetUnits: string; |
|||
|
|||
private capacityUnits = Object.values(CapacityUnits); |
|||
|
|||
constructor(private cd: ChangeDetectorRef, |
|||
private resourcesService: ResourcesService, |
|||
private translate: TranslateService) { |
|||
} |
|||
|
|||
ngOnInit(): void { |
|||
this.ctx.$scope.liquidLevelWidget = this; |
|||
this.settings = {...levelCardDefaultSettings, ...this.ctx.settings}; |
|||
this.declareStyles(); |
|||
|
|||
this.backgroundStyle = backgroundStyle(this.settings.background); |
|||
this.overlayStyle = overlayStyle(this.settings.background.overlay); |
|||
|
|||
this.hasCardClickAction = this.ctx.actionsApi.getActionDescriptors('cardClick').length > 0; |
|||
|
|||
this.getData().subscribe(data => { |
|||
if (data) { |
|||
const { svg, volume, units } = data; |
|||
if (svg && isNotEmptyStr(svg) && this.liquidLevelContent.nativeElement) { |
|||
const jQueryContainerElement = $(this.liquidLevelContent.nativeElement); |
|||
jQueryContainerElement.html(svg); |
|||
this.svg = jQueryContainerElement.find('svg'); |
|||
this.createSVG(); |
|||
this.createValueElement(); |
|||
|
|||
if (this.settings.showTooltip && (this.settings.showTooltipLevel || this.settings.showTooltipDate)) { |
|||
this.createTooltip(); |
|||
} |
|||
} |
|||
|
|||
if (isDefined(volume) && !isNaN(Number(volume))) { |
|||
this.volume = Number(volume); |
|||
} |
|||
|
|||
if (units) { |
|||
this.widgetUnits = units; |
|||
} |
|||
|
|||
this.update(true); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private declareStyles(): void { |
|||
this.tankColor = ColorProcessor.fromSettings(this.settings.tankColor); |
|||
this.valueColor = ColorProcessor.fromSettings(this.settings.valueColor); |
|||
this.liquidColor = ColorProcessor.fromSettings(this.settings.liquidColor); |
|||
this.backgroundOverlayColor = ColorProcessor.fromSettings(this.settings.backgroundOverlayColor); |
|||
this.tooltipLevelColor = ColorProcessor.fromSettings(this.settings.tooltipLevelColor); |
|||
|
|||
this.tooltipDateFormat = DateFormatProcessor.fromSettings(this.ctx.$injector, this.settings.tooltipDateFormat); |
|||
} |
|||
|
|||
private getData(): Observable<{ svg: string; volume: number; units: string }> { |
|||
const entityId: EntityId = { |
|||
entityType: this.ctx.datasources[0].entityType, |
|||
id: this.ctx.datasources[0].entityId |
|||
}; |
|||
|
|||
return this.getShape(entityId).pipe( |
|||
switchMap(shape => { |
|||
this.shape = shape; |
|||
this.svgParams = svgMapping.get(shape); |
|||
if (this.svgParams) { |
|||
return forkJoin([ |
|||
this.resourcesService.loadJsonResource<string>(this.svgParams.svg), |
|||
this.getTankersParams(entityId) |
|||
]).pipe( |
|||
map(params => ({svg: params[0], ...params[1]})) |
|||
); |
|||
} |
|||
return of(null); |
|||
}) |
|||
); |
|||
} |
|||
|
|||
public onInit() { |
|||
const borderRadius = this.ctx.$widgetElement.css('borderRadius'); |
|||
this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; |
|||
this.cd.detectChanges(); |
|||
} |
|||
|
|||
public update(ignoreAnimation?: boolean) { |
|||
if (this.svg) { |
|||
this.updateData(ignoreAnimation); |
|||
} |
|||
} |
|||
|
|||
public destroy() { |
|||
|
|||
} |
|||
|
|||
private createSVG() { |
|||
if (this.svg) { |
|||
this.svg.css('display', 'block') |
|||
.css('overflow', 'hidden') |
|||
.css('width', '100%') |
|||
.css('height', '100%'); |
|||
} |
|||
} |
|||
|
|||
private updateData(ignoreAnimation?: boolean) { |
|||
const data = this.ctx.data[0]?.data[0]; |
|||
if (data && isDefinedAndNotNull(data[1])) { |
|||
const percentage = isNumeric(data[1]) ? this.convertInputData(data[1]) : 0; |
|||
this.updateSvg(percentage, ignoreAnimation); |
|||
this.updateValueElement(data[1], percentage); |
|||
|
|||
if (this.settings.showTooltip && (this.settings.showTooltipLevel || this.settings.showTooltipDate)) { |
|||
this.updateTooltip(data); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private createTooltip(): void { |
|||
this.tooltipContent = this.getTooltipContent(); |
|||
|
|||
import('tooltipster').then(() => { |
|||
if ($.tooltipster && this.tooltip) { |
|||
this.tooltip.destroy(); |
|||
this.tooltip = null; |
|||
} |
|||
this.tooltip = this.svg.tooltipster({ |
|||
contentAsHTML: true, |
|||
theme: 'tooltipster-shadow', |
|||
side: 'top', |
|||
delay: 10, |
|||
distance: this.settings.showTitle ? -33 : -63, |
|||
triggerClose: { |
|||
click: true, |
|||
tap: true, |
|||
scroll: true, |
|||
mouseleave: true |
|||
}, |
|||
animation: 'grow', |
|||
updateAnimation: null, |
|||
trackOrigin: true, |
|||
functionBefore: (instance, helper) => { |
|||
instance.content(this.tooltipContent); |
|||
}, |
|||
functionReady: (instance, helper) => { |
|||
const tooltipsterBoxStyles: JQuery.PlainObject = { |
|||
backgroundColor: this.settings.tooltipBackgroundColor, |
|||
backdropFilter: `blur(${this.settings.tooltipBackgroundBlur}px)`, |
|||
width: '100%', |
|||
height: '100%' |
|||
}; |
|||
|
|||
$(helper.tooltip).css('max-width', this.svg.width() + 'px'); |
|||
$(helper.tooltip).css('max-height', this.svg.height() + 'px'); |
|||
$(helper.tooltip).find('.tooltipster-box').css(tooltipsterBoxStyles); |
|||
$(helper.tooltip).find('.tooltipster-arrow').empty(); |
|||
|
|||
instance.reposition(); |
|||
} |
|||
}).tooltipster('instance'); |
|||
}); |
|||
} |
|||
|
|||
private createValueElement(): void { |
|||
const jQueryContainerElement = $(this.liquidLevelContent.nativeElement); |
|||
const containerOverlay = jQueryContainerElement.find('.container-overlay'); |
|||
const percentageOverlay = jQueryContainerElement.find('.percentage-overlay'); |
|||
const absoluteOverlay = jQueryContainerElement.find('.absolute-overlay'); |
|||
|
|||
if (this.settings.layout === LevelCardLayout.absolute && !this.errorsMsg.length) { |
|||
this.overlayContainer = absoluteOverlay; |
|||
percentageOverlay.css('visibility', 'hidden'); |
|||
if (!this.settings.showBackgroundOverlay) { |
|||
absoluteOverlay.css('visibility', 'hidden'); |
|||
} |
|||
} else if (this.settings.layout === LevelCardLayout.percentage && !this.errorsMsg.length) { |
|||
this.overlayContainer = percentageOverlay; |
|||
absoluteOverlay.css('visibility', 'hidden'); |
|||
if (!this.settings.showBackgroundOverlay) { |
|||
percentageOverlay.css('visibility', 'hidden'); |
|||
} |
|||
} else { |
|||
containerOverlay.css('visibility', 'hidden'); |
|||
} |
|||
|
|||
if (this.overlayContainer) { |
|||
this.overlayContainer.attr('fill', this.backgroundOverlayColor.color); |
|||
this.overlayContainer.removeAttr('fill-opacity'); |
|||
} |
|||
} |
|||
|
|||
private getShape(entityId: EntityId): Observable<Shapes> { |
|||
if (this.settings.tankSelectionType === LiquidWidgetDataSourceType.attribute && entityId.id !== NULL_UUID) { |
|||
return this.ctx.attributeService.getEntityAttributes(entityId, null, [this.settings.shapeAttributeName]) |
|||
.pipe(map(attributes => { |
|||
const shape = extractValue<Shapes>(attributes, this.settings.shapeAttributeName); |
|||
if (!shape || !svgMapping.has(shape)) { |
|||
this.createdErrorMgs(this.settings.shapeAttributeName, isUndefinedOrNull(shape) || isEmptyStr(shape)); |
|||
return this.settings.selectedShape; |
|||
} |
|||
return shape; |
|||
} |
|||
)); |
|||
} |
|||
return of(this.settings.selectedShape); |
|||
} |
|||
|
|||
private getTankersParams(entityId: EntityId): Observable<{ volume: number; units: string }> { |
|||
const isVolumeStatic = this.settings.layout !== LevelCardLayout.absolute |
|||
&& this.settings.datasourceUnits === CapacityUnits.percent |
|||
|| this.settings.volumeSource === LiquidWidgetDataSourceType.static; |
|||
const isUnitStatic = this.settings.layout !== LevelCardLayout.absolute || |
|||
this.settings.widgetUnitsSource === LiquidWidgetDataSourceType.static; |
|||
|
|||
const attributeKeys: string[] = []; |
|||
|
|||
if (!isVolumeStatic) { |
|||
attributeKeys.push(this.settings.volumeAttributeName); |
|||
} |
|||
|
|||
if (!isUnitStatic) { |
|||
attributeKeys.push(this.settings.widgetUnitsAttributeName); |
|||
} |
|||
|
|||
if (!attributeKeys.length || entityId.id === NULL_UUID) { |
|||
return of({ |
|||
volume: this.settings.volumeConstant, |
|||
units: this.settings.units |
|||
}); |
|||
} |
|||
|
|||
return this.ctx.attributeService.getEntityAttributes(entityId, null, attributeKeys).pipe( |
|||
map(attributes => { |
|||
let volume = isVolumeStatic ? this.settings.volumeConstant : extractValue<number>(attributes, this.settings.volumeAttributeName); |
|||
let units = isUnitStatic ? this.settings.units : extractValue<string>(attributes, this.settings.widgetUnitsAttributeName); |
|||
|
|||
if (!isVolumeStatic && (!volume || !isNumeric(volume) || volume < 0.1)) { |
|||
this.createdErrorMgs(this.settings.volumeAttributeName, isUndefinedOrNull(volume) || isEmptyStr(volume)); |
|||
volume = this.settings.volumeConstant; |
|||
} |
|||
|
|||
if (!isUnitStatic) { |
|||
if (isNotEmptyStr(units)) { |
|||
const normalizeUnits = units.normalize().trim(); |
|||
units = this.capacityUnits.find(unit => unit.normalize() === normalizeUnits); |
|||
} |
|||
if (isUndefinedOrNull(units) || !isNotEmptyStr(units)) { |
|||
this.createdErrorMgs(this.settings.widgetUnitsAttributeName, isUndefinedOrNull(units) || isEmptyStr(units)); |
|||
units = this.settings.units; |
|||
} |
|||
} |
|||
|
|||
return { |
|||
volume, |
|||
units |
|||
}; |
|||
}) |
|||
); |
|||
} |
|||
|
|||
private createdErrorMgs(attributeName: string, isEmpty = false) { |
|||
if (isEmpty) { |
|||
this.errorsMsg.push(this.translate.instant('widgets.liquid-level-card.attribute-key-not-set', {attributeName})); |
|||
} else { |
|||
this.errorsMsg.push(this.translate.instant('widgets.liquid-level-card.attribute-key-invalid', {attributeName})); |
|||
} |
|||
this.cd.markForCheck(); |
|||
} |
|||
|
|||
private updateSvg(percentage: number, ignoreAnimation?: boolean) { |
|||
const yLimits: SvgLimits = { |
|||
min: this.svgParams.limits.min, |
|||
max: this.svgParams.limits.max |
|||
}; |
|||
|
|||
const newYPos = this.calculatePosition(percentage, yLimits); |
|||
this.updateShapeColor(percentage); |
|||
this.updateLevel(newYPos, percentage, ignoreAnimation); |
|||
} |
|||
|
|||
private calculatePosition(percentage: number, limits: SvgLimits): number { |
|||
if (percentage >= 100) { |
|||
return limits.max; |
|||
} if (percentage <= 0) { |
|||
return limits.min; |
|||
} |
|||
return limits.min + (percentage / 100) * (limits.max - limits.min); |
|||
} |
|||
|
|||
private updateTooltip(value: [number, any]): void { |
|||
this.tooltipContent = this.getTooltipContent(value); |
|||
|
|||
if (this.tooltip) { |
|||
this.tooltip.content(this.tooltipContent); |
|||
} |
|||
} |
|||
|
|||
private updateLevel(newY: number, percentage: number, ignoreAnimation = false): void { |
|||
this.liquidColor.update(percentage); |
|||
const jQueryContainerElement = $(this.liquidLevelContent.nativeElement); |
|||
const fill = jQueryContainerElement.find('.tb-liquid-fill'); |
|||
const surfaces = jQueryContainerElement.find('.tb-liquid-surface'); |
|||
const surfacePositionAttr = this.shape !== Shapes.vCylinder ? 'y' : 'cy'; |
|||
const animationSpeed = 500; |
|||
|
|||
const levelColor = this.errorsMsg.length ? 'transparent' : this.liquidColor.color; |
|||
|
|||
if (ignoreAnimation) { |
|||
fill.css({y : newY}); |
|||
} else { |
|||
fill.animate({y : newY}, animationSpeed); |
|||
} |
|||
fill.attr('fill', levelColor); |
|||
|
|||
surfaces.each((index, element) => { |
|||
const $element = $(element); |
|||
if (ignoreAnimation) { |
|||
$element.css({[surfacePositionAttr]: newY}); |
|||
} else { |
|||
$element.animate({[surfacePositionAttr]: newY}, animationSpeed); |
|||
} |
|||
if ($element.hasClass('tb-liquid')) { |
|||
$element.attr('fill', levelColor); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private updateShapeColor(value: number): void { |
|||
const jQueryContainerElement = $(this.liquidLevelContent.nativeElement); |
|||
const shapeStrokes = jQueryContainerElement.find('.tb-shape-stroke'); |
|||
const shapeFill = jQueryContainerElement.find('.tb-shape-fill'); |
|||
this.tankColor.update(value); |
|||
|
|||
const shapeColor = this.errorsMsg.length ? '#CACACA' : this.tankColor.color; |
|||
|
|||
shapeStrokes.each((index, element) => { |
|||
$(element).attr('stroke', shapeColor); |
|||
}); |
|||
|
|||
shapeFill.each((index, element) => { |
|||
$(element).attr('fill', shapeColor); |
|||
}); |
|||
} |
|||
|
|||
private updateValueElement(data: any, percentage: number): void { |
|||
let content: string; |
|||
let container: JQuery<HTMLElement>; |
|||
const jQueryContainerElement = $(this.liquidLevelContent.nativeElement); |
|||
let value = 'N/A'; |
|||
|
|||
if (isNumeric(data)) { |
|||
value = convertLiters(this.convertOutputData(percentage), this.widgetUnits as CapacityUnits, ConversionType.from) |
|||
.toFixed(this.settings.decimals || 0); |
|||
} |
|||
this.valueColor.update(value); |
|||
const valueTextStyle = cssTextFromInlineStyle({...inlineTextStyle(this.settings.valueFont), |
|||
color: this.valueColor.color}); |
|||
this.backgroundOverlayColor.update(percentage); |
|||
if (this.overlayContainer) { |
|||
this.overlayContainer.attr('fill', this.backgroundOverlayColor.color); |
|||
} |
|||
|
|||
if (this.settings.layout === LevelCardLayout.absolute) { |
|||
const volumeInLiters: number = convertLiters(this.volume, this.settings.volumeUnits as CapacityUnits, ConversionType.to); |
|||
const volume = convertLiters(volumeInLiters, this.widgetUnits as CapacityUnits, ConversionType.from) |
|||
.toFixed(this.settings.decimals || 0); |
|||
|
|||
const volumeTextStyle = cssTextFromInlineStyle({...inlineTextStyle(this.settings.volumeFont), |
|||
color: this.settings.volumeColor}); |
|||
|
|||
container = jQueryContainerElement.find('.absolute-value-container'); |
|||
content = createAbsoluteLayout({inputValue: value, volume}, |
|||
{valueStyle: valueTextStyle, volumeStyle: volumeTextStyle}, this.widgetUnits); |
|||
|
|||
} else if (this.settings.layout === LevelCardLayout.percentage) { |
|||
container = jQueryContainerElement.find('.percentage-value-container'); |
|||
content = createPercentLayout(value, valueTextStyle); |
|||
} |
|||
|
|||
if (content && container) { |
|||
container.html(content); |
|||
} |
|||
} |
|||
|
|||
private getTooltipContent(value?: [number, any]): string { |
|||
const contentValue = value || [0, '']; |
|||
let tooltipValue: string | number = 'N/A'; |
|||
|
|||
if (isNumeric(contentValue[1])) { |
|||
tooltipValue = this.convertTooltipData(contentValue[1]); |
|||
} |
|||
|
|||
this.tooltipLevelColor.update(tooltipValue); |
|||
this.tooltipDateFormat.update(contentValue[0]); |
|||
|
|||
const levelTextStyle = cssTextFromInlineStyle({...inlineTextStyle(this.settings.tooltipLevelFont), |
|||
color: this.tooltipLevelColor.color}); |
|||
|
|||
const dateTextStyle = cssTextFromInlineStyle({...inlineTextStyle(this.settings.tooltipDateFont), |
|||
color: this.settings.tooltipDateColor, overflow: 'hidden', 'text-overflow': 'ellipsis', 'white-space': 'nowrap'}); |
|||
|
|||
let content = `<div style="display: flex; flex-direction: column;
|
|||
gap: 8px; background-color: transparent">`;
|
|||
|
|||
if (this.settings.showTooltipLevel) { |
|||
const levelValue = typeof tooltipValue == 'number' |
|||
? `${tooltipValue.toFixed(this.settings.tooltipLevelDecimals)} ${this.settings.tooltipUnits}` |
|||
: 'N/A'; |
|||
content += this.createTooltipContent( |
|||
this.ctx.translate.instant('widgets.liquid-level-card.level'), |
|||
levelValue, |
|||
levelTextStyle |
|||
); |
|||
} |
|||
|
|||
if (this.settings.showTooltipDate) { |
|||
content += this.createTooltipContent( |
|||
this.ctx.translate.instant('widgets.liquid-level-card.last-update'), |
|||
this.tooltipDateFormat.formatted, |
|||
dateTextStyle |
|||
); |
|||
} |
|||
|
|||
content += '</div>'; |
|||
|
|||
return content; |
|||
} |
|||
|
|||
private createTooltipContent(labelText: string, contentValue: string, textStyle: string): string { |
|||
return `<div style="display: flex; justify-content: space-between; gap: 8px; align-items: baseline">
|
|||
<label style="color: rgba(0, 0, 0, 0.38); font-size: 12px; font-weight: 400; white-space: nowrap;"> |
|||
${labelText} |
|||
</label> |
|||
<label style="${textStyle}"> |
|||
${contentValue} |
|||
</label> |
|||
</div>`;
|
|||
} |
|||
|
|||
private convertInputData(value: number): number { |
|||
if (this.settings.datasourceUnits !== CapacityUnits.percent) { |
|||
return (convertLiters(value, this.settings.datasourceUnits, ConversionType.to) / |
|||
convertLiters(this.volume, this.settings.volumeUnits, ConversionType.to)) * 100; |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
private convertOutputData(value: number): number { |
|||
if (this.widgetUnits !== CapacityUnits.percent) { |
|||
return convertLiters(this.volume * (value / 100), this.settings.volumeUnits, ConversionType.to); |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
private convertTooltipData(value: number): number { |
|||
const percentage = this.convertInputData(value); |
|||
if (this.settings.tooltipUnits !== CapacityUnits.percent) { |
|||
const liters = this.convertOutputData(percentage); |
|||
|
|||
return convertLiters(liters, this.settings.tooltipUnits, ConversionType.from); |
|||
} else { |
|||
return percentage; |
|||
} |
|||
} |
|||
|
|||
public cardClick($event: Event) { |
|||
this.ctx.actionsApi.cardClick($event); |
|||
} |
|||
} |
|||
@ -0,0 +1,623 @@ |
|||
///
|
|||
/// Copyright © 2016-2023 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { |
|||
BackgroundSettings, |
|||
BackgroundType, |
|||
ColorSettings, |
|||
constantColor, |
|||
cssTextFromInlineStyle, |
|||
DateFormatSettings, |
|||
Font, |
|||
lastUpdateAgoDateFormat |
|||
} from '@shared/models/widget-settings.models'; |
|||
import { DataKey, WidgetConfig } from '@shared/models/widget.models'; |
|||
import { AttributeData, DataKeyType } from '@shared/models/telemetry/telemetry.models'; |
|||
import { forkJoin, Observable, of } from 'rxjs'; |
|||
import { singleEntityFilterFromDeviceId } from '@shared/models/query/query.models'; |
|||
import { EntityType } from '@shared/models/entity-type.models'; |
|||
import { catchError, map, mergeMap } from 'rxjs/operators'; |
|||
import { EntityService } from '@core/http/entity.service'; |
|||
import { IAliasController } from '@core/api/widget-api.models'; |
|||
import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; |
|||
import { ResourcesService } from '@core/services/resources.service'; |
|||
import { FormGroup } from '@angular/forms'; |
|||
|
|||
export interface SvgInfo { |
|||
svg: string; |
|||
limits: SvgLimits; |
|||
} |
|||
|
|||
export interface SvgLimits { |
|||
min: number; |
|||
max: number; |
|||
} |
|||
|
|||
export interface LevelCardWidgetSettings extends WidgetConfig { |
|||
tankSelectionType: LiquidWidgetDataSourceType; |
|||
selectedShape: Shapes; |
|||
shapeAttributeName: string; |
|||
tankColor: ColorSettings; |
|||
datasourceUnits: CapacityUnits; |
|||
layout: LevelCardLayout; |
|||
volumeSource: LiquidWidgetDataSourceType; |
|||
volumeConstant: number; |
|||
volumeAttributeName: string; |
|||
volumeUnits: CapacityUnits; |
|||
volumeFont: Font; |
|||
volumeColor: string; |
|||
liquidColor: ColorSettings; |
|||
valueFont: Font; |
|||
widgetUnitsSource: LiquidWidgetDataSourceType; |
|||
widgetUnitsAttributeName: string; |
|||
valueColor: ColorSettings; |
|||
showBackgroundOverlay: boolean; |
|||
backgroundOverlayColor: ColorSettings; |
|||
showTooltip: boolean; |
|||
showTooltipLevel: boolean; |
|||
tooltipUnits: CapacityUnits; |
|||
tooltipLevelDecimals: number; |
|||
tooltipLevelFont: Font; |
|||
tooltipLevelColor: ColorSettings; |
|||
showTooltipDate: boolean; |
|||
tooltipDateFormat: DateFormatSettings; |
|||
tooltipDateFont: Font; |
|||
tooltipDateColor: string; |
|||
tooltipBackgroundColor: string; |
|||
tooltipBackgroundBlur: number; |
|||
background: BackgroundSettings; |
|||
} |
|||
|
|||
export enum Shapes { |
|||
vOval = 'Vertical Oval', |
|||
vCylinder = 'Vertical Cylinder', |
|||
vCapsule = 'Vertical Capsule', |
|||
rectangle = 'Rectangle', |
|||
hOval = 'Horizontal Oval', |
|||
hEllipse = 'Horizontal Ellipse', |
|||
hDishEnds = 'Horizontal Dish Ends', |
|||
hCylinder = 'Horizontal Cylinder', |
|||
hCapsule = 'Horizontal Capsule', |
|||
hElliptical_2_1 = 'Horizontal 2:1 Elliptical' |
|||
} |
|||
|
|||
export enum CapacityUnits { |
|||
percent = '%', |
|||
liters = 'L', |
|||
cubicMillimeter = 'mm³', |
|||
cubicCentimeter = 'cm³', |
|||
cubicMeter = 'm³', |
|||
cubicKilometer = 'km³', |
|||
milliliter = 'mL', |
|||
hectoliter = 'hl', |
|||
cubicInch = 'in³', |
|||
cubicFoot = 'ft³', |
|||
cubicYard = 'yd³', |
|||
fluidOunce = 'fl-oz', |
|||
pint = 'pt', |
|||
quart = 'qt', |
|||
gallon = 'gal', |
|||
oilBarrels = 'bbl' |
|||
} |
|||
|
|||
export enum LevelCardLayout { |
|||
simple = 'simple', |
|||
percentage = 'percentage', |
|||
absolute = 'absolute' |
|||
} |
|||
|
|||
export enum LiquidWidgetDataSourceType { |
|||
static = 'static', |
|||
attribute = 'attribute' |
|||
} |
|||
|
|||
export enum ConversionType { |
|||
to = 'to', |
|||
from = 'from', |
|||
} |
|||
|
|||
export const svgMapping = new Map<Shapes, SvgInfo>( |
|||
[ |
|||
[ |
|||
Shapes.vOval, |
|||
{ |
|||
svg: 'assets/widget/liquid-level/shapes/vertical-oval.svg', |
|||
limits: { min: 171, max: 51 } |
|||
} |
|||
], |
|||
[ |
|||
Shapes.vCylinder, |
|||
{ |
|||
svg: 'assets/widget/liquid-level/shapes/vertical-cylinder.svg', |
|||
limits: { min: 161, max: 55 } |
|||
} |
|||
], |
|||
[ |
|||
Shapes.vCapsule, |
|||
{ |
|||
svg: 'assets/widget/liquid-level/shapes/vertical-capsule.svg', |
|||
limits: { min: 197, max: 25 } |
|||
} |
|||
], |
|||
[ |
|||
Shapes.rectangle, |
|||
{ |
|||
svg: 'assets/widget/liquid-level/shapes/rectangle.svg', |
|||
limits: { min: 169, max: 52 } |
|||
} |
|||
], |
|||
[ |
|||
Shapes.hOval, |
|||
{ |
|||
svg: 'assets/widget/liquid-level/shapes/horizontal-oval.svg', |
|||
limits: { min: 160, max: 63 } |
|||
} |
|||
], |
|||
[ |
|||
Shapes.hEllipse, |
|||
{ |
|||
svg: 'assets/widget/liquid-level/shapes/horizontal-ellipse.svg', |
|||
limits: { min: 160, max: 64 } |
|||
} |
|||
], |
|||
[ |
|||
Shapes.hDishEnds, |
|||
{ |
|||
svg: 'assets/widget/liquid-level/shapes/horizontal-dish-ends.svg', |
|||
limits: { min: 173, max: 50 } |
|||
} |
|||
], |
|||
[ |
|||
Shapes.hCylinder, |
|||
{ |
|||
svg: 'assets/widget/liquid-level/shapes/horizontal-cylinder.svg', |
|||
limits: { min: 165, max: 56 } |
|||
} |
|||
], |
|||
[ |
|||
Shapes.hCapsule, |
|||
{ |
|||
svg: 'assets/widget/liquid-level/shapes/horizontal-capsule.svg', |
|||
limits: { min: 171, max: 52 } |
|||
} |
|||
], |
|||
[ |
|||
Shapes.hElliptical_2_1, |
|||
{ |
|||
svg: 'assets/widget/liquid-level/shapes/horizontal-2_1-elliptical.svg', |
|||
limits: { min: 173, max: 50 } |
|||
} |
|||
] |
|||
] |
|||
); |
|||
|
|||
export const levelCardLayoutTranslations = new Map<LevelCardLayout, string>( |
|||
[ |
|||
[LevelCardLayout.simple, 'widgets.liquid-level-card.layout-simple'], |
|||
[LevelCardLayout.percentage, 'widgets.liquid-level-card.layout-percentage'], |
|||
[LevelCardLayout.absolute, 'widgets.liquid-level-card.layout-absolute'] |
|||
] |
|||
); |
|||
|
|||
export const LiquidWidgetDataSourceTypeTranslations = new Map<LiquidWidgetDataSourceType, string>( |
|||
[ |
|||
[LiquidWidgetDataSourceType.static, 'widgets.liquid-level-card.static'], |
|||
[LiquidWidgetDataSourceType.attribute, 'widgets.liquid-level-card.attribute'] |
|||
] |
|||
); |
|||
|
|||
export const ShapesTranslations = new Map<Shapes, string>( |
|||
[ |
|||
[Shapes.vOval, 'widgets.liquid-level-card.v-oval'], |
|||
[Shapes.vCylinder, 'widgets.liquid-level-card.v-cylinder'], |
|||
[Shapes.vCapsule, 'widgets.liquid-level-card.v-capsule'], |
|||
[Shapes.rectangle, 'widgets.liquid-level-card.rectangle'], |
|||
[Shapes.hOval, 'widgets.liquid-level-card.h-oval'], |
|||
[Shapes.hEllipse, 'widgets.liquid-level-card.h-ellipse'], |
|||
[Shapes.hDishEnds, 'widgets.liquid-level-card.h-dish-ends'], |
|||
[Shapes.hCylinder, 'widgets.liquid-level-card.h-cylinder'], |
|||
[Shapes.hCapsule, 'widgets.liquid-level-card.h-capsule'], |
|||
[Shapes.hElliptical_2_1, 'widgets.liquid-level-card.h-elliptical_2_1'] |
|||
] |
|||
); |
|||
|
|||
export const levelCardDefaultSettings: LevelCardWidgetSettings = { |
|||
tankSelectionType: LiquidWidgetDataSourceType.static, |
|||
selectedShape: Shapes.vCylinder, |
|||
shapeAttributeName: 'tankShape', |
|||
tankColor: constantColor('#242770'), |
|||
datasourceUnits: CapacityUnits.percent, |
|||
layout: LevelCardLayout.percentage, |
|||
showTitle: false, |
|||
title: 'Liquid level', |
|||
titleFont: { |
|||
family: 'Roboto', |
|||
size: 16, |
|||
sizeUnit: 'px', |
|||
style: 'normal', |
|||
weight: '500', |
|||
lineHeight: '1.5' |
|||
}, |
|||
titleColor: 'rgba(0, 0, 0, 0.87)', |
|||
showTitleIcon: false, |
|||
titleIcon: 'water_drop', |
|||
iconColor: '#5469FF', |
|||
volumeSource: LiquidWidgetDataSourceType.static, |
|||
volumeConstant: 500, |
|||
volumeUnits: CapacityUnits.liters, |
|||
volumeAttributeName: 'volume', |
|||
volumeFont: { |
|||
family: 'Roboto', |
|||
size: 14, |
|||
sizeUnit: 'px', |
|||
style: 'normal', |
|||
weight: '500', |
|||
lineHeight: '100%' |
|||
}, |
|||
volumeColor: 'rgba(0, 0, 0, 0.18)', |
|||
units: CapacityUnits.percent as string, |
|||
widgetUnitsSource: LiquidWidgetDataSourceType.static, |
|||
widgetUnitsAttributeName: 'units', |
|||
decimals: 0, |
|||
liquidColor: constantColor('#7A8BFF'), |
|||
valueFont: { |
|||
family: 'Roboto', |
|||
size: 24, |
|||
sizeUnit: 'px', |
|||
style: 'normal', |
|||
weight: '500', |
|||
lineHeight: '100%' |
|||
}, |
|||
valueColor: constantColor('rgba(0, 0, 0, 0.87)'), |
|||
showBackgroundOverlay: true, |
|||
backgroundOverlayColor: constantColor('rgba(255, 255, 255, 0.76)'), |
|||
showTooltip: true, |
|||
showTooltipLevel: true, |
|||
tooltipUnits: CapacityUnits.percent, |
|||
tooltipLevelDecimals: 0, |
|||
tooltipLevelFont: { |
|||
family: 'Roboto', |
|||
size: 13, |
|||
sizeUnit: 'px', |
|||
style: 'normal', |
|||
weight: '500', |
|||
lineHeight: '100%' |
|||
}, |
|||
tooltipLevelColor: constantColor('rgba(0, 0, 0, 0.76)'), |
|||
showTooltipDate: true, |
|||
tooltipDateFormat: lastUpdateAgoDateFormat(), |
|||
tooltipDateFont: { |
|||
family: 'Roboto', |
|||
size: 13, |
|||
sizeUnit: 'px', |
|||
style: 'normal', |
|||
weight: '500', |
|||
lineHeight: '100%' |
|||
}, |
|||
tooltipDateColor: 'rgba(0, 0, 0, 0.76)', |
|||
tooltipBackgroundColor: 'rgba(255, 255, 255, 0.76)', |
|||
tooltipBackgroundBlur: 3, |
|||
background: { |
|||
type: BackgroundType.color, |
|||
color: '#fff', |
|||
overlay: { |
|||
enabled: false, |
|||
color: 'rgba(255,255,255,0.72)', |
|||
blur: 3 |
|||
} |
|||
} |
|||
}; |
|||
|
|||
export const convertLiters = (value: number, units: CapacityUnits, conversionType: ConversionType): number => { |
|||
let factor: number; |
|||
|
|||
switch (units) { |
|||
case CapacityUnits.liters: |
|||
return value; |
|||
case CapacityUnits.cubicMillimeter: |
|||
factor = 1e6; |
|||
break; |
|||
case CapacityUnits.cubicCentimeter: |
|||
case CapacityUnits.milliliter: |
|||
factor = 1e3; |
|||
break; |
|||
case CapacityUnits.cubicMeter: |
|||
factor = 1e-3; |
|||
break; |
|||
case CapacityUnits.cubicKilometer: |
|||
factor = 1e-15; |
|||
break; |
|||
case CapacityUnits.hectoliter: |
|||
factor = 0.01; |
|||
break; |
|||
case CapacityUnits.cubicInch: |
|||
factor = 61.0237; |
|||
break; |
|||
case CapacityUnits.cubicFoot: |
|||
factor = 1 / 28.3168; |
|||
break; |
|||
case CapacityUnits.cubicYard: |
|||
factor = 1 / 764.555; |
|||
break; |
|||
case CapacityUnits.fluidOunce: |
|||
factor = 33.814; |
|||
break; |
|||
case CapacityUnits.pint: |
|||
factor = 1 / 0.473176; |
|||
break; |
|||
case CapacityUnits.quart: |
|||
factor = 1 / 0.946353; |
|||
break; |
|||
case CapacityUnits.gallon: |
|||
factor = 1 / 3.78541; |
|||
break; |
|||
case CapacityUnits.oilBarrels: |
|||
factor = 1 / 158.987; |
|||
break; |
|||
default: |
|||
return value; |
|||
// throw new Error(`Unknown unit: ${units}`);
|
|||
} |
|||
|
|||
return conversionType === ConversionType.to ? value / factor : value * factor; |
|||
}; |
|||
|
|||
export const extractValue = <T>(attributes: Array<AttributeData>, attributeName: string): T | undefined => { |
|||
return attributes.find(attr => attr.key === attributeName)?.value; |
|||
}; |
|||
|
|||
export const valueContainerStyleDefaults = cssTextFromInlineStyle({ |
|||
width: '100%', |
|||
height: '100%', |
|||
display: 'flex', |
|||
gap: '8px', |
|||
'text-align': 'center', |
|||
'align-items': 'center', |
|||
'align-content': 'center', |
|||
'justify-content': 'center', |
|||
'font-family': 'Roboto, Helvetica Neue, sans-serif' |
|||
}); |
|||
|
|||
export const valueTextStyleDefaults = 'letter-spacing: normal; font-style: normal; font-weight: 500; line-height: 100%;' + |
|||
' font-size: 24px; font-family: Roboto, Helvetica Neue, sans-serif; color: #000000DE'; |
|||
|
|||
export const volumeTextStyleDefaults = 'letter-spacing: normal; font-style: normal; font-weight: 500;' + |
|||
' line-height: 100%; font-size: 14px; font-family: Roboto, Helvetica Neue, sans-serif; color: rgba(0, 0, 0, 0.18)'; |
|||
|
|||
export const createAbsoluteLayout = (values?: {inputValue: number | string; volume: number | string}, |
|||
styles?: {valueStyle: string; volumeStyle: string}, |
|||
units?: string): string => { |
|||
const inputValue = values?.inputValue ? values?.inputValue : 500; |
|||
const valueTextStyle = styles?.valueStyle ? styles?.valueStyle : valueTextStyleDefaults; |
|||
const volume = values?.volume ? values?.volume : 1000; |
|||
const volumeTextStyle = styles?.volumeStyle ? styles?.volumeStyle : volumeTextStyleDefaults; |
|||
const displayUnits = units ? units : CapacityUnits.liters; |
|||
|
|||
return `<div xmlns="http://www.w3.org/1999/xhtml" style="${valueContainerStyleDefaults}">
|
|||
<div style="display: flex; flex-direction: column; justify-content: center; align-content: center;"> |
|||
<label style="${valueTextStyle}">${inputValue}</label> |
|||
<div style="border-top: 1px solid rgba(0, 0, 0, 0.38); height: 1px; width: 100%; margin: 3px 0"></div> |
|||
<label style="${volumeTextStyle}">${volume}</label> |
|||
</div> |
|||
<label style="${valueTextStyle}">${displayUnits}</label> |
|||
</div>`;
|
|||
}; |
|||
|
|||
export const createPercentLayout = (value: number | string = 50, valueTextStyle: string = valueTextStyleDefaults): string => |
|||
`<div xmlns="http://www.w3.org/1999/xhtml" style="${valueContainerStyleDefaults}">
|
|||
<label style="${valueTextStyle}">${value} ${CapacityUnits.percent}</label> |
|||
</div>`;
|
|||
|
|||
export const optionsFilter = (searchText: string): ((key: DataKey) => boolean) => |
|||
key => key?.name.toUpperCase().includes(searchText?.toUpperCase()); |
|||
|
|||
export const fetchEntityKeysForDevice = (deviceId: string, dataKeyTypes: Array<DataKeyType>, |
|||
entityService: EntityService): Observable<Array<DataKey>> => { |
|||
const entityFilter = singleEntityFilterFromDeviceId(deviceId); |
|||
return entityService.getEntityKeysByEntityFilter( |
|||
entityFilter, |
|||
dataKeyTypes, [EntityType.DEVICE], |
|||
{ignoreLoading: true, ignoreErrors: true} |
|||
).pipe( |
|||
catchError(() => of([])) |
|||
); |
|||
}; |
|||
|
|||
export const fetchEntityKeys = (entityAliasId: string, dataKeyTypes: Array<DataKeyType>, |
|||
entityService: EntityService, aliasController: IAliasController): Observable<Array<DataKey>> => |
|||
aliasController.getAliasInfo(entityAliasId).pipe( |
|||
mergeMap((aliasInfo) => entityService.getEntityKeysByEntityFilter( |
|||
aliasInfo.entityFilter, |
|||
dataKeyTypes, [], |
|||
{ignoreLoading: true, ignoreErrors: true} |
|||
)), |
|||
catchError(() => of([] as Array<DataKey>)) |
|||
); |
|||
|
|||
|
|||
export const createShapeLayout = (svg: string, layout: LevelCardLayout, sanitizer: DomSanitizer): SafeUrl => { |
|||
if (svg && layout) { |
|||
const parser = new DOMParser(); |
|||
const svgImage = parser.parseFromString(svg, 'image/svg+xml'); |
|||
|
|||
if (layout === LevelCardLayout.simple) { |
|||
svgImage.querySelector('.container-overlay').remove(); |
|||
} else if (layout === LevelCardLayout.percentage) { |
|||
svgImage.querySelector('.absolute-overlay').remove(); |
|||
svgImage.querySelector('.percentage-value-container').innerHTML = createPercentLayout(); |
|||
} else { |
|||
svgImage.querySelector('.absolute-value-container').innerHTML = createAbsoluteLayout(); |
|||
svgImage.querySelector('.percentage-overlay').remove(); |
|||
} |
|||
|
|||
const encodedSvg = encodeURIComponent(svgImage.documentElement.outerHTML); |
|||
|
|||
return sanitizer.bypassSecurityTrustResourceUrl(`data:image/svg+xml,${encodedSvg}`); |
|||
} |
|||
}; |
|||
|
|||
export const loadSvgShapesMapping = (resourcesService: ResourcesService): Observable<Map<Shapes, string>> => { |
|||
const obsArray: Array<Observable<{svg: string; shape: Shapes}>> = []; |
|||
const shapesImageMap: Map<Shapes, string> = new Map(); |
|||
svgMapping.forEach((value, shape) => { |
|||
const obs = resourcesService.loadJsonResource<string>(value.svg).pipe( |
|||
map((svg) => ({svg, shape})) |
|||
); |
|||
|
|||
obsArray.push(obs); |
|||
}); |
|||
|
|||
return forkJoin(obsArray).pipe( |
|||
map(svgData => { |
|||
for (const data of svgData) { |
|||
shapesImageMap.set(data.shape, data.svg); |
|||
} |
|||
return shapesImageMap; |
|||
}) |
|||
); |
|||
}; |
|||
|
|||
export const updatedFormSettingsValidators = (formGroup: FormGroup) => { |
|||
const datasourceUnits: string = formGroup.get('datasourceUnits').value; |
|||
const layout: LevelCardLayout = formGroup.get('layout').value; |
|||
const volumeSource: LiquidWidgetDataSourceType = formGroup.get('volumeSource').value; |
|||
const widgetUnitsSource: LiquidWidgetDataSourceType = formGroup.get('widgetUnitsSource').value; |
|||
const showTooltipLevel: boolean = formGroup.get('showTooltipLevel').value; |
|||
const showTooltipDate: boolean = formGroup.get('showTooltipDate').value; |
|||
const showTooltip: boolean = formGroup.get('showTooltip').value; |
|||
const tankSelectionType: LiquidWidgetDataSourceType = formGroup.get('tankSelectionType').value; |
|||
|
|||
if (tankSelectionType === LiquidWidgetDataSourceType.static) { |
|||
formGroup.get('selectedShape').enable({emitEvent: false}); |
|||
formGroup.get('shapeAttributeName').disable({emitEvent: false}); |
|||
} else { |
|||
formGroup.get('selectedShape').disable({emitEvent: false}); |
|||
formGroup.get('shapeAttributeName').enable({emitEvent: false}); |
|||
} |
|||
|
|||
switch (layout) { |
|||
case LevelCardLayout.simple: |
|||
case LevelCardLayout.percentage: |
|||
formGroup.get('widgetUnitsSource').disable({emitEvent: false}); |
|||
formGroup.get('units').disable({emitEvent: false}); |
|||
formGroup.get('widgetUnitsAttributeName').disable({emitEvent: false}); |
|||
|
|||
if (datasourceUnits !== CapacityUnits.percent) { |
|||
formGroup.get('volumeSource').enable({emitEvent: false}); |
|||
formGroup.get('volumeUnits').enable({emitEvent: false}); |
|||
if (volumeSource === LiquidWidgetDataSourceType.static) { |
|||
formGroup.get('volumeConstant').enable({emitEvent: false}); |
|||
formGroup.get('volumeAttributeName').disable({emitEvent: false}); |
|||
} else { |
|||
formGroup.get('volumeConstant').disable({emitEvent: false}); |
|||
formGroup.get('volumeAttributeName').enable({emitEvent: false}); |
|||
} |
|||
} else { |
|||
formGroup.get('volumeSource').disable({emitEvent: false}); |
|||
formGroup.get('volumeConstant').disable({emitEvent: false}); |
|||
formGroup.get('volumeAttributeName').disable({emitEvent: false}); |
|||
formGroup.get('volumeUnits').disable({emitEvent: false}); |
|||
} |
|||
|
|||
if (layout === LevelCardLayout.simple) { |
|||
formGroup.get('decimals')?.disable({emitEvent: false}); |
|||
formGroup.get('valueFont').disable({emitEvent: false}); |
|||
formGroup.get('valueColor').disable({emitEvent: false}); |
|||
} else { |
|||
formGroup.get('decimals')?.enable({emitEvent: false}); |
|||
formGroup.get('valueFont').enable({emitEvent: false}); |
|||
formGroup.get('valueColor').enable({emitEvent: false}); |
|||
} |
|||
|
|||
formGroup.get('volumeFont').disable({emitEvent: false}); |
|||
formGroup.get('volumeColor').disable({emitEvent: false}); |
|||
|
|||
break; |
|||
case LevelCardLayout.absolute: |
|||
formGroup.get('widgetUnitsSource').enable({emitEvent: false}); |
|||
if (widgetUnitsSource === LiquidWidgetDataSourceType.static) { |
|||
formGroup.get('units').enable({emitEvent: false}); |
|||
formGroup.get('widgetUnitsAttributeName').disable({emitEvent: false}); |
|||
} else { |
|||
formGroup.get('units').disable({emitEvent: false}); |
|||
formGroup.get('widgetUnitsAttributeName').enable({emitEvent: false}); |
|||
} |
|||
|
|||
formGroup.get('volumeSource').enable({emitEvent: false}); |
|||
formGroup.get('volumeUnits').enable({emitEvent: false}); |
|||
if (volumeSource === LiquidWidgetDataSourceType.static) { |
|||
formGroup.get('volumeConstant').enable({emitEvent: false}); |
|||
formGroup.get('volumeAttributeName').disable({emitEvent: false}); |
|||
} else { |
|||
formGroup.get('volumeConstant').disable({emitEvent: false}); |
|||
formGroup.get('volumeAttributeName').enable({emitEvent: false}); |
|||
} |
|||
|
|||
if (formGroup.get('decimals')) { |
|||
formGroup.get('decimals').enable({emitEvent: false}); |
|||
} |
|||
formGroup.get('valueFont').enable({emitEvent: false}); |
|||
formGroup.get('valueColor').enable({emitEvent: false}); |
|||
|
|||
formGroup.get('volumeFont').enable({emitEvent: false}); |
|||
formGroup.get('volumeColor').enable({emitEvent: false}); |
|||
|
|||
break; |
|||
} |
|||
|
|||
if (showTooltip) { |
|||
formGroup.get('showTooltipLevel').enable({emitEvent: false}); |
|||
formGroup.get('showTooltipDate').enable({emitEvent: false}); |
|||
formGroup.get('tooltipBackgroundColor').enable({emitEvent: false}); |
|||
formGroup.get('tooltipBackgroundBlur').enable({emitEvent: false}); |
|||
|
|||
if (showTooltipLevel) { |
|||
formGroup.get('tooltipLevelDecimals').enable({emitEvent: false}); |
|||
formGroup.get('tooltipLevelFont').enable({emitEvent: false}); |
|||
formGroup.get('tooltipLevelColor').enable({emitEvent: false}); |
|||
formGroup.get('tooltipUnits').enable({emitEvent: false}); |
|||
} else { |
|||
formGroup.get('tooltipUnits').disable({emitEvent: false}); |
|||
formGroup.get('tooltipLevelDecimals').disable({emitEvent: false}); |
|||
formGroup.get('tooltipLevelFont').disable({emitEvent: false}); |
|||
formGroup.get('tooltipLevelColor').disable({emitEvent: false}); |
|||
} |
|||
|
|||
if (showTooltipDate) { |
|||
formGroup.get('tooltipDateFormat').enable({emitEvent: false}); |
|||
formGroup.get('tooltipDateFont').enable({emitEvent: false}); |
|||
formGroup.get('tooltipDateColor').enable({emitEvent: false}); |
|||
} else { |
|||
formGroup.get('tooltipDateFormat').disable({emitEvent: false}); |
|||
formGroup.get('tooltipDateFont').disable({emitEvent: false}); |
|||
formGroup.get('tooltipDateColor').disable({emitEvent: false}); |
|||
} |
|||
} else { |
|||
formGroup.get('showTooltipLevel').disable({emitEvent: false}); |
|||
formGroup.get('showTooltipDate').disable({emitEvent: false}); |
|||
formGroup.get('tooltipBackgroundColor').disable({emitEvent: false}); |
|||
formGroup.get('tooltipBackgroundBlur').disable({emitEvent: false}); |
|||
|
|||
formGroup.get('tooltipUnits').disable({emitEvent: false}); |
|||
formGroup.get('tooltipLevelDecimals').disable({emitEvent: false}); |
|||
formGroup.get('tooltipLevelFont').disable({emitEvent: false}); |
|||
formGroup.get('tooltipLevelColor').disable({emitEvent: false}); |
|||
|
|||
formGroup.get('tooltipDateFormat').disable({emitEvent: false}); |
|||
formGroup.get('tooltipDateFont').disable({emitEvent: false}); |
|||
formGroup.get('tooltipDateColor').disable({emitEvent: false}); |
|||
} |
|||
}; |
|||
@ -0,0 +1,260 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2023 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<ng-container *ngIf="levelCardWidgetSettingsForm" [formGroup]="levelCardWidgetSettingsForm"> |
|||
<div class="tb-form-panel no-padding no-border"> |
|||
<div class="tb-form-panel"> |
|||
<div fxFlex fxLayout="column" class="tb-form-row space-between"> |
|||
<div fxFlex fxLayout="row" style="width: 100%;" fxLayoutAlign="space-between center"> |
|||
<div class="fixed-title-width" translate>widgets.liquid-level-card.shape</div> |
|||
<tb-toggle-select formControlName="tankSelectionType"> |
|||
<tb-toggle-option *ngFor="let type of DataSourceTypes" [value]="type"> |
|||
{{ DataSourceTypeTranslations.get(type) | translate }} |
|||
</tb-toggle-option> |
|||
</tb-toggle-select> |
|||
</div> |
|||
<tb-image-cards-select #shapesImageCardsSelect |
|||
[fxShow]="levelCardWidgetSettingsForm.get('tankSelectionType').value === DataSourceType.static" |
|||
rowHeight="{{ '1:1' }}" |
|||
[cols]="5" |
|||
[colsLtMd]="2" |
|||
style="width: 100%;" |
|||
label="{{ 'widgets.liquid-level-card.shape-type' | translate }}" formControlName="selectedShape"> |
|||
<tb-image-cards-select-option *ngFor="let shape of shapes" |
|||
[value]="shape" |
|||
[image]="createShape(shapesImageMap.get(shape), LevelCardLayout.simple)"> |
|||
{{ ShapesTranslationMap.get(shape) | translate }} |
|||
</tb-image-cards-select-option> |
|||
</tb-image-cards-select> |
|||
<ng-container *ngIf="levelCardWidgetSettingsForm.get('tankSelectionType').value === DataSourceType.attribute"> |
|||
<div class="tb-form-row space-between" style="width: 100%;"> |
|||
<div class="fixed-title-width" translate>widgets.liquid-level-card.shape-attribute-name</div> |
|||
<tb-string-autocomplete [fetchOptionsFn]="fetchOptions.bind(this)" |
|||
required style="flex: 1" |
|||
[errorText]="'widgets.liquid-level-card.attribute-name-required' | translate" |
|||
formControlName="shapeAttributeName"> |
|||
</tb-string-autocomplete> |
|||
</div> |
|||
<div fxFlex fxLayout="row" fxLayoutAlign="space-between center" style="width: 100%;"> |
|||
<div class="tb-hint" style="padding: 0;" translate>widgets.liquid-level-card.shape-by-attribute</div> |
|||
<div class="see-example" |
|||
[tb-help-popup]="'widget/lib/indicator/shape_attribute_fn'" |
|||
tb-help-popup-placement="left" |
|||
trigger-style="font-size: 12px" |
|||
[tb-help-popup-style]="{maxWidth: '820px'}" |
|||
trigger-text="{{ 'widgets.liquid-level-card.see-examples' | translate }}"> |
|||
</div> |
|||
</div> |
|||
</ng-container> |
|||
</div> |
|||
</div> |
|||
<div class="tb-form-panel"> |
|||
<div class="tb-form-panel-title" translate>widgets.liquid-level-card.layout</div> |
|||
<tb-image-cards-select #layoutsImageCardsSelect |
|||
rowHeight="{{ '1:1' }}" |
|||
[cols]="3" |
|||
[colsLtMd]="3" |
|||
label="{{ 'widgets.liquid-level-card.layout' | translate }}" formControlName="layout"> |
|||
<tb-image-cards-select-option |
|||
*ngFor="let layout of LevelCardLayouts" |
|||
[value]="layout" |
|||
[image]="createShape(shapesImageMap.get(levelCardWidgetSettingsForm.get('selectedShape').value), layout)"> |
|||
{{ LevelCardLayoutTranslationMap.get(layout) | translate }} |
|||
</tb-image-cards-select-option> |
|||
</tb-image-cards-select> |
|||
</div> |
|||
<div class="tb-form-panel"> |
|||
<div class="tb-form-panel-title" translate>widgets.liquid-level-card.units</div> |
|||
<div class="tb-form-row space-between"> |
|||
<div class="fixed-title-width tb-required" translate>widgets.liquid-level-card.datasource-units</div> |
|||
<tb-unit-input required style="max-width: 30%;" class="flex" |
|||
[tagFilter]="unitsType.capacity" |
|||
formControlName="datasourceUnits"> |
|||
</tb-unit-input> |
|||
</div> |
|||
<div class="tb-form-row space-between" *ngIf="levelCardWidgetSettingsForm.get('layout').value === LevelCardLayout.absolute"> |
|||
<div class="fixed-title-width tb-required" translate>widgets.liquid-level-card.widget-units</div> |
|||
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
|||
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic" style="max-width: 25%"> |
|||
<mat-select formControlName="widgetUnitsSource" placeholder="{{ 'widget-config.set' | translate }}"> |
|||
<mat-option *ngFor="let type of DataSourceTypes" [value]="type"> |
|||
{{ DataSourceTypeTranslations.get(type) | translate }} |
|||
</mat-option> |
|||
</mat-select> |
|||
</mat-form-field> |
|||
<tb-unit-input *ngIf="levelCardWidgetSettingsForm.get('widgetUnitsSource').value !== DataSourceType.attribute; else selectUnitsAttributes" |
|||
required class="flex" |
|||
[tagFilter]="unitsType.capacity" |
|||
formControlName="units"> |
|||
</tb-unit-input> |
|||
<ng-template #selectUnitsAttributes> |
|||
<tb-string-autocomplete [fetchOptionsFn]="fetchOptions.bind(this)" |
|||
required style="flex: 1" |
|||
[errorText]="'widgets.liquid-level-card.attribute-name-required' | translate" |
|||
formControlName="widgetUnitsAttributeName"> |
|||
</tb-string-autocomplete> |
|||
</ng-template> |
|||
</div> |
|||
</div> |
|||
<div class="tb-form-row" *ngIf="volumeInput"> |
|||
<div class="fixed-title-width tb-required" translate>widgets.liquid-level-card.total-volume</div> |
|||
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
|||
<mat-form-field class="flex" appearance="outline" subscriptSizing="dynamic" style="max-width: 25%"> |
|||
<mat-select formControlName="volumeSource" placeholder="{{ 'widget-config.set' | translate }}"> |
|||
<mat-option *ngFor="let type of DataSourceTypes" [value]="type"> |
|||
{{ DataSourceTypeTranslations.get(type) | translate }} |
|||
</mat-option> |
|||
</mat-select> |
|||
</mat-form-field> |
|||
<mat-form-field *ngIf="levelCardWidgetSettingsForm.get('volumeSource').value === DataSourceType.static; else volumeAttributes" |
|||
appearance="outline" class="flex number" subscriptSizing="dynamic"> |
|||
<input matInput formControlName="volumeConstant" type="number" |
|||
min="0.1" step="1" placeholder="{{ 'widget-config.set' | translate }}"> |
|||
<mat-icon matSuffix |
|||
matTooltipPosition="above" |
|||
matTooltipClass="tb-error-tooltip" |
|||
[matTooltip]="'widgets.liquid-level-card.total-volume-required' | translate" |
|||
*ngIf="levelCardWidgetSettingsForm.get('volumeConstant').hasError('required')" |
|||
class="material-icons tb-suffix-show-always tb-error"> |
|||
warning |
|||
</mat-icon> |
|||
</mat-form-field> |
|||
<ng-template #volumeAttributes> |
|||
<tb-string-autocomplete [fetchOptionsFn]="fetchOptions.bind(this)" |
|||
required style="flex: 1" |
|||
[errorText]="'widgets.liquid-level-card.attribute-name-required' | translate" |
|||
formControlName="volumeAttributeName"> |
|||
</tb-string-autocomplete> |
|||
</ng-template> |
|||
<tb-unit-input [tagFilter]="unitsType.capacity" |
|||
required style="max-width: 25%" class="flex" |
|||
formControlName="volumeUnits"> |
|||
</tb-unit-input> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="tb-form-panel"> |
|||
<div class="tb-form-panel-title" translate>widgets.liquid-level-card.color-and-font</div> |
|||
<div class="tb-form-row space-between"> |
|||
<div class="fixed-title-width" translate>widgets.liquid-level-card.tank-color</div> |
|||
<tb-color-settings formControlName="tankColor" settingsKey="{{'widgets.liquid-level-card.tank-color' | translate }}"> |
|||
</tb-color-settings> |
|||
</div> |
|||
<div class="tb-form-row space-between"> |
|||
<div class="fixed-title-width" translate>widgets.liquid-level-card.liquid-color</div> |
|||
<tb-color-settings formControlName="liquidColor" settingsKey="{{'widgets.liquid-level-card.liquid-color' | translate }}"> |
|||
</tb-color-settings> |
|||
</div> |
|||
<div class="tb-form-row space-between"> |
|||
<div>{{ 'widgets.background.background' | translate }}</div> |
|||
<tb-background-settings formControlName="background"> |
|||
</tb-background-settings> |
|||
</div> |
|||
<div class="tb-form-row space-between" |
|||
*ngIf="levelCardWidgetSettingsForm.get('layout').value !== LevelCardLayout.simple"> |
|||
<div class="fixed-title-width" translate>widgets.liquid-level-card.value</div> |
|||
<div fxFlex fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px"> |
|||
<tb-font-settings formControlName="valueFont" |
|||
[previewText]="valuePreviewFn"> |
|||
</tb-font-settings> |
|||
<tb-color-settings formControlName="valueColor" settingsKey="{{'widgets.liquid-level-card.value' | translate }}"> |
|||
</tb-color-settings> |
|||
</div> |
|||
</div> |
|||
<div class="tb-form-row" *ngIf="levelCardWidgetSettingsForm.get('layout').value === LevelCardLayout.absolute"> |
|||
<div class="fixed-title-width" translate>widgets.liquid-level-card.total-volume</div> |
|||
<div fxFlex fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px"> |
|||
<tb-font-settings formControlName="volumeFont" |
|||
[previewText]="totalVolumeValuePreviewFn"> |
|||
</tb-font-settings> |
|||
<tb-color-input asBoxInput colorClearButton formControlName="volumeColor"> |
|||
</tb-color-input> |
|||
</div> |
|||
</div> |
|||
<div class="tb-form-row space-between" |
|||
*ngIf="levelCardWidgetSettingsForm.get('layout').value !== LevelCardLayout.simple"> |
|||
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showBackgroundOverlay"> |
|||
{{ 'widgets.liquid-level-card.background-overlay' | translate }} |
|||
</mat-slide-toggle> |
|||
<tb-color-settings formControlName="backgroundOverlayColor" settingsKey="{{'widgets.liquid-level-card.background-overlay' | translate }}"> |
|||
</tb-color-settings> |
|||
</div> |
|||
</div> |
|||
<div class="tb-form-panel tb-slide-toggle"> |
|||
<mat-expansion-panel class="tb-settings" |
|||
[expanded]="levelCardWidgetSettingsForm.get('showTooltip').value" |
|||
[disabled]="!levelCardWidgetSettingsForm.get('showTooltip').value"> |
|||
<mat-expansion-panel-header fxLayout="row wrap"> |
|||
<mat-panel-title> |
|||
<mat-slide-toggle class="mat-slide" formControlName="showTooltip" (click)="$event.stopPropagation()" |
|||
fxLayoutAlign="center"> |
|||
{{ 'widget-config.tooltip' | translate }} |
|||
</mat-slide-toggle> |
|||
</mat-panel-title> |
|||
</mat-expansion-panel-header> |
|||
<ng-template matExpansionPanelContent> |
|||
<div class="tb-form-row column-xs"> |
|||
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTooltipLevel"> |
|||
{{ 'widgets.liquid-level-card.level' | translate }} |
|||
</mat-slide-toggle> |
|||
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
|||
<tb-unit-input [tagFilter]="unitsType.capacity" |
|||
required class="flex" formControlName="tooltipUnits"> |
|||
</tb-unit-input> |
|||
<mat-form-field appearance="outline" class="flex number" subscriptSizing="dynamic"> |
|||
<input matInput formControlName="tooltipLevelDecimals" type="number" min="0" |
|||
max="15" step="1" placeholder="{{ 'widget-config.set' | translate }}"> |
|||
<div matSuffix fxHide.lt-md translate>widget-config.decimals-suffix</div> |
|||
</mat-form-field> |
|||
<tb-font-settings formControlName="tooltipLevelFont" |
|||
[previewText]="tooltipValuePreviewFn"> |
|||
</tb-font-settings> |
|||
<tb-color-settings formControlName="tooltipLevelColor" settingsKey="{{'widgets.liquid-level-card.level' | translate }}"> |
|||
</tb-color-settings> |
|||
</div> |
|||
</div> |
|||
<div class="tb-form-row column-xs"> |
|||
<mat-slide-toggle class="mat-slide fixed-title-width" formControlName="showTooltipDate"> |
|||
{{ 'widgets.value-card.date' | translate }} |
|||
</mat-slide-toggle> |
|||
<div fxFlex.gt-xs fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="8px"> |
|||
<tb-date-format-select fxFlex formControlName="tooltipDateFormat"></tb-date-format-select> |
|||
<tb-font-settings formControlName="tooltipDateFont" |
|||
[previewText]="datePreviewFn"> |
|||
</tb-font-settings> |
|||
<tb-color-input asBoxInput colorClearButton formControlName="tooltipDateColor"> |
|||
</tb-color-input> |
|||
</div> |
|||
</div> |
|||
<div class="tb-form-row space-between"> |
|||
<div class="fixed-title-width" translate>widgets.liquid-level-card.tooltip-background</div> |
|||
<tb-color-input asBoxInput colorClearButton formControlName="tooltipBackgroundColor"> |
|||
</tb-color-input> |
|||
</div> |
|||
<div class="tb-form-row space-between"> |
|||
<div class="fixed-title-width" translate>widgets.liquid-level-card.background-blur</div> |
|||
<mat-form-field appearance="outline" class="number" subscriptSizing="dynamic"> |
|||
<input matInput formControlName="tooltipBackgroundBlur" type="number" min="0" step="1" |
|||
placeholder="{{ 'widget-config.set' | translate }}"> |
|||
<div matSuffix>px</div> |
|||
</mat-form-field> |
|||
</div> |
|||
</ng-template> |
|||
</mat-expansion-panel> |
|||
</div> |
|||
</div> |
|||
</ng-container> |
|||
@ -0,0 +1,306 @@ |
|||
///
|
|||
/// Copyright © 2016-2023 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { ChangeDetectorRef, Component, Injector, ViewChild } from '@angular/core'; |
|||
import { |
|||
DataKey, |
|||
Datasource, |
|||
DatasourceType, |
|||
WidgetSettings, |
|||
WidgetSettingsComponent |
|||
} from '@shared/models/widget.models'; |
|||
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { formatValue, isDefined } from '@core/utils'; |
|||
import { WidgetConfigComponentData } from '@home/models/widget-component.models'; |
|||
import { DateFormatProcessor, DateFormatSettings } from '@shared/models/widget-settings.models'; |
|||
import { |
|||
CapacityUnits, |
|||
createShapeLayout, |
|||
fetchEntityKeys, |
|||
fetchEntityKeysForDevice, |
|||
levelCardDefaultSettings, |
|||
LevelCardLayout, |
|||
levelCardLayoutTranslations, |
|||
LiquidWidgetDataSourceType, |
|||
LiquidWidgetDataSourceTypeTranslations, |
|||
loadSvgShapesMapping, |
|||
optionsFilter, |
|||
Shapes, |
|||
ShapesTranslations, |
|||
updatedFormSettingsValidators |
|||
} from '@home/components/widget/lib/indicator/liquid-level-widget.models'; |
|||
import { UnitsType } from '@shared/models/unit.models'; |
|||
import { ImageCardsSelectComponent } from '@home/components/widget/lib/settings/common/image-cards-select.component'; |
|||
import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; |
|||
import { Observable, of, ReplaySubject } from 'rxjs'; |
|||
import { map, share, tap } from 'rxjs/operators'; |
|||
import { ResourcesService } from '@core/services/resources.service'; |
|||
import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; |
|||
import { UtilsService } from '@core/services/utils.service'; |
|||
import { EntityService } from '@core/http/entity.service'; |
|||
|
|||
@Component({ |
|||
selector: 'liquid-level-card-widget-settings', |
|||
templateUrl: './liquid-level-card-widget-settings.component.html', |
|||
styleUrls: [] |
|||
}) |
|||
export class LiquidLevelCardWidgetSettingsComponent extends WidgetSettingsComponent { |
|||
|
|||
@ViewChild('layoutsImageCardsSelect', { static: false }) layoutsImageCardsSelect: ImageCardsSelectComponent; |
|||
|
|||
@ViewChild('shapesImageCardsSelect', { static: false }) shapesImageCardsSelect: ImageCardsSelectComponent; |
|||
|
|||
public get volumeInput(): boolean { |
|||
const datasourceUnits = this.levelCardWidgetSettingsForm.get('datasourceUnits').value; |
|||
const layout: LevelCardLayout = this.levelCardWidgetSettingsForm.get('layout').value; |
|||
|
|||
if (layout === LevelCardLayout.absolute) { |
|||
return true; |
|||
} |
|||
return datasourceUnits !== CapacityUnits.percent; |
|||
} |
|||
|
|||
private get datasource(): Datasource { |
|||
if (this.widgetConfig.config.datasources && this.widgetConfig.config.datasources) { |
|||
return this.widgetConfig.config.datasources[0]; |
|||
} else { |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
LevelCardLayout = LevelCardLayout; |
|||
LevelCardLayouts = Object.values(LevelCardLayout) as LevelCardLayout[]; |
|||
LevelCardLayoutTranslationMap = levelCardLayoutTranslations; |
|||
|
|||
DataSourceType = LiquidWidgetDataSourceType; |
|||
DataSourceTypes = Object.values(LiquidWidgetDataSourceType) as LiquidWidgetDataSourceType[]; |
|||
DataSourceTypeTranslations = LiquidWidgetDataSourceTypeTranslations; |
|||
|
|||
shapes = Object.values(Shapes) as Shapes[]; |
|||
shapesImageMap: Map<Shapes, string> = new Map(); |
|||
ShapesTranslationMap = ShapesTranslations; |
|||
|
|||
unitsType = UnitsType; |
|||
|
|||
levelCardWidgetSettingsForm: FormGroup; |
|||
|
|||
valuePreviewFn = this._valuePreviewFn.bind(this); |
|||
|
|||
tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); |
|||
|
|||
totalVolumeValuePreviewFn = this._totalVolumeValuePreviewFn.bind(this); |
|||
|
|||
datePreviewFn = this._datePreviewFn.bind(this); |
|||
|
|||
private keySearchText: string; |
|||
private latestKeySearchTextResult: Array<string>; |
|||
|
|||
private functionTypeKeys: Array<DataKey> = []; |
|||
|
|||
private lastKeysId: string; |
|||
private lastFetchedKeys: Array<DataKey>; |
|||
|
|||
constructor(protected store: Store<AppState>, |
|||
private $injector: Injector, |
|||
private fb: FormBuilder, |
|||
private resourcesService: ResourcesService, |
|||
private sanitizer: DomSanitizer, |
|||
private cd: ChangeDetectorRef, |
|||
private utils: UtilsService, |
|||
private entityService: EntityService) { |
|||
super(store); |
|||
} |
|||
|
|||
protected settingsForm(): FormGroup { |
|||
return this.levelCardWidgetSettingsForm; |
|||
} |
|||
|
|||
protected onWidgetConfigSet(widgetConfig: WidgetConfigComponentData) { |
|||
this.createSvgShapesMapping(); |
|||
|
|||
for (const type of this.utils.getPredefinedFunctionsList()) { |
|||
this.functionTypeKeys.push({ |
|||
name: type, |
|||
type: DataKeyType.function |
|||
}); |
|||
} |
|||
} |
|||
|
|||
protected defaultSettings(): WidgetSettings { |
|||
return levelCardDefaultSettings; |
|||
} |
|||
|
|||
protected onSettingsSet(settings: WidgetSettings) { |
|||
this.levelCardWidgetSettingsForm = this.fb.group({ |
|||
tankSelectionType: [settings.tankSelectionType, []], |
|||
selectedShape: [settings.selectedShape, [Validators.required]], |
|||
shapeAttributeName: [settings.shapeAttributeName, [Validators.required]], |
|||
tankColor: [settings.tankColor, []], |
|||
datasourceUnits: [settings.datasourceUnits, [Validators.required]], |
|||
|
|||
layout: [settings.layout, []], |
|||
|
|||
volumeSource: [settings.volumeSource, []], |
|||
volumeConstant: [settings.volumeConstant, [Validators.required, Validators.min(0.1)]], |
|||
volumeAttributeName: [settings.volumeAttributeName, [Validators.required]], |
|||
volumeUnits: [settings.volumeUnits, [Validators.required]], |
|||
volumeFont: [settings.volumeFont, []], |
|||
volumeColor: [settings.volumeColor, []], |
|||
valueFont: [settings.valueFont, []], |
|||
valueColor: [settings.valueColor, []], |
|||
units: [settings.units, [Validators.required]], |
|||
widgetUnitsSource: [settings.widgetUnitsSource, [Validators.required]], |
|||
widgetUnitsAttributeName: [settings.widgetUnitsAttributeName, [Validators.required]], |
|||
showBackgroundOverlay: [settings.showBackgroundOverlay, []], |
|||
backgroundOverlayColor: [settings.backgroundOverlayColor, []], |
|||
|
|||
liquidColor: [settings.liquidColor, []], |
|||
background: [settings.background], |
|||
|
|||
showTooltip: [settings.showTooltip, []], |
|||
showTooltipLevel: [settings.showTooltipLevel, []], |
|||
tooltipUnits: [settings.tooltipUnits, []], |
|||
tooltipLevelDecimals: [settings.tooltipLevelDecimals, []], |
|||
tooltipLevelFont: [settings.tooltipLevelFont, []], |
|||
tooltipLevelColor: [settings.tooltipLevelColor, []], |
|||
showTooltipDate: [settings.showTooltipDate, []], |
|||
tooltipDateFormat: [settings.tooltipDateFormat, []], |
|||
tooltipDateFont: [settings.tooltipDateFont, []], |
|||
tooltipDateColor: [settings.tooltipDateColor, []], |
|||
tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], |
|||
tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], |
|||
}); |
|||
|
|||
this.levelCardWidgetSettingsForm.get('selectedShape').valueChanges.subscribe(() => { |
|||
this.cd.detectChanges(); |
|||
this.layoutsImageCardsSelect?.imageCardsSelectOptions.notifyOnChanges(); |
|||
}); |
|||
} |
|||
|
|||
protected validatorTriggers(): string[] { |
|||
return [ |
|||
'showBackgroundOverlay', 'showTooltip', 'showTooltipLevel', 'tankSelectionType', 'datasourceUnits', |
|||
'showTooltipDate', 'layout', 'volumeSource', 'widgetUnitsSource' |
|||
]; |
|||
} |
|||
|
|||
protected updateValidators(emitEvent: boolean, trigger?: string) { |
|||
updatedFormSettingsValidators(this.levelCardWidgetSettingsForm); |
|||
|
|||
if (this.levelCardWidgetSettingsForm.get('showBackgroundOverlay').value) { |
|||
this.levelCardWidgetSettingsForm.get('backgroundOverlayColor').enable({emitEvent: false}); |
|||
} else { |
|||
this.levelCardWidgetSettingsForm.get('backgroundOverlayColor').disable({emitEvent: false}); |
|||
} |
|||
} |
|||
|
|||
private createSvgShapesMapping(): void { |
|||
loadSvgShapesMapping(this.resourcesService).subscribe(shapeMap => { |
|||
this.shapesImageMap = shapeMap; |
|||
|
|||
this.cd.detectChanges(); |
|||
this.layoutsImageCardsSelect?.imageCardsSelectOptions.notifyOnChanges(); |
|||
this.shapesImageCardsSelect?.imageCardsSelectOptions.notifyOnChanges(); |
|||
}); |
|||
} |
|||
|
|||
createShape(svg: string, layout: LevelCardLayout): SafeUrl { |
|||
return createShapeLayout(svg, layout, this.sanitizer); |
|||
} |
|||
|
|||
private _valuePreviewFn(): string { |
|||
const units: string = this.widgetConfig.config.units; |
|||
const decimals: number = this.widgetConfig.config.decimals; |
|||
return formatValue(32, decimals, units, true); |
|||
} |
|||
|
|||
private _tooltipValuePreviewFn() { |
|||
const units: string = this.levelCardWidgetSettingsForm.get('tooltipUnits').value; |
|||
const decimals: number = this.levelCardWidgetSettingsForm.get('tooltipLevelDecimals').value; |
|||
return formatValue(32, decimals, units, true); |
|||
} |
|||
|
|||
private _totalVolumeValuePreviewFn() { |
|||
const value = this.levelCardWidgetSettingsForm.get('volumeConstant').value; |
|||
const datasourceUnits = this.levelCardWidgetSettingsForm.get('datasourceUnits').value; |
|||
const decimals: number = this.widgetConfig.config.decimals; |
|||
let units: string = this.widgetConfig.config.units; |
|||
|
|||
if (datasourceUnits !== CapacityUnits.percent) { |
|||
units = datasourceUnits; |
|||
} |
|||
|
|||
return formatValue((isDefined(value) ? value : 500), decimals, units, true); |
|||
} |
|||
|
|||
private _datePreviewFn(): string { |
|||
const dateFormat: DateFormatSettings = this.levelCardWidgetSettingsForm.get('tooltipDateFormat').value; |
|||
const processor = DateFormatProcessor.fromSettings(this.$injector, dateFormat); |
|||
processor.update(Date.now()); |
|||
return processor.formatted; |
|||
} |
|||
|
|||
public fetchOptions(searchText: string): Observable<Array<string>> { |
|||
if (this.keySearchText !== searchText) { |
|||
this.keySearchText = searchText; |
|||
const dataKeyFilter = optionsFilter(this.keySearchText); |
|||
return this.getKeys().pipe( |
|||
tap(res => this.lastFetchedKeys !== res ? this.lastFetchedKeys = res : []), |
|||
map(name => name?.filter(dataKeyFilter).map(key => key.name)), |
|||
tap(res => this.latestKeySearchTextResult = res) |
|||
); |
|||
} |
|||
return of(this.latestKeySearchTextResult); |
|||
} |
|||
|
|||
private getKeys(): Observable<Array<DataKey>> { |
|||
let fetchObservable: Observable<Array<DataKey>>; |
|||
if (this.datasource?.type === DatasourceType.function) { |
|||
fetchObservable = of(this.functionTypeKeys); |
|||
} else if (this.datasource?.type === DatasourceType.entity && this.datasource?.entityAliasId || |
|||
this.datasource?.type === DatasourceType.device && this.datasource?.deviceId) { |
|||
if (this.datasource?.type === DatasourceType.device) { |
|||
if (this.lastKeysId !== this.datasource?.deviceId || !this.lastFetchedKeys) { |
|||
this.lastKeysId = this.datasource.deviceId; |
|||
fetchObservable = fetchEntityKeysForDevice(this.datasource?.deviceId, [DataKeyType.attribute], |
|||
this.entityService); |
|||
} else { |
|||
fetchObservable = of(this.lastFetchedKeys); |
|||
} |
|||
} else { |
|||
if (this.lastKeysId !== this.datasource?.entityAliasId || !this.lastFetchedKeys) { |
|||
this.lastKeysId = this.datasource.entityAliasId; |
|||
fetchObservable = fetchEntityKeys(this.datasource?.entityAliasId, [DataKeyType.attribute], |
|||
this.entityService, this.aliasController); |
|||
} else { |
|||
fetchObservable = of(this.lastFetchedKeys); |
|||
} |
|||
} |
|||
} else { |
|||
fetchObservable = of([]); |
|||
} |
|||
return fetchObservable.pipe( |
|||
share({ |
|||
connector: () => new ReplaySubject(1), |
|||
resetOnError: false, |
|||
resetOnComplete: false, |
|||
resetOnRefCountZero: false |
|||
}) |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2023 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<mat-form-field [appearance]="appearance" [class]="additionalClass" |
|||
[subscriptSizing]="subscriptSizing" style="width: 100%"> |
|||
<mat-label *ngIf="label">{{label}}</mat-label> |
|||
<input matInput #nameInput [formControl]="selectionFormControl" |
|||
[placeholder]="placeholderText" |
|||
(focusin)="onFocus()" |
|||
[matAutocomplete]="optionsAutocomplete"> |
|||
<button *ngIf="selectionFormControl.value && !disabled" |
|||
type="button" |
|||
class="tb-icon-24" |
|||
matSuffix mat-icon-button aria-label="Clear" |
|||
(click)="clear()"> |
|||
<mat-icon class="material-icons">close</mat-icon> |
|||
</button> |
|||
<mat-icon *ngIf="selectionFormControl.hasError('required') && !showInlineError" |
|||
matIconSuffix |
|||
[matTooltip]="errorText" |
|||
matTooltipPosition="above" |
|||
[matTooltipClass]="tooltipClass" |
|||
class="material-icons tb-suffix-show-always tb-error"> |
|||
warning |
|||
</mat-icon> |
|||
<mat-error *ngIf="selectionFormControl.hasError('required') && showInlineError"> |
|||
{{errorText}} |
|||
</mat-error> |
|||
<mat-autocomplete |
|||
#optionsAutocomplete="matAutocomplete" |
|||
class="tb-autocomplete tb-options-input-autocomplete" |
|||
panelWidth="fit-content"> |
|||
<mat-option *ngFor="let option of filteredOptions$ | async" [value]="option"> |
|||
<span class="tb-option-name" fxFlex [innerHTML]="option | highlight:searchText:true:'ig'"></span> |
|||
</mat-option> |
|||
</mat-autocomplete> |
|||
</mat-form-field> |
|||
@ -0,0 +1,43 @@ |
|||
/** |
|||
* Copyright © 2016-2023 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
:host { |
|||
mat-form-field { |
|||
display: flex; |
|||
flex: 1; |
|||
max-width: 100%; |
|||
box-sizing: border-box; |
|||
|
|||
.tb-autocomplete.tb-option-input-autocomplete { |
|||
.mat-mdc-option { |
|||
border-bottom: none; |
|||
|
|||
.mdc-list-item__primary-text { |
|||
flex: 1; |
|||
display: flex; |
|||
flex-direction: row; |
|||
gap: 8px; |
|||
|
|||
.tb-option { |
|||
font-size: 14px; |
|||
font-weight: 400; |
|||
line-height: 20px; |
|||
letter-spacing: 0.2px; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,180 @@ |
|||
///
|
|||
/// Copyright © 2016-2023 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { |
|||
Component, |
|||
Input, |
|||
forwardRef, |
|||
OnInit, |
|||
ViewChild, |
|||
ElementRef |
|||
} from '@angular/core'; |
|||
import { |
|||
ControlValueAccessor, |
|||
NG_VALUE_ACCESSOR, |
|||
FormControl, |
|||
Validators, |
|||
FormBuilder |
|||
} from '@angular/forms'; |
|||
import { Observable, of } from 'rxjs'; |
|||
import { tap, map, switchMap, take } from 'rxjs/operators'; |
|||
import { TranslateService } from '@ngx-translate/core'; |
|||
import { coerceBoolean } from '@shared/decorators/coercion'; |
|||
import { MatFormFieldAppearance, SubscriptSizing } from '@angular/material/form-field'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-string-autocomplete', |
|||
templateUrl: './string-autocomplete.component.html', |
|||
styleUrls: ['./string-autocomplete.component.scss'], |
|||
providers: [ |
|||
{ |
|||
provide: NG_VALUE_ACCESSOR, |
|||
useExisting: forwardRef(() => StringAutocompleteComponent), |
|||
multi: true |
|||
} |
|||
] |
|||
}) |
|||
export class StringAutocompleteComponent implements ControlValueAccessor, OnInit { |
|||
|
|||
@ViewChild('nameInput', {static: true}) nameInput: ElementRef; |
|||
|
|||
@Input() |
|||
disabled: boolean; |
|||
|
|||
@Input() |
|||
@coerceBoolean() |
|||
required = false; |
|||
|
|||
@Input() |
|||
fetchOptionsFn: (searchText?: string) => Observable<Array<string>>; |
|||
|
|||
@Input() |
|||
placeholderText: string = this.translate.instant('widget-config.set'); |
|||
|
|||
@Input() |
|||
subscriptSizing: SubscriptSizing = 'dynamic'; |
|||
|
|||
@Input() |
|||
additionalClass: string | string[] | Record<string, boolean | undefined | null> = 'tb-inline-field tb-suffix-show-on-hover'; |
|||
|
|||
@Input() |
|||
appearance: MatFormFieldAppearance = 'outline'; |
|||
|
|||
@Input() |
|||
label: string; |
|||
|
|||
@Input() |
|||
tooltipClass = 'tb-error-tooltip'; |
|||
|
|||
@Input() |
|||
errorText: string; |
|||
|
|||
@Input() |
|||
@coerceBoolean() |
|||
showInlineError = false; |
|||
|
|||
selectionFormControl: FormControl; |
|||
|
|||
modelValue: string | null; |
|||
|
|||
filteredOptions$: Observable<Array<string>>; |
|||
|
|||
searchText = ''; |
|||
|
|||
private dirty = false; |
|||
|
|||
private propagateChange = (_val: any) => {}; |
|||
|
|||
constructor(private fb: FormBuilder, |
|||
private translate: TranslateService) { |
|||
} |
|||
|
|||
ngOnInit() { |
|||
this.selectionFormControl = this.fb.control('', this.required ? [Validators.required] : []); |
|||
this.filteredOptions$ = this.selectionFormControl.valueChanges |
|||
.pipe( |
|||
tap(value => this.updateView(value)), |
|||
map(value => value ? value : ''), |
|||
switchMap(value => this.fetchOptionsFn ? this.fetchOptionsFn(value) : of([])) |
|||
); |
|||
} |
|||
|
|||
writeValue(option?: string): void { |
|||
this.searchText = ''; |
|||
this.modelValue = option ? option : null; |
|||
|
|||
if (this.fetchOptionsFn) { |
|||
this.fetchOptionsFn(option) |
|||
.pipe( |
|||
map(options => { |
|||
if (options) { |
|||
const foundOption = options.find(opt => opt === option); |
|||
return foundOption ? foundOption : option; |
|||
} |
|||
|
|||
return option; |
|||
}), |
|||
take(1) |
|||
) |
|||
.subscribe(result => { |
|||
this.selectionFormControl.patchValue(result, { emitEvent: false }); |
|||
this.dirty = true; |
|||
}); |
|||
} else { |
|||
this.selectionFormControl.patchValue(null, { emitEvent: false }); |
|||
this.dirty = true; |
|||
} |
|||
} |
|||
|
|||
onFocus() { |
|||
if (this.dirty) { |
|||
this.selectionFormControl.updateValueAndValidity({onlySelf: true, emitEvent: true}); |
|||
this.dirty = false; |
|||
} |
|||
} |
|||
|
|||
updateView(value: string) { |
|||
this.searchText = value ? value : ''; |
|||
if (this.modelValue !== value) { |
|||
this.modelValue = value; |
|||
this.propagateChange(this.modelValue); |
|||
} |
|||
} |
|||
|
|||
registerOnChange(fn: any): void { |
|||
this.propagateChange = fn; |
|||
} |
|||
|
|||
registerOnTouched(fn: any): void { |
|||
} |
|||
|
|||
setDisabledState(isDisabled: boolean): void { |
|||
this.disabled = isDisabled; |
|||
if (this.disabled) { |
|||
this.selectionFormControl.disable({emitEvent: false}); |
|||
} else { |
|||
this.selectionFormControl.enable({emitEvent: false}); |
|||
} |
|||
} |
|||
|
|||
clear() { |
|||
this.selectionFormControl.patchValue(null, {emitEvent: true}); |
|||
setTimeout(() => { |
|||
this.nameInput.nativeElement.blur(); |
|||
this.nameInput.nativeElement.focus(); |
|||
}, 0); |
|||
} |
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
#### Shape attribute name |
|||
|
|||
A string expression value that allows you dynamically select shape image depending on attribute name (ex. tankShape). |
|||
|
|||
**Attribute values that could be used to add the image:** |
|||
|
|||
<img style="width: 50px; height: 50px; margin-bottom: -58px; margin-left: 300px;" src="assets/widget/liquid-level/shapes/vertical-oval.svg" alt="Alt text" /> |
|||
|
|||
``` |
|||
Vertical Oval |
|||
{:copy-code} |
|||
``` |
|||
<img style="width: 50px; height: 50px; margin-bottom: -58px; margin-left: 300px;" src="assets/widget/liquid-level/shapes/vertical-cylinder.svg" alt="Alt text" /> |
|||
|
|||
``` |
|||
Vertical Cylinder |
|||
{:copy-code} |
|||
``` |
|||
<img style="width: 50px; height: 50px; margin-bottom: -58px; margin-left: 300px;" src="assets/widget/liquid-level/shapes/vertical-capsule.svg" alt="Alt text" /> |
|||
|
|||
``` |
|||
Vertical Capsule |
|||
{:copy-code} |
|||
``` |
|||
<img style="width: 50px; height: 50px; margin-bottom: -58px; margin-left: 300px;" src="assets/widget/liquid-level/shapes/rectangle.svg" alt="Alt text" /> |
|||
|
|||
``` |
|||
Rectangle |
|||
{:copy-code} |
|||
``` |
|||
<img style="width: 50px; height: 50px; margin-bottom: -58px; margin-left: 300px;" src="assets/widget/liquid-level/shapes/horizontal-oval.svg" alt="Alt text" /> |
|||
|
|||
``` |
|||
Horizontal Oval |
|||
{:copy-code} |
|||
``` |
|||
<img style="width: 50px; height: 50px; margin-bottom: -58px; margin-left: 300px;" src="assets/widget/liquid-level/shapes/horizontal-ellipse.svg" alt="Alt text" /> |
|||
|
|||
``` |
|||
Horizontal Ellipse |
|||
{:copy-code} |
|||
``` |
|||
<img style="width: 50px; height: 50px; margin-bottom: -58px; margin-left: 300px;" src="assets/widget/liquid-level/shapes/horizontal-dish-ends.svg" alt="Alt text" /> |
|||
|
|||
``` |
|||
Horizontal Dish Ends |
|||
{:copy-code} |
|||
``` |
|||
<img style="width: 50px; height: 50px; margin-bottom: -58px; margin-left: 300px;" src="assets/widget/liquid-level/shapes/horizontal-cylinder.svg" alt="Alt text" /> |
|||
|
|||
``` |
|||
Horizontal Cylinder |
|||
{:copy-code} |
|||
``` |
|||
<img style="width: 50px; height: 50px; margin-bottom: -58px; margin-left: 300px;" src="assets/widget/liquid-level/shapes/horizontal-capsule.svg" alt="Alt text" /> |
|||
|
|||
``` |
|||
Horizontal Capsule |
|||
{:copy-code} |
|||
``` |
|||
<img style="width: 50px; height: 50px; margin-bottom: -58px; margin-left: 300px;" src="assets/widget/liquid-level/shapes/horizontal-2_1-elliptical.svg" alt="Alt text" /> |
|||
|
|||
``` |
|||
Horizontal 2:1 Elliptical |
|||
{:copy-code} |
|||
``` |
|||
|
|||
<br> |
|||
<br> |
|||
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 11 KiB |