Functional read-only app
parent
ed690495d2
commit
1a019a2343
@ -1,2 +1,2 @@
|
|||||||
tool_directory: ./firmware/www/tool/
|
tool_directory: ./firmware/www/tool/
|
||||||
app_config_directory: .
|
app_config_directory: netoolclient/src/netoolclient/resources
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# OSX useful to ignore
|
||||||
|
*.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
env/
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
*.dist-info/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
|
||||||
|
# IntelliJ Idea family of suites
|
||||||
|
.idea
|
||||||
|
*.iml
|
||||||
|
## File-based project format:
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
## mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# Briefcase log files
|
||||||
|
logs/
|
@ -0,0 +1,5 @@
|
|||||||
|
# Netool Client Release Notes
|
||||||
|
|
||||||
|
## 0.0.1 (31 May 2024)
|
||||||
|
|
||||||
|
* Initial release
|
@ -0,0 +1,14 @@
|
|||||||
|
Netool Client: A third-party cross-platform Netool companion app.
|
||||||
|
Copyright (C) 2024 Amelia Deck
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of version 3 of the GNU General Public License as
|
||||||
|
published by the Free Software Foundation.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
@ -0,0 +1,8 @@
|
|||||||
|
Netool Client
|
||||||
|
=============
|
||||||
|
|
||||||
|
A third-party cross-platform Netool companion app.
|
||||||
|
|
||||||
|
.. _`Briefcase`: https://briefcase.readthedocs.io/
|
||||||
|
.. _`The BeeWare Project`: https://beeware.org/
|
||||||
|
.. _`becoming a financial member of BeeWare`: https://beeware.org/contributing/membership
|
@ -0,0 +1,184 @@
|
|||||||
|
# This project was generated with 0.3.18 using template: https://github.com/beeware/briefcase-template@v0.3.18
|
||||||
|
[tool.briefcase]
|
||||||
|
project_name = "Netool Client"
|
||||||
|
bundle = "sh.deck"
|
||||||
|
version = "0.0.1"
|
||||||
|
url = "https://git.deck.sh/shark/netool-newapp"
|
||||||
|
license = "GNU General Public License v3 (GPLv3)"
|
||||||
|
author = "Amelia Deck"
|
||||||
|
author_email = "amelia@deck.sh"
|
||||||
|
|
||||||
|
[tool.briefcase.app.netoolclient]
|
||||||
|
formal_name = "Netool Client"
|
||||||
|
description = "A third-party cross-platform Netool companion app."
|
||||||
|
long_description = """More details about the app should go here.
|
||||||
|
"""
|
||||||
|
sources = [
|
||||||
|
"src/netoolclient",
|
||||||
|
]
|
||||||
|
test_sources = [
|
||||||
|
"tests",
|
||||||
|
]
|
||||||
|
|
||||||
|
requires = [
|
||||||
|
"httpx",
|
||||||
|
]
|
||||||
|
test_requires = [
|
||||||
|
"pytest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.briefcase.app.netoolclient.macOS]
|
||||||
|
universal_build = true
|
||||||
|
requires = [
|
||||||
|
"toga-cocoa~=0.4.0",
|
||||||
|
"std-nslog~=1.0.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.briefcase.app.netoolclient.linux]
|
||||||
|
requires = [
|
||||||
|
"toga-gtk~=0.4.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.briefcase.app.netoolclient.linux.system.debian]
|
||||||
|
system_requires = [
|
||||||
|
# Needed to compile pycairo wheel
|
||||||
|
"libcairo2-dev",
|
||||||
|
# Needed to compile PyGObject wheel
|
||||||
|
"libgirepository1.0-dev",
|
||||||
|
]
|
||||||
|
|
||||||
|
system_runtime_requires = [
|
||||||
|
# Needed to provide GTK and its GI bindings
|
||||||
|
"gir1.2-gtk-3.0",
|
||||||
|
"libgirepository-1.0-1",
|
||||||
|
# Dependencies that GTK looks for at runtime
|
||||||
|
"libcanberra-gtk3-module",
|
||||||
|
# Needed to provide WebKit2 at runtime
|
||||||
|
# Note: Debian 11 and Ubuntu 20.04 require gir1.2-webkit2-4.0 instead
|
||||||
|
# "gir1.2-webkit2-4.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.briefcase.app.netoolclient.linux.system.rhel]
|
||||||
|
system_requires = [
|
||||||
|
# Needed to compile pycairo wheel
|
||||||
|
"cairo-gobject-devel",
|
||||||
|
# Needed to compile PyGObject wheel
|
||||||
|
"gobject-introspection-devel",
|
||||||
|
]
|
||||||
|
|
||||||
|
system_runtime_requires = [
|
||||||
|
# Needed to support Python bindings to GTK
|
||||||
|
"gobject-introspection",
|
||||||
|
# Needed to provide GTK
|
||||||
|
"gtk3",
|
||||||
|
# Dependencies that GTK looks for at runtime
|
||||||
|
"libcanberra-gtk3",
|
||||||
|
# Needed to provide WebKit2 at runtime
|
||||||
|
# "webkit2gtk3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.briefcase.app.netoolclient.linux.system.suse]
|
||||||
|
system_requires = [
|
||||||
|
# Needed to compile pycairo wheel
|
||||||
|
"cairo-devel",
|
||||||
|
# Needed to compile PyGObject wheel
|
||||||
|
"gobject-introspection-devel",
|
||||||
|
]
|
||||||
|
|
||||||
|
system_runtime_requires = [
|
||||||
|
# Needed to provide GTK
|
||||||
|
"gtk3",
|
||||||
|
# Needed to support Python bindings to GTK
|
||||||
|
"gobject-introspection", "typelib(Gtk) = 3.0",
|
||||||
|
# Dependencies that GTK looks for at runtime
|
||||||
|
"libcanberra-gtk3-module",
|
||||||
|
# Needed to provide WebKit2 at runtime
|
||||||
|
# "libwebkit2gtk3", "typelib(WebKit2)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.briefcase.app.netoolclient.linux.system.arch]
|
||||||
|
system_requires = [
|
||||||
|
# Needed to compile pycairo wheel
|
||||||
|
"cairo",
|
||||||
|
# Needed to compile PyGObject wheel
|
||||||
|
"gobject-introspection",
|
||||||
|
# Runtime dependencies that need to exist so that the
|
||||||
|
# Arch package passes final validation.
|
||||||
|
# Needed to provide GTK
|
||||||
|
"gtk3",
|
||||||
|
# Dependencies that GTK looks for at runtime
|
||||||
|
"libcanberra",
|
||||||
|
# Needed to provide WebKit2
|
||||||
|
# "webkit2gtk",
|
||||||
|
]
|
||||||
|
|
||||||
|
system_runtime_requires = [
|
||||||
|
# Needed to provide GTK
|
||||||
|
"gtk3",
|
||||||
|
# Needed to provide PyGObject bindings
|
||||||
|
"gobject-introspection-runtime",
|
||||||
|
# Dependencies that GTK looks for at runtime
|
||||||
|
"libcanberra",
|
||||||
|
# Needed to provide WebKit2 at runtime
|
||||||
|
# "webkit2gtk",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.briefcase.app.netoolclient.linux.appimage]
|
||||||
|
manylinux = "manylinux_2_28"
|
||||||
|
|
||||||
|
system_requires = [
|
||||||
|
# Needed to compile pycairo wheel
|
||||||
|
"cairo-gobject-devel",
|
||||||
|
# Needed to compile PyGObject wheel
|
||||||
|
"gobject-introspection-devel",
|
||||||
|
# Needed to provide GTK
|
||||||
|
"gtk3-devel",
|
||||||
|
# Dependencies that GTK looks for at runtime, that need to be
|
||||||
|
# in the build environment to be picked up by linuxdeploy
|
||||||
|
"libcanberra-gtk3",
|
||||||
|
"PackageKit-gtk3-module",
|
||||||
|
"gvfs-client",
|
||||||
|
]
|
||||||
|
|
||||||
|
linuxdeploy_plugins = [
|
||||||
|
"DEPLOY_GTK_VERSION=3 gtk",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.briefcase.app.netoolclient.linux.flatpak]
|
||||||
|
flatpak_runtime = "org.gnome.Platform"
|
||||||
|
flatpak_runtime_version = "45"
|
||||||
|
flatpak_sdk = "org.gnome.Sdk"
|
||||||
|
|
||||||
|
[tool.briefcase.app.netoolclient.windows]
|
||||||
|
requires = [
|
||||||
|
"toga-winforms~=0.4.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Mobile deployments
|
||||||
|
[tool.briefcase.app.netoolclient.iOS]
|
||||||
|
requires = [
|
||||||
|
"toga-iOS~=0.4.0",
|
||||||
|
"std-nslog~=1.0.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[tool.briefcase.app.netoolclient.android]
|
||||||
|
requires = [
|
||||||
|
"toga-android~=0.4.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
base_theme = "Theme.MaterialComponents.Light.DarkActionBar"
|
||||||
|
|
||||||
|
build_gradle_dependencies = [
|
||||||
|
"androidx.appcompat:appcompat:1.6.1",
|
||||||
|
"com.google.android.material:material:1.11.0",
|
||||||
|
# Needed for DetailedList
|
||||||
|
"androidx.swiperefreshlayout:swiperefreshlayout:1.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
# Web deployments
|
||||||
|
[tool.briefcase.app.netoolclient.web]
|
||||||
|
requires = [
|
||||||
|
"toga-web~=0.4.0",
|
||||||
|
]
|
||||||
|
style_framework = "Shoelace v2.3"
|
||||||
|
|
@ -0,0 +1,4 @@
|
|||||||
|
from netoolclient.app import main
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main().main_loop()
|
@ -0,0 +1,272 @@
|
|||||||
|
"""
|
||||||
|
A third-party cross-platform Netool companion app.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import toga
|
||||||
|
from toga.style import Pack
|
||||||
|
from toga.style.pack import COLUMN, LEFT, RIGHT, TOP, BOTTOM, CENTER, ROW, Pack
|
||||||
|
from netoolclient.call_api import call_api_preloaded
|
||||||
|
import asyncio
|
||||||
|
import threading
|
||||||
|
import json
|
||||||
|
import socket
|
||||||
|
|
||||||
|
def run_asyncio_event_loop():
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
loop.run_forever()
|
||||||
|
|
||||||
|
class NetoolClient(toga.App):
|
||||||
|
ip = "192.168.49.1"
|
||||||
|
conntype = "eth" # or ble
|
||||||
|
connected = False
|
||||||
|
page = "Status"
|
||||||
|
lastpage = "None"
|
||||||
|
page_mode = "DetailedList" # Text, Switches, etc
|
||||||
|
refresh = True
|
||||||
|
def startup(self):
|
||||||
|
"""Construct and show the Toga application.
|
||||||
|
|
||||||
|
Usually, you would add your application to a main content box.
|
||||||
|
We then create a main window (with a name matching the app), and
|
||||||
|
show the main window.
|
||||||
|
"""
|
||||||
|
self.main_box = toga.Box(style=Pack(direction=COLUMN))
|
||||||
|
|
||||||
|
|
||||||
|
self.status_header = toga.Label(
|
||||||
|
"Connecting to Netool.IO device via WiFi...",
|
||||||
|
style=Pack(padding=(5, 5)),
|
||||||
|
)
|
||||||
|
self.page_box = toga.Box(style=Pack(direction=ROW, padding=(5,5)))
|
||||||
|
# self.page_header = toga.Label(
|
||||||
|
# "",
|
||||||
|
# style=Pack(padding=(5, 5)),
|
||||||
|
# )
|
||||||
|
self.page_selector = toga.Selection(items=["Status", "Discovery Packet (LLDP, CDP...) Details", "STP Details", "ARP Scan", "Traceroute Log", "NTP Status", "PCAP Status", "History", "WiFi Settings", "Ethernet Settings", "Discovery Timers", "Scan Toggle Switches", "About", ], style=Pack(flex=1), on_change=self.set_refresh)
|
||||||
|
self.page_box.add(self.page_selector)
|
||||||
|
|
||||||
|
self.page_loading = toga.ProgressBar(max=None, style=Pack(alignment=RIGHT, flex=1))
|
||||||
|
|
||||||
|
self.info_box = toga.Box(style=Pack(direction=COLUMN, flex=1))
|
||||||
|
# self.info_text = toga.Label(
|
||||||
|
# "",
|
||||||
|
# style=Pack(padding=(5, 5), flex=1),
|
||||||
|
# )
|
||||||
|
self.info_text = toga.MultilineTextInput(readonly=True, value="", style=Pack(padding=(5, 5), flex=1))
|
||||||
|
self.info_list = toga.DetailedList(data=[], style=Pack(padding=(5, 5), flex=1))
|
||||||
|
self.info_box.add(self.info_list)
|
||||||
|
# container = toga.ScrollContainer(content=self.info_box, style=Pack(flex=1), horizontal=False)
|
||||||
|
|
||||||
|
self.main_box.add(self.status_header)
|
||||||
|
# self.page_box.add(self.page_header)
|
||||||
|
self.main_box.add(self.page_loading)
|
||||||
|
self.main_box.add(self.page_box)
|
||||||
|
|
||||||
|
self.main_box.add(self.info_box)
|
||||||
|
|
||||||
|
if toga.platform.current_platform == "android":
|
||||||
|
self.page_refresh = toga.Button(text="Refresh", style=Pack(flex=5, alignment=CENTER), on_press=self.set_refresh)
|
||||||
|
self.auto_refresh = toga.Switch(text=None, style=Pack(padding_top=11, flex=1, alignment=CENTER))
|
||||||
|
else:
|
||||||
|
self.page_refresh = toga.Button(text="Refresh", style=Pack(flex=7, alignment=CENTER), on_press=self.set_refresh)
|
||||||
|
self.auto_refresh = toga.Switch(text="Auto", style=Pack(padding_top=4, padding_left=8, flex=1, alignment=CENTER))
|
||||||
|
self.refresh_box = toga.Box(style=Pack(direction=ROW, padding=(5,5)))
|
||||||
|
self.refresh_box.add(self.page_refresh)
|
||||||
|
self.refresh_box.add(self.auto_refresh)
|
||||||
|
self.main_box.add(self.refresh_box)
|
||||||
|
|
||||||
|
self.main_window = toga.MainWindow(title=self.formal_name)
|
||||||
|
self.main_window.content = self.main_box
|
||||||
|
self.main_window.show()
|
||||||
|
|
||||||
|
#toga.App.add_background_task(self, handler=self.check_online)
|
||||||
|
|
||||||
|
toga.App.add_background_task(self, handler=self.update_page)
|
||||||
|
|
||||||
|
def set_refresh(self, a):
|
||||||
|
self.refresh = True
|
||||||
|
async def check_online(self):
|
||||||
|
# Your periodic task logic here
|
||||||
|
if True or self.conntype == "eth":
|
||||||
|
result = await self.check_online_wifi()
|
||||||
|
if result:
|
||||||
|
text = "Connected to Netool.IO device."
|
||||||
|
self.connected = True
|
||||||
|
else:
|
||||||
|
text = "Connecting to Netool.IO device via WiFi..."
|
||||||
|
self.connected = False
|
||||||
|
#print(text)
|
||||||
|
# Schedule the GUI update on the main thread
|
||||||
|
if self.status_header.text != text:
|
||||||
|
print(text)
|
||||||
|
self.status_header.text = text
|
||||||
|
# Wait for 5 seconds before running again
|
||||||
|
return result
|
||||||
|
|
||||||
|
async def update_page(self, a):
|
||||||
|
counter = 0
|
||||||
|
while True:
|
||||||
|
self.page = self.page_selector.value
|
||||||
|
if self.connected:
|
||||||
|
if (self.refresh) and await self.check_online():
|
||||||
|
self.refresh = False
|
||||||
|
print("Switching to page " + self.page)
|
||||||
|
self.lastpage = self.page
|
||||||
|
#self.page_header.text = self.page
|
||||||
|
self.page_loading.start()
|
||||||
|
data = await self.get_info()
|
||||||
|
if self.page_mode == "Text":
|
||||||
|
self.info_text.value = str(data)
|
||||||
|
if self.page_mode == "DetailedList":
|
||||||
|
self.info_list.data = data
|
||||||
|
self.page_loading.stop()
|
||||||
|
else:
|
||||||
|
await self.check_online()
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
if self.auto_refresh.value == True:
|
||||||
|
counter += 1
|
||||||
|
if counter >= 4:
|
||||||
|
counter = 0
|
||||||
|
self.refresh=True
|
||||||
|
else:
|
||||||
|
counter = 0
|
||||||
|
|
||||||
|
|
||||||
|
# render new page
|
||||||
|
else:
|
||||||
|
result = await self.check_online()
|
||||||
|
if not result:
|
||||||
|
await asyncio.sleep(0.25)
|
||||||
|
|
||||||
|
def set_info_mode(self, mode):
|
||||||
|
if mode != self.page_mode:
|
||||||
|
match self.page_mode:
|
||||||
|
case "DetailedList":
|
||||||
|
self.info_box.remove(self.info_list)
|
||||||
|
case "Text":
|
||||||
|
self.info_box.remove(self.info_text)
|
||||||
|
self.page_mode = mode
|
||||||
|
match mode:
|
||||||
|
case "DetailedList":
|
||||||
|
self.info_box.add(self.info_list)
|
||||||
|
case "Text":
|
||||||
|
self.info_box.add(self.info_text)
|
||||||
|
|
||||||
|
async def get_info(self):
|
||||||
|
match self.page:
|
||||||
|
case "Status":
|
||||||
|
call = "operations"
|
||||||
|
case "About":
|
||||||
|
call = "about"
|
||||||
|
case "History":
|
||||||
|
call = "history"
|
||||||
|
case "ARP Scan":
|
||||||
|
call = "arpscan"
|
||||||
|
case "Discovery Packet (LLDP, CDP...) Details":
|
||||||
|
call = "dp_detail"
|
||||||
|
case "STP Details":
|
||||||
|
call = "stp_detail"
|
||||||
|
case "NTP Status":
|
||||||
|
call = "ntp_status"
|
||||||
|
case "PCAP Status":
|
||||||
|
call = "sniff"
|
||||||
|
case "Discovery Timers":
|
||||||
|
call = "scan_time_status"
|
||||||
|
case "WiFi Settings":
|
||||||
|
call = "wifi_status"
|
||||||
|
case "Ethernet Settings":
|
||||||
|
call = "eth_status"
|
||||||
|
case "Scan Toggle Switches":
|
||||||
|
call = "toggle_settings_status"
|
||||||
|
case "Traceroute Log":
|
||||||
|
call = "traceroute_log"
|
||||||
|
|
||||||
|
|
||||||
|
case _:
|
||||||
|
call = None
|
||||||
|
|
||||||
|
if call is not None:
|
||||||
|
res = await self.call_api_safe(call)
|
||||||
|
while res is None:
|
||||||
|
res = await self.call_api_safe(call)
|
||||||
|
#return res
|
||||||
|
|
||||||
|
match self.page:
|
||||||
|
case "Status":
|
||||||
|
self.set_info_mode("DetailedList")
|
||||||
|
return [{"title": key, "subtitle": str(val).replace("\n", " ")} for key, val in res.items()]
|
||||||
|
|
||||||
|
case "About":
|
||||||
|
self.set_info_mode("DetailedList")
|
||||||
|
return [{"title": key, "subtitle": str(val).replace("\n", " ")} for key, val in res.items()]
|
||||||
|
case "History":
|
||||||
|
self.set_info_mode("DetailedList")
|
||||||
|
out = []
|
||||||
|
for index, entry in enumerate(res):
|
||||||
|
for key, val in entry.items():
|
||||||
|
out.append({"title": str(index)+" "+key, "subtitle": str(val).replace("\n", " ")})
|
||||||
|
print(out)
|
||||||
|
return out
|
||||||
|
case "ARP Scan":
|
||||||
|
self.set_info_mode("DetailedList")
|
||||||
|
return [{"title": dev["IP"], "subtitle": dev["MAC"]} for dev in res]
|
||||||
|
case "Discovery Packet (LLDP, CDP...) Details":
|
||||||
|
self.set_info_mode("Text")
|
||||||
|
return res.replace("<br>", "\n")
|
||||||
|
case "STP Details":
|
||||||
|
self.set_info_mode("Text")
|
||||||
|
return res.replace("<br>", "\n")
|
||||||
|
case "NTP Status":
|
||||||
|
self.set_info_mode("DetailedList")
|
||||||
|
return [{"title": key, "subtitle": str(val).replace("_", " ")} for key, val in res.items()]
|
||||||
|
case "PCAP Status":
|
||||||
|
self.set_info_mode("DetailedList")
|
||||||
|
return [{"title": key, "subtitle": str(val).replace("\n", " ")} for key, val in res.items()]
|
||||||
|
case "Discovery Timers":
|
||||||
|
self.set_info_mode("DetailedList")
|
||||||
|
return [{"title": key, "subtitle": str(val).replace("\n", " ") + " seconds"} for key, val in res.items()]
|
||||||
|
case "WiFi Settings":
|
||||||
|
self.set_info_mode("DetailedList")
|
||||||
|
return [{"title": key, "subtitle": str(val).replace("\n", " ")} for key, val in res.items()]
|
||||||
|
case "Ethernet Settings":
|
||||||
|
self.set_info_mode("DetailedList")
|
||||||
|
return [{"title": key, "subtitle": str(val).replace("\n", " ")} for key, val in res.items()]
|
||||||
|
case "Scan Toggle Switches":
|
||||||
|
self.set_info_mode("DetailedList")
|
||||||
|
return [{"title": key, "subtitle": str(val).replace("\n", " ")} for key, val in res.items()]
|
||||||
|
case "Traceroute Log":
|
||||||
|
self.set_info_mode("Text")
|
||||||
|
return res.replace("<br><br>", "\n")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async def call_api_safe(self, callname, method="auto", params={}, baseaddr="https://192.168.49.1/tool/"):
|
||||||
|
try:
|
||||||
|
response = await call_api_preloaded(callname, method, params, baseaddr, conntype=self.conntype, filepath=str(self.paths.app) + "/resources")
|
||||||
|
#print("Response Status Code:", response.status_code)
|
||||||
|
try:
|
||||||
|
#print("Response JSON:", json.dumps(response.json(), indent=2))
|
||||||
|
return response.json()
|
||||||
|
except ValueError:
|
||||||
|
#print("Response Text:", response.text)
|
||||||
|
return response.text
|
||||||
|
except Exception as e:
|
||||||
|
print("Error:", str(e))
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
async def check_online_wifi(self):
|
||||||
|
# host = await async_ping(self.ip, count=1, timeout=0.25)
|
||||||
|
# return host.is_alive
|
||||||
|
|
||||||
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
result = sock.connect_ex((self.ip,443))
|
||||||
|
sock.close()
|
||||||
|
return result == 0
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
return NetoolClient()
|
@ -0,0 +1,2 @@
|
|||||||
|
Put any application resources (e.g., icons and resources) here;
|
||||||
|
they can be referenced in code as "resources/filename".
|
@ -0,0 +1,35 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
def run_tests():
|
||||||
|
project_path = Path(__file__).parent.parent
|
||||||
|
os.chdir(project_path)
|
||||||
|
|
||||||
|
# Determine any args to pass to pytest. If there aren't any,
|
||||||
|
# default to running the whole test suite.
|
||||||
|
args = sys.argv[1:]
|
||||||
|
if len(args) == 0:
|
||||||
|
args = ["tests"]
|
||||||
|
|
||||||
|
returncode = pytest.main(
|
||||||
|
[
|
||||||
|
# Turn up verbosity
|
||||||
|
"-vv",
|
||||||
|
# Disable color
|
||||||
|
"--color=no",
|
||||||
|
# Overwrite the cache directory to somewhere writable
|
||||||
|
"-o",
|
||||||
|
f"cache_dir={tempfile.gettempdir()}/.pytest_cache",
|
||||||
|
] + args
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f">>>>>>>>>> EXIT {returncode} <<<<<<<<<<")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
run_tests()
|
@ -0,0 +1,3 @@
|
|||||||
|
def test_first():
|
||||||
|
"""An initial test for the app."""
|
||||||
|
assert 1 + 1 == 2
|
@ -0,0 +1,5 @@
|
|||||||
|
# requirements for GUI app
|
||||||
|
# not needed for core API
|
||||||
|
briefcase
|
||||||
|
httpx
|
||||||
|
icmplib
|
Loading…
Reference in New Issue