From 604a8312ee30961c9f96012b977e6f373edfcfdf Mon Sep 17 00:00:00 2001 From: jokob-sk Date: Fri, 24 Apr 2026 07:46:09 +1000 Subject: [PATCH 1/2] BE: DEEP_SLEEP #1555 co-author @legionGer Signed-off-by: jokob-sk --- server/__main__.py | 24 +++++++++++++++++++++--- server/api.py | 42 ++++++++++++++++++++++++++++++++++++++++++ server/const.py | 1 + 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/server/__main__.py b/server/__main__.py index 32240f54..1948bd4d 100755 --- a/server/__main__.py +++ b/server/__main__.py @@ -27,7 +27,7 @@ from logger import mylog from helper import filePermissions from utils.datetime_utils import timeNowUTC from app_state import updateState -from api import update_api +from api import update_api, check_activity, update_GUI_port from scan.session_events import process_scan from initialise import importConfigs, renameSettings from database import DB @@ -85,6 +85,9 @@ def main(): # Initialize the WorkflowManager workflow_manager = WorkflowManager(db) + #Run this once to update the defined GUI port for the activity check + update_GUI_port() + # =============================================================================== # This is the main loop of NetAlertX # =============================================================================== @@ -261,8 +264,23 @@ def main(): if userUpdatedDevices: update_api(db, all_plugins, True, ["devices"], userUpdatedDevices) - # loop - time.sleep(5) # wait for N seconds + # ------------------------------------------------------------------ + # Loop with dynamic sleep if enabled (energy saving) + # ------------------------------------------------------------------ + if conf.DEEP_SLEEP: + is_active = check_activity() + + if is_active: + mylog("debug", ["[DEEP_SLEEP] Active Cycle"]) + time.sleep(5) + else: + mylog("debug", ["[DEEP_SLEEP] Passive Cycle"]) + for _ in range(3): + if check_activity(): + break + time.sleep(20) + else: + time.sleep(5) # =============================================================================== diff --git a/server/api.py b/server/api.py index 11834bc4..f1c71b64 100755 --- a/server/api.py +++ b/server/api.py @@ -3,6 +3,7 @@ import json import time import threading import datetime +import os # Register NetAlertX modules import conf @@ -21,6 +22,7 @@ from const import ( sql_notifications_all, sql_online_history, sql_devices_filters, + defaultWebPort, ) from db.db_helper import get_sql_devices_tiles from logger import mylog @@ -34,6 +36,8 @@ from api_server.api_server_start import start_server apiEndpoints = [] +hex_gui_port = None + # Lock for thread safety api_lock = threading.Lock() periodic_write_lock = threading.Lock() @@ -251,3 +255,41 @@ def stop_periodic_write(): periodic_write_thread.join() periodic_write_running = False mylog("trace", ["[API] periodic_write thread stopped."]) + + +def update_GUI_port(): + """ + Grabs the PORT for the webinterface and converts it to HEX to use for activity checks + """ + global hex_gui_port + gui_port_string = port = os.environ.get('PORT', defaultWebPort) + hex_gui_port = ':'+format(int(gui_port_string), '04X') + + +def check_activity(): + """ + Check for active TCP connections on the host. + + Reads `/proc/net/tcp` and looks for entries in the ESTABLISHED state + (state code `01`). If any are found, the system is considered "active", + typically indicating interaction via the web UI or API. + + Returns: + bool: True if at least one established TCP connection exists, + False otherwise or if the check fails. + + Notes: + - Linux-only: relies on `/proc/net/tcp`. + - Lightweight heuristic; does not distinguish connection origin + (e.g., UI vs other services). + - Fail-safe: returns False on any read/parse error. + """ + + try: + with open("/proc/net/tcp", "r") as f: + for line in f: + if hex_gui_port in line and " 01 " in line: + return True + except: + pass + return False \ No newline at end of file diff --git a/server/const.py b/server/const.py index 9762ed69..30e91be5 100755 --- a/server/const.py +++ b/server/const.py @@ -30,6 +30,7 @@ logRoot = LOG_PATH_STR dbFileName = "app.db" confFileName = "app.conf" +defaultWebPort = 20211 confPath = CONFIG_PATH_WITH_TRAILING_SEP + confFileName dbPath = DB_PATH_WITH_TRAILING_SEP + dbFileName From 48e587f580b684136675d4babd4e666a804b80a5 Mon Sep 17 00:00:00 2001 From: jokob-sk Date: Fri, 24 Apr 2026 08:09:47 +1000 Subject: [PATCH 2/2] BE: DEEP_SLEEP #1555 co-author @legionGer Signed-off-by: jokob-sk --- server/api.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/server/api.py b/server/api.py index f1c71b64..b65d23e1 100755 --- a/server/api.py +++ b/server/api.py @@ -262,8 +262,14 @@ def update_GUI_port(): Grabs the PORT for the webinterface and converts it to HEX to use for activity checks """ global hex_gui_port - gui_port_string = port = os.environ.get('PORT', defaultWebPort) - hex_gui_port = ':'+format(int(gui_port_string), '04X') + + gui_port_string = os.environ.get('PORT', str(defaultWebPort)) + try: + port = int(gui_port_string) + except (TypeError, ValueError): + mylog("none", [f"[API] Invalid PORT value '{gui_port_string}', falling back to {defaultWebPort}"]) + port = defaultWebPort + hex_gui_port = ':' + format(port, '04X') def check_activity():