Compare commits

...

7 Commits

Author SHA1 Message Date
jokob-sk
a69f72fd3d Merge pull request #745 from FlyingToto/main
Some checks are pending
docker / docker_dev (push) Waiting to run
minor fix: able to handle an empty omada devices list
2024-07-21 12:36:43 +10:00
jokob-sk
df40b5caf9 🔀Guess types #738 2024-07-21 12:04:42 +10:00
jokob-sk
54b6b1d408 🎨Guess icons #738 2024-07-21 11:42:59 +10:00
FlyingToto
d79acc7bad Merge branch 'jokob-sk:main' into main 2024-07-20 13:36:10 -04:00
ffsb
57d8db771a Merge branch 'main' of https://github.com/FlyingToto/NetAlertX 2024-07-20 13:35:38 -04:00
ffsb
43759b6b7d na 2024-07-20 13:28:43 -04:00
ffsb
3305828947 1.1 handle failed omada devices calls. 2024-07-20 13:26:49 -04:00
17 changed files with 466 additions and 79 deletions

View File

@@ -673,7 +673,9 @@
(!emptyArr.includes(nodeData.data.port )) ? port = nodeData.data.port : port = "";
(port == "" || port == 0 ) ? portBckgIcon = `<i class="fa fa-wifi"></i>` : portBckgIcon = `<i class="fa fa-ethernet"></i>`;
(port == "" || port == 0 || port == 'None' ) ? portBckgIcon = `<i class="fa fa-wifi"></i>` : portBckgIcon = `<i class="fa fa-ethernet"></i>`;
portHtml = (port == "" || port == 0 || port == 'None' ) ? "" : port
// Build HTML for individual nodes in the network diagram
deviceIcon = (!emptyArr.includes(nodeData.data.icon )) ?
@@ -682,7 +684,7 @@
</div>` : "";
devicePort = `<div class="netPort"
style="width:${emSize*sizeCoefficient}em;height:${emSize*sizeCoefficient}em">
${port}</div>
${portHtml}</div>
<div class="portBckgIcon"
style="margin-left:-${emSize*sizeCoefficient}em;">
${portBckgIcon}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -6,6 +6,8 @@ __version__ = "0.3" # split devices API calls to allow multithreading but had t
__version__ = "0.6" # found issue with multithreading - my omada calls redirect stdout which gets clubbered by normal stdout... not sure how to fix for now...
__version__ = "0.7" # avoid updating omada sdn client name when it is the MAC, and naxname is also the same MAC...
__version__ = "1.0" # fixed the timzone mylog issue by resetting the tz value at the begining of the script... I suspect it doesn't inherit the tz from the main.
__version__ = "1.1" # added logic to handle gracefully a failure of omada devices so it won't try to populate uplinks on non-existent switches and AP.
# query OMADA SDN to populate NetAlertX witch omada switches, access points, clients.
# try to identify and populate their connections by switch/accesspoints and ports/SSID
@@ -454,7 +456,8 @@ def get_device_data(omada_clients_output,switches_and_aps,device_handler):
# step4, let's go recursively through switches other links to mark update their uplinks
# and pray it ends one day...
#
add_uplink(default_router_mac,first_switch, device_data_bymac,sadevices_linksbymac,port_byswitchmac_byclientmac)
if len(sadevices) > 0:
add_uplink(default_router_mac,first_switch, device_data_bymac,sadevices_linksbymac,port_byswitchmac_byclientmac)
return device_data_bymac.values()
if __name__ == '__main__':

View File

@@ -0,0 +1,238 @@
import re
""""
how to rebuild and re-run...
savefolder=~/naxdev/NetAlertX.v7
cd ~/naxdev
mv NetAlertX $savefolder
gh repo clone FlyingToto/NetAlertX
cd NetAlertX
ln -s ../docker-compose.yml.ffsb42 .
ln -s ../.env.omada.ffsb42 .
cd front/plugins/omada_sdn_imp/
cp -p $savefoder/front/plugins/omada_sdn_imp/omada_sdn.py* .
cp -p $savefoder/front/plugins/omada_sdn_imp/README.md .
cp -p $savefoder/front/plugins/omada_sdn_imp/omada_account_sample.png .
cp -p $savefoder/front/plugins/omada_sdn_imp/testre.py .
#cp -p $savefoder/front/plugins/omada_sdn_imp/config.json config.json.v6
cd ~/naxdev/NetAlertX
sudo docker-compose --env-file .env.omada.ffsb42 -f ./docker-compose.yml.ffsb42 up
to gather data for Boris:
today=$(date +%Y_%m_%d__%H_%M)
mkdir /drives/c/temp/4boris/$today
cd /drives/c/temp/4boris/$today
scp hal:~/naxdev/logs/app.log .
scp hal:~/naxdev/NetAlertX/front/plugins/omada_sdn_imp/* .
gzip -c app.log > app_$today.log.gz
scp hal:~/naxdev/NetAlertX/front/plugins/omada_sdn_imp/omada_sdn.py /drives/c/temp/4boris/
"""
def extract_mac_addresses(text):
mac_pattern = r"([0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2})"
#mac_pattern = r'([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})'
#r"(([0-9A-F]{2}-){5}[0-9A-F]{2})"
#r"([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})"
#r"([0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2})"
mac_addresses = re.findall(mac_pattern, text)
return ["".join(parts) for parts in mac_addresses]
# Example usage:
foo = """
Name: office
Address: 0C-80-63-69-C4-D1 (192.168.0.5)
Status: CONNECTED (CONNECTED)
Ports: 28
Supports PoE: False
Model: T1600G-28TS v3.0
LED Setting: SITE_SETTINGS
Uptime: 5day(s) 22h 39m 6s
Uplink switch: D8-07-B6-71-FF-7F office24
Downlink devices:
- 40-AE-30-A5-A7-50 ompapaoffice
- B0-95-75-46-0C-39 pantry12
"""
mac_list = extract_mac_addresses(foo)
print("mac list",mac_list)
# ['0C-80-63-69-C4-D1', 'D8-07-B6-71-FF-7F', '40-AE-30-A5-A7-50', 'B0-95-75-46-0C-39']
# ['C4-:D1', 'FF-:7F', 'A7-:50', '0C-:39']
linked_switches_and_ports_by_mac = {}
foo = """"
something
some BOB12
blah BOB23
--- BEGIN ---
something else BOB12
blah BOB23
--- END ---
"""
def extract_BOB_patterns(foo):
pattern = r"BOB\d{2}(?=.*BEGIN)"
matches = re.findall(pattern, foo, re.DOTALL)
return matches
BOBresult = extract_BOB_patterns(foo)
print("BOB:",BOBresult) # Output: ['BOB12', 'BOB23']
#0C-80-63-69-C4-D1
clientmac_by_switchmac_by_switchportSSID = {}
switch_mac_and_ports_by_clientmac = {}
def extract_uplinks_mac_and_ports(tplink_device_dump):
mac_switches = []
mac_pattern = r"([0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2}[:-][0-9A-Fa-f]{2})(?=.*BEGIN)"
mac_addresses = re.findall(mac_pattern, tplink_device_dump,re.DOTALL)
mac_switches = ["".join(parts) for parts in mac_addresses]
print(" mac_switches1=",mac_switches)
mymac = mac_switches[0]
mylinks = mac_switches[1:]
for mylink in mylinks:
port_pattern = r"(?=\{.*\"port\"\: )([0-9]+)(?=.*"+re.escape(mylink)+r")"
port_pattern = r"(?:{/s\"port\"\: )([0-9]+)(?:[!\}].*"+re.escape(mylink)+r")"
#port_pattern = rf"{{.*?{found_mac}.*?port\s*:\s*(\d+).*?}}"
#port_pattern = rf"{{.*?.*?port\s*:\s*(\d+)[!\\}]*{mylink}?}}"
port_pattern = r"(?:\{[!\}]port/s:/s)([0-9]+\,)(?:[!\}]*"+re.escape(mylink)+r"[!\{]*\})"
#port_pattern = r"(?:\{.*\"port\"\: )([0-9]+)(?=.*"+re.escape(mylink)+r")"
port_pattern = r"(?:{[^}]*\"port\"\: )([0-9]+)(?=[^}]*"+re.escape(mylink)+r")"
myport = re.findall(port_pattern, tplink_device_dump,re.DOTALL)
print("myswitch=",mymac, "- link_switch=", mylink, "myport=", myport)
return(0)
'''
with open('/tmp/switch.bigroom.dump.json', 'r') as file:
foo3 = file_content = file.read()
print("bigroom", end="")
extract_uplinks_mac_and_ports(foo3)
with open('/tmp/switch.office.dump.json', 'r') as file:
foo4 = file_content = file.read()
print("office", end="")
extract_uplinks_mac_and_ports(foo4)
'''
import netifaces
gw = netifaces.gateways()
print(gw['default'][netifaces.AF_INET][0])
d = {'a': ['0', 'Arthur'], 'b': ['foo', 'Belling']}
print(d.items())
print(d.keys())
print(d.values())
foo = 2
#while foo > 0:
# foo = 'toto'
print("foo is ",foo)
if foo in ( 'bar', '', 'null'):
print("foo is bar")
else:
print("foo is not bar")
foo='192-168-0-150.local'
bar = foo.split('.')[0]
print("bar=",bar,"-")
bar2 = 'toto'
print("bar2=",bar2,"-")
import concurrent.futures
import time
import random
def phello(arg):
print('running phell',arg)
delay = random.uniform(0, 6)
time.sleep(delay)
return f"parallel hello : {arg}", delay
def testparalel():
arguments = ["Alice", "Bob", "Charlie", "David"]
results = {}
results2 = {}
para = 10
# Using ThreadPoolExecutor for parallel execution
with concurrent.futures.ThreadPoolExecutor(max_workers=para) as executor:
# Submit tasks to the executor
future_to_arg = {executor.submit(phello, arg): arg for arg in arguments}
# Wait for all futures to complete
done, _ = concurrent.futures.wait(future_to_arg)
# Retrieve results
for future in done:
arg = future_to_arg[future]
try:
result, result2 = future.result()
results[arg] = result
results2[arg] = result2
except Exception as exc:
print(f"{arg} generated an exception: {exc}")
# Print results after all threads have completed
print("All threads completed. Results:")
for arg, result in results.items():
print(f"arg:{arg}, result={results[arg]}, result2={results2[arg]}")
#testparalel()
import netifaces
import ipaddress
def get_interfaces_and_subnets():
result = []
for interface in netifaces.interfaces():
addrs = netifaces.ifaddresses(interface)
if netifaces.AF_INET in addrs:
for addr in addrs[netifaces.AF_INET]:
ip = addr['addr']
mask = addr['netmask']
try:
network = ipaddress.IPv4Network(f"{ip}/{mask}", strict=False)
result.append((interface, str(network)))
except ValueError:
pass
return result
# Example usage:
interfaces_and_subnets = get_interfaces_and_subnets()
for interface, subnet in interfaces_and_subnets:
print(f"interface={interface}, subnet={subnet}")
'''
interface=lo, subnet=127.0.0.0/8
interface=enp6s0, subnet=192.168.0.0/24
interface=br-ba0070d71f2a, subnet=172.16.0.16/29
interface=br-bc4a4c4e0f93, subnet=172.16.0.40/29
interface=br-e043e0ae9c8c, subnet=172.16.0.72/29
interface=br-6acc3945cfba, subnet=172.16.0.48/29
interface=br-6f931807e709, subnet=172.16.0.80/29
interface=docker0, subnet=172.17.0.0/16
interface=br-9ce2cb7c38c3, subnet=172.16.0.24/29
interface=br-eec81501f666, subnet=172.16.0.32/29
interface=br-1064712a4791, subnet=172.16.0.56/29
interface=br-a93ebdba2a28, subnet=172.16.0.8/29
interface=br-d8fa7a3015e2, subnet=172.16.0.64/29
interface=br-e7cdd041d3d3, subnet=172.16.0.0/29
'''

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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,40 @@ 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 )
# Guess Type
recordsToUpdate = []
query = """SELECT * FROM Devices
WHERE dev_DeviceType in ('', 'null')
OR dev_DeviceType IS NULL"""
default_type = get_setting_value('NEWDEV_dev_DeviceType')
for device in sql.execute (query) :
# Conditional logic for dev_Icon guessing
dev_DeviceType = guess_type(device['dev_Vendor'], device['dev_MAC'], device['dev_LastIP'], device['dev_Name'], default_type)
recordsToUpdate.append ([dev_DeviceType, device['dev_MAC']])
if len(recordsToUpdate) > 0:
sql.executemany ("UPDATE Devices SET dev_DeviceType = ? WHERE dev_MAC = ? ", recordsToUpdate )
mylog('debug','[Update Devices] Update devices end')
@@ -582,3 +607,118 @@ 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
#-------------------------------------------------------------------------------
# Guess device type
def guess_type(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 = "Phone"
elif "cisco" in vendor:
result = "Router"
elif "lg" in vendor:
result = "TV"
elif "google" in vendor:
result = "Phone"
elif "ubiquiti" in vendor:
result = "Router"
# Guess type based on MAC address patterns
elif mac == "INTERNET":
result = "Internet"
# Guess type based on name
elif 'google' in name:
result = "Phone"
# Guess type based on IP address ranges
elif ip == ("192.168.1.1"):
result = "Router"
return result

View File

@@ -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')