From 54b6b1d408b04733b52fac9449719d7b2041463e Mon Sep 17 00:00:00 2001 From: jokob-sk Date: Sun, 21 Jul 2024 11:42:59 +1000 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8Guess=20icons=20#738?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/plugins/csv_backup/script.py | 2 +- front/plugins/ddns_update/script.py | 2 +- front/plugins/dhcp_leases/script.py | 1 + front/plugins/dhcp_servers/script.py | 1 + front/plugins/internet_speedtest/script.py | 2 +- front/plugins/nmap_scan/script.py | 2 +- front/plugins/pholus_scan/script.py | 2 +- front/plugins/snmp_discovery/script.py | 2 +- front/plugins/undiscoverables/script.py | 2 +- front/plugins/unifi_import/script.py | 1 + front/plugins/vendor_update/script.py | 2 +- front/plugins/website_monitor/script.py | 1 + server/device.py | 224 +++++++++++++++------ server/initialise.py | 2 +- 14 files changed, 170 insertions(+), 76 deletions(-) diff --git a/front/plugins/csv_backup/script.py b/front/plugins/csv_backup/script.py index cc727a3e..8cc9ec9f 100755 --- a/front/plugins/csv_backup/script.py +++ b/front/plugins/csv_backup/script.py @@ -16,7 +16,7 @@ sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64 from logger import mylog, append_line_to_file -from helper import timeNowTZ +from helper import timeNowTZ, get_setting_value from const import logPath, applicationPath, fullDbPath import conf from pytz import timezone diff --git a/front/plugins/ddns_update/script.py b/front/plugins/ddns_update/script.py index 9afaec6c..ea869801 100755 --- a/front/plugins/ddns_update/script.py +++ b/front/plugins/ddns_update/script.py @@ -19,7 +19,7 @@ sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64 from logger import mylog, append_line_to_file -from helper import timeNowTZ, check_IP_format +from helper import timeNowTZ, get_setting_value, check_IP_format from const import logPath, applicationPath, fullDbPath import conf from pytz import timezone diff --git a/front/plugins/dhcp_leases/script.py b/front/plugins/dhcp_leases/script.py index 12fd29f9..1353aad5 100755 --- a/front/plugins/dhcp_leases/script.py +++ b/front/plugins/dhcp_leases/script.py @@ -15,6 +15,7 @@ sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) from plugin_helper import Plugin_Object, Plugin_Objects, handleEmpty, is_mac from logger import mylog from dhcp_leases import DhcpLeases +from helper import timeNowTZ, get_setting_value import conf from pytz import timezone diff --git a/front/plugins/dhcp_servers/script.py b/front/plugins/dhcp_servers/script.py index 4e1e4d3f..28afd207 100755 --- a/front/plugins/dhcp_servers/script.py +++ b/front/plugins/dhcp_servers/script.py @@ -12,6 +12,7 @@ sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) from plugin_helper import Plugin_Objects, Plugin_Object from logger import mylog +from helper import timeNowTZ, get_setting_value import conf from pytz import timezone diff --git a/front/plugins/internet_speedtest/script.py b/front/plugins/internet_speedtest/script.py index ea948d66..5ea90963 100755 --- a/front/plugins/internet_speedtest/script.py +++ b/front/plugins/internet_speedtest/script.py @@ -13,7 +13,7 @@ sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) from plugin_helper import Plugin_Objects from logger import mylog, append_line_to_file -from helper import timeNowTZ +from helper import timeNowTZ, get_setting_value import conf from pytz import timezone diff --git a/front/plugins/nmap_scan/script.py b/front/plugins/nmap_scan/script.py index b93225cf..1dcffbd9 100755 --- a/front/plugins/nmap_scan/script.py +++ b/front/plugins/nmap_scan/script.py @@ -15,7 +15,7 @@ sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64 from logger import mylog, append_line_to_file -from helper import timeNowTZ +from helper import timeNowTZ, get_setting_value from const import logPath, applicationPath import conf from pytz import timezone diff --git a/front/plugins/pholus_scan/script.py b/front/plugins/pholus_scan/script.py index dc0da874..66185878 100755 --- a/front/plugins/pholus_scan/script.py +++ b/front/plugins/pholus_scan/script.py @@ -16,7 +16,7 @@ sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) from logger import mylog from plugin_helper import Plugin_Object, Plugin_Objects -from helper import timeNowTZ +from helper import timeNowTZ, get_setting_value from const import logPath, applicationPath import conf from pytz import timezone diff --git a/front/plugins/snmp_discovery/script.py b/front/plugins/snmp_discovery/script.py index 18c80bc1..0fcb1ea6 100755 --- a/front/plugins/snmp_discovery/script.py +++ b/front/plugins/snmp_discovery/script.py @@ -13,7 +13,7 @@ sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64, handleEmpty, normalize_mac from logger import mylog -from helper import timeNowTZ +from helper import timeNowTZ, get_setting_value from const import logPath, applicationPath import conf from pytz import timezone diff --git a/front/plugins/undiscoverables/script.py b/front/plugins/undiscoverables/script.py index abf22b7b..2ad03361 100755 --- a/front/plugins/undiscoverables/script.py +++ b/front/plugins/undiscoverables/script.py @@ -13,7 +13,7 @@ sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64 from logger import mylog, append_line_to_file -from helper import timeNowTZ +from helper import timeNowTZ, get_setting_value from const import logPath, applicationPath import conf from pytz import timezone diff --git a/front/plugins/unifi_import/script.py b/front/plugins/unifi_import/script.py index fc469a00..94871f08 100755 --- a/front/plugins/unifi_import/script.py +++ b/front/plugins/unifi_import/script.py @@ -21,6 +21,7 @@ sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) from plugin_helper import Plugin_Object, Plugin_Objects from logger import mylog +from helper import timeNowTZ, get_setting_value import conf from pytz import timezone diff --git a/front/plugins/vendor_update/script.py b/front/plugins/vendor_update/script.py index 31c429e3..f1721cb6 100755 --- a/front/plugins/vendor_update/script.py +++ b/front/plugins/vendor_update/script.py @@ -17,7 +17,7 @@ sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64, handleEmpty from logger import mylog, append_line_to_file -from helper import timeNowTZ +from helper import timeNowTZ, get_setting_value from const import logPath, applicationPath, fullDbPath from device import query_MAC_vendor import conf diff --git a/front/plugins/website_monitor/script.py b/front/plugins/website_monitor/script.py index 05d98117..d9a32d07 100755 --- a/front/plugins/website_monitor/script.py +++ b/front/plugins/website_monitor/script.py @@ -15,6 +15,7 @@ sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"]) from plugin_helper import Plugin_Objects from datetime import datetime from const import logPath +from helper import timeNowTZ, get_setting_value import conf from pytz import timezone diff --git a/server/device.py b/server/device.py index 3445920e..e2b24e5f 100755 --- a/server/device.py +++ b/server/device.py @@ -171,40 +171,48 @@ def create_new_devices (db): mylog('debug','[New Devices] 2 Create devices') # default New Device values preparation - newDevColumns = """dev_AlertEvents, - dev_AlertDeviceDown, - dev_PresentLastScan, - dev_Archived, - dev_NewDevice, - dev_SkipRepeated, - dev_ScanCycle, - dev_Owner, - dev_Favorite, - dev_Group, - dev_Comments, - dev_LogEvents, - dev_Location, - dev_Icon""" + newDevColumns = """dev_AlertEvents, + dev_AlertDeviceDown, + dev_PresentLastScan, + dev_Archived, + dev_NewDevice, + dev_SkipRepeated, + dev_ScanCycle, + dev_Owner, + dev_Favorite, + dev_Group, + dev_Comments, + dev_LogEvents, + dev_Location""" - newDevDefaults = f"""{get_setting_value('NEWDEV_dev_AlertEvents')}, - {get_setting_value('NEWDEV_dev_AlertDeviceDown')}, - {get_setting_value('NEWDEV_dev_PresentLastScan')}, - {get_setting_value('NEWDEV_dev_Archived')}, - {get_setting_value('NEWDEV_dev_NewDevice')}, - {get_setting_value('NEWDEV_dev_SkipRepeated')}, - {get_setting_value('NEWDEV_dev_ScanCycle')}, - '{get_setting_value('NEWDEV_dev_Owner')}', - {get_setting_value('NEWDEV_dev_Favorite')}, - '{get_setting_value('NEWDEV_dev_Group')}', - '{get_setting_value('NEWDEV_dev_Comments')}', - {get_setting_value('NEWDEV_dev_LogEvents')}, - '{get_setting_value('NEWDEV_dev_Location')}', - '{get_setting_value('NEWDEV_dev_Icon')}' - """ + newDevDefaults = f"""{get_setting_value('NEWDEV_dev_AlertEvents')}, + {get_setting_value('NEWDEV_dev_AlertDeviceDown')}, + {get_setting_value('NEWDEV_dev_PresentLastScan')}, + {get_setting_value('NEWDEV_dev_Archived')}, + {get_setting_value('NEWDEV_dev_NewDevice')}, + {get_setting_value('NEWDEV_dev_SkipRepeated')}, + {get_setting_value('NEWDEV_dev_ScanCycle')}, + '{get_setting_value('NEWDEV_dev_Owner')}', + {get_setting_value('NEWDEV_dev_Favorite')}, + '{get_setting_value('NEWDEV_dev_Group')}', + '{get_setting_value('NEWDEV_dev_Comments')}', + {get_setting_value('NEWDEV_dev_LogEvents')}, + '{get_setting_value('NEWDEV_dev_Location')}'""" - # Bulk-inserting devices from the CurrentScan table as new devices in the table Devices ... - # ... with new device defaults and ignoring specidfied IPs and MACs) - sqlQuery = f"""INSERT OR IGNORE INTO Devices + # Fetch data from CurrentScan + current_scan_data = sql.execute("SELECT cur_MAC, cur_Name, cur_Vendor, cur_IP, cur_SyncHubNodeName, cur_NetworkNodeMAC, cur_PORT, cur_NetworkSite, cur_SSID, cur_Type FROM CurrentScan").fetchall() + + for row in current_scan_data: + cur_MAC, cur_Name, cur_Vendor, cur_IP, cur_SyncHubNodeName, cur_NetworkNodeMAC, cur_PORT, cur_NetworkSite, cur_SSID, cur_Type = row + + # Handle NoneType + cur_Name = cur_Name.strip() if cur_Name else '(unknown)' + cur_Type = cur_Type.strip() if cur_Type else get_setting_value("NEWDEV_dev_DeviceType") + cur_NetworkNodeMAC = cur_NetworkNodeMAC.strip() if cur_NetworkNodeMAC else '' + cur_NetworkNodeMAC = cur_NetworkNodeMAC if cur_NetworkNodeMAC and cur_MAC != "Internet" else (get_setting_value("NEWDEV_dev_Network_Node_MAC_ADDR") if cur_MAC != "Internet" else "null") + + # Preparing the individual insert statement + sqlQuery = f"""INSERT OR IGNORE INTO Devices ( dev_MAC, dev_name, @@ -221,44 +229,28 @@ def create_new_devices (db): dev_DeviceType, {newDevColumns} ) - SELECT - cur_MAC, - CASE - WHEN LENGTH(TRIM(cur_Name)) > 0 THEN cur_Name ELSE '(unknown)' - END, - cur_Vendor, - cur_IP, + VALUES + ( + '{cur_MAC}', + '{cur_Name}', + '{cur_Vendor}', + '{cur_IP}', ?, ?, - cur_SyncHubNodeName, - {sql_generateGuid}, - CASE - WHEN LENGTH(TRIM(cur_NetworkNodeMAC)) > 0 - AND cur_MAC != 'Internet' - THEN cur_NetworkNodeMAC - ELSE - CASE - WHEN cur_MAC = 'Internet' - THEN 'null' - ELSE '{get_setting_value('NEWDEV_dev_Network_Node_MAC_ADDR')}' - END - END, - cur_PORT, - cur_NetworkSite, - cur_SSID, - CASE - WHEN LENGTH(TRIM(cur_Type)) > 0 THEN cur_Type ELSE '{get_setting_value('NEWDEV_dev_DeviceType')}' - END, + '{cur_SyncHubNodeName}', + {sql_generateGuid}, + '{cur_NetworkNodeMAC}', + '{cur_PORT}', + '{cur_NetworkSite}', + '{cur_SSID}', + '{cur_Type}', {newDevDefaults} - FROM CurrentScan - WHERE 1=1 - {list_to_where('OR', 'cur_MAC', 'NOT LIKE', get_setting_value('NEWDEV_ignored_MACs'))} - {list_to_where('OR', 'cur_IP', 'NOT LIKE', get_setting_value('NEWDEV_ignored_IPs'))} - """ + )""" - mylog('debug',f'[New Devices] Create devices SQL: {sqlQuery}') + mylog('trace', f'[New Devices] Create device SQL: {sqlQuery}') + + sql.execute(sqlQuery, (startTime, startTime)) - sql.execute (sqlQuery, (startTime, startTime) ) mylog('debug','[New Devices] New Devices end') db.commitDB() @@ -410,6 +402,7 @@ def update_devices_data_from_scan (db): AND cur_Name <> '' ) """) + # Update VENDORS recordsToUpdate = [] query = """SELECT * FROM Devices WHERE dev_Vendor = '(unknown)' OR dev_Vendor ='' @@ -420,8 +413,24 @@ def update_devices_data_from_scan (db): if vendor != -1 and vendor != -2 : recordsToUpdate.append ([vendor, device['dev_MAC']]) - sql.executemany ("UPDATE Devices SET dev_Vendor = ? WHERE dev_MAC = ? ", - recordsToUpdate ) + if len(recordsToUpdate) > 0: + sql.executemany ("UPDATE Devices SET dev_Vendor = ? WHERE dev_MAC = ? ", recordsToUpdate ) + + # Guess ICONS + recordsToUpdate = [] + query = """SELECT * FROM Devices + WHERE dev_Icon in ('', 'null') + OR dev_Icon IS NULL""" + default_icon = get_setting_value('NEWDEV_dev_Icon') + + for device in sql.execute (query) : + # Conditional logic for dev_Icon guessing + dev_Icon = guess_icon(device['dev_Vendor'], device['dev_MAC'], device['dev_LastIP'], device['dev_Name'], default_icon) + + recordsToUpdate.append ([dev_Icon, device['dev_MAC']]) + + if len(recordsToUpdate) > 0: + sql.executemany ("UPDATE Devices SET dev_Icon = ? WHERE dev_MAC = ? ", recordsToUpdate ) mylog('debug','[Update Devices] Update devices end') @@ -582,3 +591,84 @@ def query_MAC_vendor (pMAC): mylog('none', [f"[Vendor Check] ⚠ ERROR: Vendors file {vendorsPath} not found."]) return -1 + +#=============================================================================== +# Icons +#=============================================================================== +#------------------------------------------------------------------------------- +# Base64 encoded HTML string for FontAwesome icons +icons = { + "globe": "PGkgY2xhc3M9ImZhcyBmYS1nbG9iZSI+PC9pPg==", # globe icon + "phone": "PGkgY2xhc3M9ImZhcyBmYS1tb2JpbGUtYWx0Ij48L2k+", + "laptop": "PGkgY2xhc3M9ImZhIGZhLWxhcHRvcCI+PC9pPg==", + "printer": "PGkgY2xhc3M9ImZhIGZhLXByaW50ZXIiPjwvaT4=", + "router": "PGkgY2xhc3M9ImZhcyBmYS1yYW5kb20iPjwvaT4=", + "tv": "PGkgY2xhc3M9ImZhIGZhLXR2Ij48L2k+", + "desktop": "PGkgY2xhc3M9ImZhIGZhLWRlc2t0b3AiPjwvaT4=", + "tablet": "PGkgY2xhc3M9ImZhIGZhLXRhYmxldCI+PC9pPg==", + "watch": "PGkgY2xhc3M9ImZhIGZhLXdhbmNoIj48L2k+", + "camera": "PGkgY2xhc3M9ImZhIGZhLWNhbWVyYSI+PC9pPg==", + "home": "PGkgY2xhc3M9ImZhIGZhLWhvbWUiPjwvaT4=", + "apple": "PGkgY2xhc3M9ImZhYiBmYS1hcHBsZSI+PC9pPg==", + "ethernet": "PGkgY2xhc3M9ImZhcyBmYS1ldGhlcm5ldCI+PC9pPg==", + "google": "PGkgY2xhc3M9ImZhYiBmYS1nb29nbGUiPjwvaT4=", + "raspberry": "PGkgY2xhc3M9ImZhYiBmYS1yYXNwYmVycnktcGkiPjwvaT4=", + "microchip": "PGkgY2xhc3M9ImZhcyBmYS1taWNyb2NoaXAiPjwvaT4=" +} + +#------------------------------------------------------------------------------- +# Guess device icon +def guess_icon(vendor, mac, ip, name, default): + result = default + mac = mac.upper() + vendor = vendor.lower() + name = name.lower() + + # Guess icon based on vendor + if any(brand in vendor for brand in {"samsung", "motorola"}): + result = icons.get("phone") + elif "dell" in vendor: + result = icons.get("laptop") + elif "hp" in vendor: + result = icons.get("printer") + elif "cisco" in vendor: + result = icons.get("router") + elif "lg" in vendor: + result = icons.get("tv") + elif "raspberry" in vendor: + result = icons.get("raspberry") + elif "apple" in vendor: + result = icons.get("apple") + elif "google" in vendor: + result = icons.get("google") + elif "ubiquiti" in vendor: + result = icons.get("router") + elif any(brand in vendor for brand in {"espressif"}): + result = icons.get("microchip") + + # Guess icon based on MAC address patterns + elif mac == "INTERNET": # Apple + result = icons.get("globe") + elif mac.startswith("00:1A:79"): # Apple + result = icons.get("apple") + elif mac.startswith("B0:BE:83"): # Apple + result = icons.get("apple") + elif mac.startswith("00:1B:63"): # Sony + result = icons.get("tablet") + elif mac.startswith("74:AC:B9"): # Unifi + result = icons.get("ethernet") + + + # Guess icon based on name + elif 'google' in name: + result = icons.get("google") + elif 'desktop' in name: + result = icons.get("desktop") + + # Guess icon based on IP address ranges + elif ip.startswith("192.168.1."): + result = icons.get("laptop") + + + return result + \ No newline at end of file diff --git a/server/initialise.py b/server/initialise.py index a53b84ba..e6cabe66 100755 --- a/server/initialise.py +++ b/server/initialise.py @@ -145,7 +145,7 @@ def importConfigs (db, all_plugins): # UI conf.UI_LANG = ccd('UI_LANG', 'English' , c_d, 'Language Interface', '{"dataType":"string", "elements": [{"elementType" : "select", "elementOptions" : [] ,"transformers": []}]}', "['English', 'French', 'German', 'Norwegian', 'Russian', 'Spanish', 'Italian (it_it)', 'Portuguese (pt_br)', 'Polish (pl_pl)', 'Turkish (tr_tr)', 'Chinese (zh_cn)' ]", 'UI') conf.UI_NOT_RANDOM_MAC = ccd('UI_NOT_RANDOM_MAC', [] , c_d, 'Exlude from Random Prefix', '{"dataType": "array","elements": [ {"elementType": "input","elementOptions": [{ "placeholder": "Enter value" },{ "suffix": "_in" },{ "cssClasses": "col-sm-10" },{ "prefillValue": "null" }],"transformers": [] }, {"elementType": "button","elementOptions": [{ "sourceSuffixes": ["_in"] },{ "separator": "" },{ "cssClasses": "col-xs-12" },{ "onClick": "addList(this, false)" },{ "getStringKey": "Gen_Add" }],"transformers": [] }, {"elementType": "button","elementOptions": [{ "sourceSuffixes": [] },{ "separator": "" },{ "cssClasses": "col-xs-6" },{ "onClick": "removeAllOptions(this)" },{ "getStringKey": "Gen_Remove_All" }],"transformers": []},{"elementType": "button","elementOptions": [{ "sourceSuffixes": [] },{ "separator": "" },{ "cssClasses": "col-xs-6" },{ "onClick": "removeFromList(this)" },{ "getStringKey": "Gen_Remove_Last" }],"transformers": []}, {"elementType": "select","elementOptions": [{ "multiple": "true" },{ "readonly": "true" },{ "editable": "true" }],"transformers": [] }]}', "[]", 'UI') - conf.UI_ICONS = ccd('UI_ICONS', ['PGkgY2xhc3M9ImZhIGZhLWNvbXB1dGVyIj48L2k+', 'PGkgY2xhc3M9ImZhIGZhLWV0aGVybmV0Ij48L2k+', 'PGkgY2xhc3M9ImZhIGZhLWdhbWVwYWQiPjwvaT4', 'PGkgY2xhc3M9ImZhIGZhLWdsb2JlIj48L2k+', 'PGkgY2xhc3M9ImZhIGZhLWxhcHRvcCI+PC9pPg==', 'PGkgY2xhc3M9ImZhIGZhLWxpZ2h0YnVsYiI+PC9pPg==', 'PGkgY2xhc3M9ImZhIGZhLXNoaWVsZCI+PC9pPg==', 'PGkgY2xhc3M9ImZhIGZhLXdpZmkiPjwvaT4'] , c_d, 'Icons', '{"dataType": "array","elements": [ {"elementType": "input","elementOptions": [{ "placeholder": "Enter value" },{ "suffix": "_in" },{ "cssClasses": "col-sm-10" },{ "prefillValue": "null" }],"transformers": [] }, {"elementType": "button","elementOptions": [{ "sourceSuffixes": ["_in"] },{ "separator": "" },{ "cssClasses": "col-xs-12" },{ "onClick": "addList(this, false)" },{ "getStringKey": "Gen_Add" }],"transformers": [] }, {"elementType": "button","elementOptions": [{ "sourceSuffixes": [] },{ "separator": "" },{ "cssClasses": "col-xs-6" },{ "onClick": "removeAllOptions(this)" },{ "getStringKey": "Gen_Remove_All" }],"transformers": []},{"elementType": "button","elementOptions": [{ "sourceSuffixes": [] },{ "separator": "" },{ "cssClasses": "col-xs-6" },{ "onClick": "removeFromList(this)" },{ "getStringKey": "Gen_Remove_Last" }],"transformers": []}, {"elementType": "select","elementOptions": [{ "multiple": "true" },{ "readonly": "true" },{ "editable": "true" }],"transformers": [] }]}', "[]", 'UI') + conf.UI_ICONS = ccd('UI_ICONS', ['PGkgY2xhc3M9J2ZhIGZhLXdpZmknPjwvaT4=', 'PGkgY2xhc3M9ImZhIGZhLWNvbXB1dGVyIj48L2k+', 'PGkgY2xhc3M9ImZhIGZhLWV0aGVybmV0Ij48L2k+', 'PGkgY2xhc3M9ImZhIGZhLWdhbWVwYWQiPjwvaT4', 'PGkgY2xhc3M9ImZhIGZhLWdsb2JlIj48L2k+', 'PGkgY2xhc3M9ImZhIGZhLWxhcHRvcCI+PC9pPg==', 'PGkgY2xhc3M9ImZhIGZhLWxpZ2h0YnVsYiI+PC9pPg==', 'PGkgY2xhc3M9ImZhIGZhLXNoaWVsZCI+PC9pPg==', 'PGkgY2xhc3M9ImZhIGZhLXdpZmkiPjwvaT4', 'PGkgY2xhc3M9J2ZhIGZhLWdhbWVwYWQnPjwvaT4'] , c_d, 'Icons', '{"dataType": "array","elements": [ {"elementType": "input","elementOptions": [{ "placeholder": "Enter value" },{ "suffix": "_in" },{ "cssClasses": "col-sm-10" },{ "prefillValue": "null" }],"transformers": [] }, {"elementType": "button","elementOptions": [{ "sourceSuffixes": ["_in"] },{ "separator": "" },{ "cssClasses": "col-xs-12" },{ "onClick": "addList(this, false)" },{ "getStringKey": "Gen_Add" }],"transformers": [] }, {"elementType": "button","elementOptions": [{ "sourceSuffixes": [] },{ "separator": "" },{ "cssClasses": "col-xs-6" },{ "onClick": "removeAllOptions(this)" },{ "getStringKey": "Gen_Remove_All" }],"transformers": []},{"elementType": "button","elementOptions": [{ "sourceSuffixes": [] },{ "separator": "" },{ "cssClasses": "col-xs-6" },{ "onClick": "removeFromList(this)" },{ "getStringKey": "Gen_Remove_Last" }],"transformers": []}, {"elementType": "select","elementOptions": [{ "multiple": "true" },{ "readonly": "true" },{ "editable": "true" }],"transformers": [] }]}', "[]", 'UI') conf.UI_REFRESH = ccd('UI_REFRESH', 0 , c_d, 'Refresh interval', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', "[]", 'UI') conf.UI_DEV_SECTIONS = ccd('UI_DEV_SECTIONS', [] , c_d, 'Show sections', '{"dataType":"array", "elements": [{"elementType" : "select", "elementOptions" : [{"multiple":"true"}] ,"transformers": []}]}', "['Tile Cards', 'Device Presence']", 'UI') conf.UI_PRESENCE = ccd('UI_PRESENCE', ['online', 'offline', 'archived'] , c_d, 'Include in presence', '{"dataType":"array", "elements": [{"elementType" : "select", "elementOptions" : [{"multiple":"true"}] ,"transformers": []}]}', "['online', 'offline', 'archived']", 'UI')