From d09c14518e3fd1cb2f54af7db389ee45463e8c23 Mon Sep 17 00:00:00 2001 From: DYefremov Date: Sat, 7 May 2022 23:13:17 +0300 Subject: [PATCH] added simple network explorer (#60) --- app/connections.py | 4 +-- app/ui/control.glade | 30 +++++++++++++---- app/ui/control.py | 79 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 100 insertions(+), 13 deletions(-) diff --git a/app/connections.py b/app/connections.py index 9562b020..14412c2c 100644 --- a/app/connections.py +++ b/app/connections.py @@ -733,9 +733,9 @@ class HttpAPI: self._executor.shutdown() @staticmethod - def get_response(req_type, url, data=None, s_type=SettingsType.ENIGMA_2): + def get_response(req_type, url, data=None, s_type=SettingsType.ENIGMA_2, timeout=10): try: - with urlopen(Request(url, data=data), timeout=10) as f: + with urlopen(Request(url, data=data), timeout=timeout) as f: if s_type is SettingsType.ENIGMA_2: return HttpAPI.get_e2_response_data(req_type, f) elif s_type is SettingsType.NEUTRINO_MP: diff --git a/app/ui/control.glade b/app/ui/control.glade index c86cfa07..751cf4df 100644 --- a/app/ui/control.glade +++ b/app/ui/control.glade @@ -3,7 +3,7 @@ The MIT License (MIT) -Copyright (c) 2018-2021 Dmitriy Yefremov +Copyright (c) 2018-2022 Dmitriy Yefremov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -32,7 +32,7 @@ Author: Dmitriy Yefremov - + 23 @@ -1141,6 +1141,8 @@ Author: Dmitriy Yefremov + + @@ -2457,7 +2459,8 @@ Author: Dmitriy Yefremov gtk-network 100 - True + True + False True center True @@ -2619,7 +2622,7 @@ Author: Dmitriy Yefremov 10 - 240 + 280 False 0.5 in @@ -2645,11 +2648,14 @@ Author: Dmitriy Yefremov network_model False 3 + + True + 25 Name True 0.5 @@ -2663,11 +2669,16 @@ Author: Dmitriy Yefremov + True + 25 IP True 0.5 - + + 2 + 0.49 + 1 @@ -2676,11 +2687,16 @@ Author: Dmitriy Yefremov - Status + True + 25 + Power True 0.5 - + + 0.49 + end + 2 diff --git a/app/ui/control.py b/app/ui/control.py index 81b51251..f7a0263c 100644 --- a/app/ui/control.py +++ b/app/ui/control.py @@ -28,6 +28,7 @@ """ Receiver control module via HTTP API. """ import os +import re from datetime import datetime from enum import Enum from ftplib import all_errors @@ -842,7 +843,8 @@ class ControlTool(Gtk.Box): handlers = {"on_volume_changed": self.on_volume_changed, "on_screenshot_draw": self.on_screenshot_draw, - "on_network_toggled": self.on_network_toggled} + "on_network_toggled": self.on_network_toggled, + "on_network_view_query_tooltip": self.on_network_view_query_tooltip} builder = get_builder(UI_RESOURCES_PATH + "control.glade", handlers, objects=("control_box", "volume_adjustment", "network_model")) @@ -860,8 +862,11 @@ class ControlTool(Gtk.Box): self._ber_level_bar = builder.get_object("ber_level_bar") self._agc_level_bar = builder.get_object("agc_level_bar") self._volume_button = builder.get_object("volume_button") - self._network_button = builder.get_object("control_network_button") self._header_box = builder.get_object("control_header_box") + # Network. + self._network_button = builder.get_object("control_network_button") + self._network_model = builder.get_object("network_model") + self.init_actions(app) if settings.alternate_layout: @@ -1038,6 +1043,72 @@ class ControlTool(Gtk.Box): # ***************** Network explorer ********************** # - @run_task def on_network_toggled(self, button): - pass + self._network_model.clear() + if button.get_active(): + self.update_network() + + @run_task + def update_network(self): + pattern = re.compile(r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})') + + ips = [match for match in re.findall(pattern, os.popen("arp -a").read())] + for ip in ips: + if not self._network_button.get_active(): + break + + url = f"http://{ip}/web/{HttpAPI.Request.INFO.value}" + try: + resp = HttpAPI.get_response(HttpAPI.Request.INFO, url, timeout=5) + except OSError as e: + log(f"{ip} {e}") + else: + if resp.get("e2distroversion", None): + log(f"Receiver found. Model: {resp.get('e2model', 'N/A')} [{ip} ]") + self.append_box_data(resp) + + @run_idle + def append_box_data(self, data): + ip = data.get('e2lanip', 'N/A') + itr = self._network_model.append((data.get("e2model", "N/A"), ip, None, data, None)) + GLib.timeout_add_seconds(3, self.check_power_state, itr, priority=GLib.PRIORITY_LOW) + + def on_network_view_query_tooltip(self, view, x, y, keyboard_mode, tooltip): + result = view.get_dest_row_at_pos(x, y) + if not result: + return False + + path, pos = result + model = view.get_model() + data = model[path][3] + + dist = data.get("e2distroversion", "N/A") + img = data.get("e2imageversion", "N/A") + txt = f"Distro version: {dist}\nImage version: {img}" + tooltip.set_text(txt) + view.set_tooltip_row(tooltip, path) + return True + + def check_power_state(self, itr): + active = self._network_button.get_active() + if not active: + return False + + data = self._network_model.get_value(itr, 3) + url = f"http://{data.get('e2lanip', 'N/A')}/web/powerstate" + self.update_power_state(itr, url) + return active + + @run_task + def update_power_state(self, itr, url): + try: + resp = HttpAPI.get_response(HttpAPI.Request.POWER, url, timeout=2) + except OSError as e: + log(e) + else: + state = get_message("On" if resp.get("e2instandby", "N/A").strip() == "false" else "Standby") + GLib.idle_add(self._network_model.set_value, itr, 2, state) + + +if __name__ == "__main__": + pass