From c9b76df6271308d887de26a37c74d4ca2fc658b2 Mon Sep 17 00:00:00 2001 From: frytimo Date: Wed, 24 Sep 2025 17:27:10 -0300 Subject: [PATCH] Create network usage dashboard widget (#7531) * Add new dashboard widget system_network_status * Add new dashboard widget system_network_status * Initial working commit * Fix max_points used twice * Update system dashboard service to send network traffic * Remove console logging used for debugging * Remove interface check * Return instead of exit on a dashboard widget * Use default value instead of null coalescing operator * Disable the details state (to be added later) * Set to Line type only * Update config.php --------- Co-authored-by: FusionPBX --- app/system/app_config.php | 4 + .../classes/system_dashboard_service.php | 76 ++++++- app/system/resources/dashboard/config.php | 39 ++++ .../dashboard/system_network_status.php | 206 ++++++++++++++++++ .../resources/javascript/ws_client.js | 2 +- 5 files changed, 322 insertions(+), 5 deletions(-) create mode 100644 app/system/resources/dashboard/system_network_status.php diff --git a/app/system/app_config.php b/app/system/app_config.php index 67d7f495e..254d286d5 100644 --- a/app/system/app_config.php +++ b/app/system/app_config.php @@ -66,6 +66,10 @@ $apps[$x]['permissions'][$y]['menu']['uuid'] = "7497ade1-b6f4-473d-9859-92c957c36503"; $apps[$x]['permissions'][$y]['groups'][] = "superadmin"; $y++; + $apps[$x]['permissions'][$y]['name'] = "system_view_network"; + $apps[$x]['permissions'][$y]['menu']['uuid'] = "5243e0d2-0e8b-277a-912e-9d8b5fcdb41d"; + $apps[$x]['permissions'][$y]['groups'][] = "superadmin"; + $y++; $apps[$x]['permissions'][$y]['name'] = "software_add"; //$apps[$x]['permissions'][$y]['groups'][] = "superadmin"; $y++; diff --git a/app/system/resources/classes/system_dashboard_service.php b/app/system/resources/classes/system_dashboard_service.php index 5a0cf52ea..72c293f7e 100644 --- a/app/system/resources/classes/system_dashboard_service.php +++ b/app/system/resources/classes/system_dashboard_service.php @@ -11,9 +11,11 @@ class system_dashboard_service extends base_websocket_system_service { 'system_view_memcache', 'system_view_ram', 'system_view_support', + 'system_view_network', ]; const CPU_STATUS_TOPIC = 'cpu_status'; + const NETWORK_STATUS_TOPIC = "network_status"; /** * @@ -33,6 +35,9 @@ class system_dashboard_service extends base_websocket_system_service { */ private $cpu_status_refresh_interval; + private $network_status_refresh_interval; + private $network_interface; + protected function reload_settings(): void { static::set_system_information(); @@ -45,11 +50,17 @@ class system_dashboard_service extends base_websocket_system_service { // Connect to the database $database = new database(['config' => parent::$config]); - // get the interval + // get the settings using global defaults $this->settings = new settings(['database' => $database]); - // get the settings from the global defaults + // get the cpu interval $this->cpu_status_refresh_interval = $this->settings->get('dashboard', 'cpu_status_refresh_interval', 3); + + // get the network interval + $this->network_status_refresh_interval = $this->settings->get('dashboard', 'network_status_refresh_interval', 3); + + // get the network card to watch + $this->network_interface = $this->settings->get('dashboard', 'network_interface', 'eno1'); } /** @@ -60,6 +71,9 @@ class system_dashboard_service extends base_websocket_system_service { // Send the CPU status $this->on_cpu_status(); + // Send the network average + $this->on_network_status(); + // Reset the timer $this->set_timer($this->cpu_status_refresh_interval); } @@ -77,13 +91,67 @@ class system_dashboard_service extends base_websocket_system_service { self::$system_information = system_information::new(); // Register the call back to respond to cpu_status requests - $this->on_topic('cpu_status', [$this, 'on_cpu_status']); + $this->on_topic(self::CPU_STATUS_TOPIC, [$this, 'on_cpu_status']); + + // Register the call back to respond to network_status requests + $this->on_topic(self::NETWORK_STATUS_TOPIC, [$this, 'on_cpu_status']); // Set a timer $this->set_timer($this->cpu_status_refresh_interval); // Notify the user of the interval $this->info("Broadcasting CPU Status every {$this->cpu_status_refresh_interval}s"); + $this->info("Broadcasting Network Status every {$this->network_status_refresh_interval}s"); + } + + public function on_network_status($message = null): void { + // Get RX (receive) and TX (transmit) bps + $network_rates = self::$system_information->get_network_speed($this->network_interface); + + // Prepare a response + $response = new websocket_message(); + $response + ->payload([self::NETWORK_STATUS_TOPIC => $network_rates]) + ->service_name(self::get_service_name()) + ->topic(self::NETWORK_STATUS_TOPIC) + ; + if ($message !== null && $message instanceof websocket_message) { + $this->debug("Responding to message request id: ".$message->id()); + $response->id($message->id()); + } + + // Log for debugging + $this->debug(sprintf( + "Broadcasting Network interface %s of RX %d bps, TX %d bps", + $this->network_interface, + $network_rates['rx_bps'], + $network_rates['tx_bps'] + )); + + $show_disconnect_message = true; + try { + // Send the broadcast + $this->respond($response); + } catch (\socket_disconnected_exception $sde) { + // wait until we connect again + while (!$this->connect_to_ws_server()) { + if ($show_disconnect_message) { + $this->warn("Websocket server disconnected"); + $show_disconnect_message = false; + } + sleep(1); + } + $this->warn("Websocket server connected"); + } + } + + public function on_network_interface_select($message = null): void { + if ($message !== null && $message instanceof websocket_message) { + $payload = $message->payload(); + if (!empty($payload['network_interface'])) { + $this->network_interface = ['network_interface']; + } + } } public function on_cpu_status($message = null): void { @@ -143,7 +211,7 @@ class system_dashboard_service extends base_websocket_system_service { $permissions = $subscriber->get_permissions(); // Create a filter for broadcaster => permission - $permission_filter = new permission_filter([self::get_service_name() => 'system_view_cpu']); + $permission_filter = new permission_filter([self::get_service_name() => 'system_view_cpu', self::get_service_name() => 'system_view_network']); // Match them to create a filter foreach (self::PERMISSIONS as $permission) { diff --git a/app/system/resources/dashboard/config.php b/app/system/resources/dashboard/config.php index ffdbedcda..d890d6b9a 100644 --- a/app/system/resources/dashboard/config.php +++ b/app/system/resources/dashboard/config.php @@ -117,6 +117,45 @@ $array['dashboard_widgets'][$x]['dashboard_widget_groups'][$y]['dashboard_widget $array['dashboard_widgets'][$x]['dashboard_widget_groups'][$y]['dashboard_widget_uuid'] = 'beade936-846b-4f02-986c-a2de6fa762c2'; $array['dashboard_widgets'][$x]['dashboard_widget_groups'][$y]['group_name'] = 'superadmin'; +$x++; +$array['dashboard_widgets'][$x]['dashboard_uuid'] = '3e2cbaa4-2bec-41b2-a626-999a59b8b19c'; +$array['dashboard_widgets'][$x]['dashboard_widget_uuid'] = '7923d3c3-7aa2-4a8a-b151-d4c5135b0102'; +$array['dashboard_widgets'][$x]['widget_name'] = 'System Network Status'; +$array['dashboard_widgets'][$x]['widget_path'] = 'system/system_network_status'; +$array['dashboard_widgets'][$x]['widget_icon'] = ''; +$array['dashboard_widgets'][$x]['widget_icon_color'] = ''; +$array['dashboard_widgets'][$x]['widget_url'] = ''; +$array['dashboard_widgets'][$x]['widget_target'] = ''; +$array['dashboard_widgets'][$x]['widget_width'] = ''; +$array['dashboard_widgets'][$x]['widget_height'] = ''; +$array['dashboard_widgets'][$x]['widget_content'] = ''; +$array['dashboard_widgets'][$x]['widget_content_text_align'] = ''; +$array['dashboard_widgets'][$x]['widget_content_details'] = ''; +$array['dashboard_widgets'][$x]['widget_chart_type'] = 'line'; +$array['dashboard_widgets'][$x]['widget_chart_type_options'] = ['line']; +$array['dashboard_widgets'][$x]['widget_label_enabled'] = 'true'; +$array['dashboard_widgets'][$x]['widget_label_text_color'] = ''; +$array['dashboard_widgets'][$x]['widget_label_text_color_hover'] = ''; +$array['dashboard_widgets'][$x]['widget_label_background_color'] = ''; +$array['dashboard_widgets'][$x]['widget_label_background_color_hover'] = ''; +$array['dashboard_widgets'][$x]['widget_number_text_color'] = ''; +$array['dashboard_widgets'][$x]['widget_number_text_color_hover'] = ''; +$array['dashboard_widgets'][$x]['widget_number_background_color'] = ''; +$array['dashboard_widgets'][$x]['widget_background_color'] = ''; +$array['dashboard_widgets'][$x]['widget_background_color_hover'] = ''; +$array['dashboard_widgets'][$x]['widget_detail_background_color'] = ''; +$array['dashboard_widgets'][$x]['widget_column_span'] = '1'; +$array['dashboard_widgets'][$x]['widget_row_span'] = '2'; +$array['dashboard_widgets'][$x]['widget_details_state'] = 'disabled'; +$array['dashboard_widgets'][$x]['widget_order'] = '100'; +$array['dashboard_widgets'][$x]['widget_enabled'] = 'false'; +$array['dashboard_widgets'][$x]['widget_description'] = 'System Network information.'; +$y = 0; +$array['dashboard_widgets'][$x]['dashboard_widget_groups'][$y]['dashboard_uuid'] = '3e2cbaa4-2bec-41b2-a626-999a59b8b19c'; +$array['dashboard_widgets'][$x]['dashboard_widget_groups'][$y]['dashboard_widget_group_uuid'] = '258f7e02-b016-417e-ad60-f6cc15ff3a73'; +$array['dashboard_widgets'][$x]['dashboard_widget_groups'][$y]['dashboard_widget_uuid'] = '7923d3c3-7aa2-4a8a-b151-d4c5135b0102'; +$array['dashboard_widgets'][$x]['dashboard_widget_groups'][$y]['group_name'] = 'superadmin'; + $x++; $array['dashboard_widgets'][$x]['dashboard_uuid'] = '3e2cbaa4-2bec-41b2-a626-999a59b8b19c'; $array['dashboard_widgets'][$x]['dashboard_widget_uuid'] = 'bb76f7fc-669f-41dd-a636-6ddaee5deae1'; diff --git a/app/system/resources/dashboard/system_network_status.php b/app/system/resources/dashboard/system_network_status.php new file mode 100644 index 000000000..4fe9172e0 --- /dev/null +++ b/app/system/resources/dashboard/system_network_status.php @@ -0,0 +1,206 @@ +get($_SESSION['domain']['language']['code'], 'app/system'); + + //set the row style class names + $c = 0; + $row_style["0"] = "row_style0"; + $row_style["1"] = "row_style1"; + +// //get the network details +// if (stristr(PHP_OS, 'Linux')) { +// $result = shell_exec("ls /sys/class/net | tr '\n' ' '"); +// $cards = array_map('trim', explode(' ', $result)); +// $selected_card = $settings->get('dashboard', 'network_interface', 'eno1'); +// if (!in_array($selected_card, $cards, true)) { +// // Selected card not in list +// return; +// } +// +// } + + $token = (new token())->create($_SERVER['PHP_SELF']); + + // Register as a subscriber for the dashboard information service + subscriber::save_token($token, [system_dashboard_service::get_service_name()]); + + //system network status + echo "
\n"; + echo "\n"; + echo "
\n"; + echo "\n"; + echo "".$text['label-network_usage']."\n"; + echo "\n"; + //if ($dashboard_chart_type === 'line') { ?> +
+ +
+ + \n"; + echo "
\n"; diff --git a/core/dashboard/resources/javascript/ws_client.js b/core/dashboard/resources/javascript/ws_client.js index 16a5e4c74..8954282c0 100644 --- a/core/dashboard/resources/javascript/ws_client.js +++ b/core/dashboard/resources/javascript/ws_client.js @@ -14,7 +14,7 @@ class ws_client { let message; let switch_event; try { - console.log(ev.data); + //console.log(ev.data); message = JSON.parse(ev.data); // check for authentication request if (message.status_code === 407) {