{ "custom": { "refreshTimerInterval": null }, "params": { "deviceType": "Battery", "tagPath": "[PHXA2_IG_COMN]System/Device Lists/Battery" }, "propConfig": { "custom.automaticUpdateEnabled": { "binding": { "config": { "bidirectional": true, "coalesce": true, "fallbackDelay": 2.5, "mode": "indirect", "references": { "tagPath": "{view.params.tagPath}" }, "tagPath": "{tagPath}/Config/Enable Automatic Refresh" }, "type": "tag" }, "persistent": false }, "custom.deviceData": { "binding": { "config": { "fallbackDelay": 2.5, "mode": "indirect", "references": { "tagPath": "{view.params.tagPath}" }, "tagPath": "{tagPath}/Device Data" }, "type": "tag" }, "persistent": false }, "custom.isLoaded": { "binding": { "config": { "struct": { "deviceData": "{view.custom.deviceData}" }, "waitOnAll": true }, "transforms": [ { "expression": "True", "type": "expression" } ], "type": "expr-struct" }, "persistent": false }, "custom.lastUpdate": { "binding": { "config": { "fallbackDelay": 2.5, "mode": "indirect", "references": { "tagPath": "{view.params.tagPath}" }, "tagPath": "{tagPath}/Last Update" }, "transforms": [ { "expression": "//dateFormat({value}, \"MM/dd, hh:mm:ss aa\")\r\ndateFormat(dateArithmetic({value}, -7, \"hour\"), \"MM/dd, hh:mm:ss aa\")", "type": "expression" } ], "type": "tag" }, "persistent": false }, "custom.refreshTimerInterval": { "binding": { "config": { "fallbackDelay": 2.5, "mode": "indirect", "references": { "tagPath": "{view.params.tagPath}" }, "tagPath": "{tagPath}/Parameters.Refresh Timer Interval" }, "type": "tag" }, "persistent": true }, "custom.tableColumns": { "binding": { "config": { "path": "view.custom.tagsToRead" }, "transforms": [ { "code": "\timport copy, time\n\t\n\t# Convert a string to camelCase. We do this because Ignition doesn\u0027t like\n\t# keys being spaced strings. (i.e \u0027Discharge Air Temperature\u0027)\n\tdef to_camel_case(text):\n\t words \u003d text.split()\n\t return words[0].lower() + \u0027\u0027.join(word.capitalize() for word in words[1:])\n\t \n\t# Define the default configuration for a column.\n\tbase_config \u003d {\n\t \"field\": \"\",\n\t \"visible\": True,\n\t \"editable\": False,\n\t \"render\": \"auto\",\n\t \"justify\": \"auto\",\n\t \"align\": \"center\",\n\t \"resizable\": True,\n\t \"sortable\": True,\n\t \"sort\": \"none\",\n\t \"filter\": {\n\t \"enabled\": False,\n\t \"visible\": \"on-hover\",\n\t \"string\": {\"condition\": \"\", \"value\": \"\"},\n\t \"number\": {\"condition\": \"\", \"value\": \"\"},\n\t \"boolean\": {\"condition\": \"\"},\n\t \"date\": {\"condition\": \"\", \"value\": \"\"}\n\t },\n\t \"viewPath\": \"\",\n\t \"viewParams\": {},\n\t \"boolean\": \"checkbox\",\n\t \"number\": \"value\",\n\t \"progressBar\": {\n\t \"max\": 100,\n\t \"min\": 0,\n\t \"bar\": {\"color\": \"\", \"style\": {\"classes\": \"\"}},\n\t \"track\": {\"color\": \"\", \"style\": {\"classes\": \"\"}},\n\t \"value\": {\n\t \"enabled\": True,\n\t \"format\": \"0,0.##\",\n\t \"justify\": \"center\",\n\t \"style\": {\"classes\": \"\"}\n\t }\n\t },\n\t \"toggleSwitch\": {\n\t \"color\": {\"selected\": \"\", \"unselected\": \"\"}\n\t },\n\t \"nullFormat\": {\n\t \"includeNullStrings\": False,\n\t \"strict\": False,\n\t \"nullFormatValue\": \"\"\n\t },\n\t \"numberFormat\": \"0,0.##\",\n\t \"dateFormat\": \"MM/DD/YYYY\",\n\t \"width\": \"\",\n\t \"strictWidth\": False,\n\t \"header\": {\n\t \"title\": \"nothingBurger\",\n\t \"justify\": \"left\",\n\t \"align\": \"center\",\n\t \"style\": {\"classes\": \"\"}\n\t },\n\t \"footer\": {\n\t \"title\": \"\",\n\t \"justify\": \"left\",\n\t \"align\": \"center\",\n\t \"style\": {\"classes\": \"\"}\n\t },\n\t \"style\": {\"classes\": \"\"}\n\t}\n\t\n\tres \u003d []\n\tfor idx, column in enumerate(value): # Expects \u0027CHW Valve Command\u0027\n\t\tconfig \u003d copy.deepcopy(base_config)\n\t\t\n\t\t# Convert \"Meta/Building\" -\u003e \"Building\"\n\t\tif \u0027/\u0027 in column:\n\t\t\tcolumn \u003d column.split(\u0027/\u0027)[-1] \n\t\t\t\n\t\tif \u0027Parameters\u0027 in column:\n\t\t\tcolumn \u003d column[10:]\n\t\t\t\n\t\tif idx \u003d\u003d 0: # Set extra width to the device name column.\n\t\t\tconfig[\u0027width\u0027] \u003d 120\n\t\t\t\n\t\t# Update the column data and header to the tagToRead.\n\t\tconfig[\u0027field\u0027] \u003d to_camel_case(column) # \"CHW Valve Command\" -\u003e \"chwValveCommand\"\n\t\tconfig[\u0027header\u0027][\u0027title\u0027] \u003d column\n\t\tres.append(config)\n\n\treturn res\n\t", "type": "script" } ], "type": "property" }, "persistent": false }, "custom.tagsToRead": { "binding": { "config": { "fallbackDelay": 2.5, "mode": "indirect", "references": { "tagPath": "{view.params.tagPath}" }, "tagPath": "{tagPath}/Config/Tags To Read" }, "type": "tag" }, "persistent": false }, "custom.updateTableTimer": { "binding": { "config": { "expression": "now(15000) // This updates the binding every fifteen seconds, which triggers the on-change script." }, "enabled": false, "transforms": [ { "expression": "dateFormat({value}, \"h:mm:ss a\")", "type": "expression" } ], "type": "expr" }, "onChange": { "enabled": null, "script": "\tif self.custom.automaticUpdateEnabled:\n\t\tsystem.perspective.sendMessage(\u0027updateTableData\u0027, {})" }, "persistent": false }, "params.deviceType": { "paramDirection": "input", "persistent": true }, "params.selectedDeviceTagpath": { "binding": { "config": { "path": "/root/Location/Table.props.selection.selectedRow" }, "transforms": [ { "expression": "{view.custom.deviceData}[{value}][\u0027value\u0027][\u0027tagPath\u0027]", "type": "expression" } ], "type": "property" }, "paramDirection": "output", "persistent": false }, "params.tagPath": { "paramDirection": "input", "persistent": true } }, "props": { "defaultSize": { "height": 1584, "width": 1303 }, "loading": { "mode": "blocking" } }, "root": { "children": [ { "children": [ { "children": [ { "children": [ { "meta": { "name": "Markdown_1" }, "position": { "shrink": 0 }, "props": { "source": "Batteries", "style": { "alignSelf": "center", "classes": "labels/text-xl" } }, "type": "ia.display.markdown" }, { "children": [ { "children": [ { "events": { "dom": { "onClick": { "config": { "script": "\tself.view.custom.automaticUpdateEnabled \u003d not self.view.custom.automaticUpdateEnabled" }, "scope": "G", "type": "script" } } }, "meta": { "name": "Icon", "tooltip": { "enabled": true, "location": "top", "style": { "classes": "labels/text-md" } } }, "position": { "grow": 1 }, "propConfig": { "meta.tooltip.text": { "binding": { "config": { "struct": { "automaticUpdateInterval": "numberFormat(toInt({view.custom.refreshTimerInterval}) / 1000, \u00270.#\u0027)", "automaticUpdatesEnabled": "{view.custom.automaticUpdateEnabled}" }, "waitOnAll": true }, "transforms": [ { "expression": "if({value}[\u0027automaticUpdatesEnabled\u0027],\r\n\tstringFormat(\"Automatic updates are currently enabled. \\nThe table will update every %s seconds.\", {value}[\u0027automaticUpdateInterval\u0027]),\r\n\t\"Automatic updates are currently disabled. Click to enable them.\"\r\n)", "type": "expression" } ], "type": "expr-struct" } }, "props.color": { "binding": { "config": { "path": "view.custom.automaticUpdateEnabled" }, "transforms": [ { "fallback": "#AAAAAA", "inputType": "scalar", "mappings": [ { "input": true, "output": "#00D9D9" } ], "outputType": "color", "type": "map" } ], "type": "property" } }, "props.style.classes": { "binding": { "config": { "path": "view.custom.automaticUpdateEnabled" }, "transforms": [ { "expression": "if({value}, \u0027rotate\u0027, \u0027\u0027)", "type": "expression" } ], "type": "property" } } }, "props": { "path": "material/autorenew", "style": { "cursor": "pointer" } }, "type": "ia.display.icon" } ], "meta": { "name": "IconContainer" }, "position": { "basis": "1.5rem", "shrink": 0 }, "props": { "style": { "classes": "Animation/pulse containers/HoverOver", "marginLeft": "0.5rem", "overflow": "hidden" } }, "type": "ia.container.flex" }, { "events": { "dom": { "onClick": { "config": { "script": "\tself.view.custom.automaticUpdateEnabled \u003d not self.view.custom.automaticUpdateEnabled" }, "scope": "G", "type": "script" } } }, "meta": { "name": "updateLabel" }, "position": { "grow": 1, "shrink": 0 }, "propConfig": { "props.source": { "binding": { "config": { "path": "view.custom.lastUpdate" }, "transforms": [ { "expression": "concat(\"Last Update: \", {value})", "type": "expression" } ], "type": "property" }, "onChange": { "enabled": null, "script": "\tsystem.perspective.sendMessage(\u0027flashTimerText\u0027, {})" }, "persistent": true } }, "props": { "markdown": { "escapeHtml": false }, "source": "Last Update: 05/14, 02:25:30 PM", "style": { "alignContent": "center", "classes": "labels/text-sm, Animation/fadeToBlack_A", "cursor": "pointer", "fontWeight": "400", "opacity": 0.5, "paddingRight": "0.25rem", "textAlign": "right" } }, "type": "ia.display.markdown" }, { "events": { "dom": { "onClick": { "config": { "script": "\tsystem.perspective.sendMessage(\u0027updateTableData\u0027, {})" }, "scope": "G", "type": "script" } } }, "meta": { "name": "Button" }, "position": { "basis": "6rem", "shrink": 0 }, "props": { "image": { "height": 0, "width": 0 }, "style": { "backgroundColor": "--tagvalue-color", "color": "white" }, "text": "Refresh Data", "textStyle": { "color": "white" } }, "type": "ia.input.button" } ], "meta": { "name": "FlexContainer" }, "position": { "grow": 1 }, "props": { "alignItems": "center", "style": { "gap": "0.5rem", "overflow": "hidden", "padding": "1rem 0" }, "wrap": "wrap" }, "type": "ia.container.flex" } ], "meta": { "name": "LargeHeader" }, "position": { "size": "large" }, "props": { "alignItems": "center", "style": { "overflow": "hidden", "paddingRight": "0.5rem", "width": "100%" } }, "type": "ia.container.flex" }, { "children": [ { "meta": { "name": "Markdown_1" }, "position": { "shrink": 0 }, "props": { "source": "Device List", "style": { "alignSelf": "center", "classes": "labels/text-xl" } }, "type": "ia.display.markdown" }, { "children": [ { "children": [ { "events": { "dom": { "onClick": { "config": { "script": "\tself.view.custom.automaticUpdateEnabled \u003d not self.view.custom.automaticUpdateEnabled" }, "scope": "G", "type": "script" } } }, "meta": { "name": "Icon", "tooltip": { "enabled": true, "location": "top", "style": { "classes": "labels/text-md" } } }, "position": { "grow": 1 }, "propConfig": { "meta.tooltip.text": { "binding": { "config": { "struct": { "automaticUpdateInterval": "numberFormat(toInt({view.custom.refreshTimerInterval}) / 1000, \u00270.#\u0027)", "automaticUpdatesEnabled": "{view.custom.automaticUpdateEnabled}" }, "waitOnAll": true }, "transforms": [ { "expression": "if({value}[\u0027automaticUpdatesEnabled\u0027],\r\n\tstringFormat(\"Automatic updates are currently enabled. \\nThe table will update every %s seconds.\", {value}[\u0027automaticUpdateInterval\u0027]),\r\n\t\"Automatic updates are currently disabled. Click to enable them.\"\r\n)", "type": "expression" } ], "type": "expr-struct" } }, "props.color": { "binding": { "config": { "path": "view.custom.automaticUpdateEnabled" }, "transforms": [ { "fallback": "#AAAAAA", "inputType": "scalar", "mappings": [ { "input": true, "output": "#00D9D9" } ], "outputType": "color", "type": "map" } ], "type": "property" } }, "props.style.classes": { "binding": { "config": { "path": "view.custom.automaticUpdateEnabled" }, "transforms": [ { "expression": "if({value}, \u0027rotate\u0027, \u0027\u0027)", "type": "expression" } ], "type": "property" } } }, "props": { "path": "material/autorenew", "style": { "cursor": "pointer" } }, "type": "ia.display.icon" } ], "meta": { "name": "IconContainer" }, "position": { "basis": "1.5rem", "shrink": 0 }, "props": { "style": { "classes": "Animation/pulse containers/HoverOver", "marginLeft": "0.5rem", "overflow": "hidden" } }, "type": "ia.container.flex" }, { "events": { "dom": { "onClick": { "config": { "script": "\tsystem.perspective.sendMessage(\u0027updateTableData\u0027, {})" }, "scope": "G", "type": "script" } } }, "meta": { "name": "Button" }, "position": { "basis": "6rem", "shrink": 0 }, "props": { "image": { "height": 0, "width": 0 }, "style": { "backgroundColor": "--tagvalue-color", "color": "white" }, "text": "Refresh Data", "textStyle": { "color": "white" } }, "type": "ia.input.button" } ], "meta": { "name": "FlexContainer" }, "position": { "grow": 1 }, "props": { "alignItems": "center", "justify": "space-between", "style": { "gap": "0.5rem", "overflow": "hidden", "padding": "1rem 0" }, "wrap": "wrap" }, "type": "ia.container.flex" } ], "meta": { "name": "SmallHeader" }, "props": { "alignItems": "center", "style": { "overflow": "hidden", "paddingRight": "0.5rem", "width": "100%" } }, "type": "ia.container.flex" } ], "meta": { "name": "BreakpointContainer" }, "position": { "basis": "2.5rem", "shrink": 0 }, "props": { "breakpoint": 480, "currentBreakpoint": "large" }, "type": "ia.container.breakpt" }, { "children": [ { "meta": { "name": "Markdown" }, "props": { "source": "Loading...", "style": { "classes": "Animation/loading labels/text-xl", "textAlign": "center" } }, "type": "ia.display.markdown" }, { "meta": { "name": "Progress" }, "props": { "bar": { "color": "#D5D5D5" }, "mode": "indeterminate", "style": { "height": "0.5rem", "width": "12rem" }, "track": { "color": "#AAAAAA" } }, "type": "ia.display.progress" } ], "meta": { "name": "Loading" }, "position": { "basis": "100%", "grow": 1, "shrink": 0 }, "propConfig": { "position.display": { "binding": { "config": { "expression": "!{view.custom.isLoaded}" }, "type": "expr" } } }, "props": { "alignItems": "center", "direction": "column", "justify": "center", "style": { "backgroundColor": "#d9d8d4", "gap": "1rem" } }, "type": "ia.container.flex" }, { "events": { "component": { "onRowClick": { "config": { "script": "\ttagPath \u003d str(self.view.params.selectedDeviceTagpath)\n\t\n\t# Retrieve device name from tagpath, accounting for /1 and nested UDTs.\n\tnoTagProvider \u003d tagPath.partition(\u0027]\u0027)[2]\n\tstart, _, after \u003d noTagProvider.partition(\u0027PHXA2\u0027)\n\tmiddle, _, _ \u003d after.rpartition(\u0027/\u0027)\n\t\n\tif middle:\n\t\tsystem.perspective.sendMessage(\u0027updateFloorplanDeviceRoom\u0027, \u0027PHXA2%s\u0027 % middle)\n\telse:\n\t\tsystem.perspective.sendMessage(\u0027updateFloorplanDeviceRoom\u0027, \u0027PHXA2%s\u0027 % after)" }, "enabled": false, "scope": "G", "type": "script" }, "onRowDoubleClick": { "config": { "script": "\tutils.nav.openEQPopup(self.view.params.deviceType, self.view.params.selectedDeviceTagpath)" }, "scope": "G", "type": "script" } } }, "meta": { "name": "Table" }, "position": { "grow": 1, "shrink": 0 }, "propConfig": { "meta.visible": { "binding": { "config": { "path": "view.custom.isLoaded" }, "type": "property" } }, "position.display": { "binding": { "config": { "path": "view.custom.isLoaded" }, "type": "property" }, "persistent": false }, "props.columns": { "binding": { "config": { "path": "view.custom.tableColumns" }, "type": "property" } }, "props.data": { "binding": { "config": { "path": "view.custom.deviceData" }, "type": "property" } } }, "props": { "cells": { "style": { "cursor": "pointer" } }, "emptyMessage": { "noData": { "text": "N/A" } }, "filter": { "results": { "enabled": true } }, "pager": { "bottom": false }, "rows": { "striped": { "color": { "even": "#D5D5D5" } } } }, "type": "ia.display.table" } ], "meta": { "name": "Location" }, "position": { "basis": "33%", "grow": 1 }, "props": { "alignContent": "space-between", "direction": "column", "justify": "space-around", "style": { "gap": "0.25rem", "height": "100%", "overflow": "hidden" } }, "type": "ia.container.flex" } ], "meta": { "name": "root" }, "props": { "direction": "column" }, "scripts": { "customMethods": [], "extensionFunctions": null, "messageHandlers": [ { "messageType": "updateTableData", "pageScope": true, "script": "\t# If the data hasn\u0027t loaded, don\u0027t refresh.\n\tif not self.view.custom.isLoaded:\n\t\treturn\n\t\n\t# Write to the manual refresh command and refresh the time.\n\tsystem.tag.writeBlocking(self.view.params.tagPath + \u0027/Config/Manual Refresh Command\u0027, True)\n#\tcurrentTime \u003d \u0027Last Update: *{}*\u0027.format(system.date.format(system.date.now(), \"hh:mm:ss aa\"))\n#\tself.getChild(\"Location\").getChild(\"FlexContainer\").getChild(\"FlexContainer\").getChild(\"updateLabel\").props.source \u003d currentTime\n\t\n#\t\t\n#\ttry:\n#\t\tself.view.refreshBinding(\u0027custom.deviceData\u0027)\n#\texcept:\n#\t\tsystem.perspective.print(\u0027Device data binding disabled.\u0027)", "sessionScope": false, "viewScope": true }, { "messageType": "flashTimerText", "pageScope": true, "script": "\t\"\"\"\n\t\tHelper function called by the \u0027Last Update:\u0027 text to flash when it updates.\n\t\"\"\"\n\tclasses \u003d self.getChild(\"Location\").getChild(\"BreakpointContainer\").getChild(\"LargeHeader\").getChild(\"FlexContainer\").getChild(\"updateLabel\").props.style.classes\n\t\n\t# Refresh the animation by toggling between two identical classes.\n\tif \u0027fadeToBlack_A\u0027 in classes:\n\t\tself.getChild(\"Location\").getChild(\"BreakpointContainer\").getChild(\"LargeHeader\").getChild(\"FlexContainer\").getChild(\"updateLabel\").props.style.classes \u003d \"labels/text-sm, Animation/fadeToBlack_B\"\n\telse:\n\t\tself.getChild(\"Location\").getChild(\"BreakpointContainer\").getChild(\"LargeHeader\").getChild(\"FlexContainer\").getChild(\"updateLabel\").props.style.classes \u003d \"labels/text-sm, Animation/fadeToBlack_A\"", "sessionScope": false, "viewScope": true } ] }, "type": "ia.container.flex" } }