mirror of
https://github.com/pdxlocations/contact.git
synced 2026-03-28 17:12:35 +01:00
Compare commits
2 Commits
fix-shutdo
...
reset
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b53dab1840 | ||
|
|
f2904eb550 |
@@ -12,6 +12,7 @@ Reboot, "Reboot", ""
|
||||
Reset Node DB, "Reset Node DB", ""
|
||||
Shutdown, "Shutdown", ""
|
||||
Factory Reset, "Factory Reset", ""
|
||||
factory_reset_config, "Factory Reset Config", ""
|
||||
Exit, "Exit", ""
|
||||
Yes, "Yes", ""
|
||||
No, "No", ""
|
||||
@@ -55,6 +56,7 @@ confirm.reboot, "Are you sure you want to Reboot?", ""
|
||||
confirm.reset_node_db, "Are you sure you want to Reset Node DB?", ""
|
||||
confirm.shutdown, "Are you sure you want to Shutdown?", ""
|
||||
confirm.factory_reset, "Are you sure you want to Factory Reset?", ""
|
||||
confirm.factory_reset_config, "Are you sure you want to Factory Reset Config?", ""
|
||||
confirm.save_before_exit_section, "You have unsaved changes in {section}. Save before exiting?", ""
|
||||
prompt.select_region, "Select your region:", ""
|
||||
dialog.slow_down_title, "Slow down", ""
|
||||
|
||||
@@ -12,6 +12,7 @@ Reboot, "Перезагрузить", ""
|
||||
Reset Node DB, "Сбросить БД узлов", ""
|
||||
Shutdown, "Выключить", ""
|
||||
Factory Reset, "Сброс до заводских", ""
|
||||
factory_reset_config, "Сбросить только конфигурацию", ""
|
||||
Exit, "Выход", ""
|
||||
Yes, "Да", ""
|
||||
No, "Нет", ""
|
||||
@@ -55,6 +56,7 @@ confirm.reboot, "Перезагрузить устройство?", ""
|
||||
confirm.reset_node_db, "Сбросить БД узлов?", ""
|
||||
confirm.shutdown, "Выключить устройство?", ""
|
||||
confirm.factory_reset, "Сбросить до заводских настроек?", ""
|
||||
confirm.factory_reset_config, "Сбросить только конфигурацию?", ""
|
||||
confirm.save_before_exit_section, "Есть несохраненные изменения в {section}. Сохранить перед выходом?", ""
|
||||
prompt.select_region, "Выберите ваш регион:", ""
|
||||
dialog.slow_down_title, "Подождите", ""
|
||||
|
||||
@@ -5,6 +5,7 @@ import logging
|
||||
import os
|
||||
import sys
|
||||
from typing import List
|
||||
from meshtastic.protobuf import admin_pb2
|
||||
|
||||
from contact.utilities.save_to_radio import save_changes
|
||||
import contact.ui.default_config as config
|
||||
@@ -34,7 +35,7 @@ MAX_MENU_WIDTH = 80 # desired max; will shrink on small terminals
|
||||
save_option = "Save Changes"
|
||||
max_help_lines = 0
|
||||
help_win = None
|
||||
sensitive_settings = ["Reboot", "Reset Node DB", "Shutdown", "Factory Reset"]
|
||||
sensitive_settings = ["Reboot", "Reset Node DB", "Shutdown", "Factory Reset", "factory_reset_config"]
|
||||
|
||||
|
||||
# Compute the effective menu width for the current terminal
|
||||
@@ -43,7 +44,7 @@ def get_menu_width() -> int:
|
||||
return max(20, min(MAX_MENU_WIDTH, curses.COLS - 2))
|
||||
|
||||
|
||||
sensitive_settings = ["Reboot", "Reset Node DB", "Shutdown", "Factory Reset"]
|
||||
sensitive_settings = ["Reboot", "Reset Node DB", "Shutdown", "Factory Reset", "factory_reset_config"]
|
||||
|
||||
# Get the parent directory of the script
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
@@ -246,6 +247,26 @@ def reconnect_after_admin_action(stdscr: object, interface: object, action, log_
|
||||
return reconnect_interface_with_splash(stdscr, interface)
|
||||
|
||||
|
||||
def request_factory_reset(node: object, full: bool = False):
|
||||
try:
|
||||
return node.factoryReset(full=full)
|
||||
except TypeError as ex:
|
||||
field_name = "factory_reset_device" if full else "factory_reset_config"
|
||||
field = admin_pb2.AdminMessage.DESCRIPTOR.fields_by_name[field_name]
|
||||
if field.cpp_type != field.CPPTYPE_INT32:
|
||||
raise
|
||||
|
||||
node.ensureSessionKey()
|
||||
message = admin_pb2.AdminMessage()
|
||||
setattr(message, field_name, 1)
|
||||
|
||||
if node == node.iface.localNode:
|
||||
on_response = None
|
||||
else:
|
||||
on_response = node.onAckNak
|
||||
return node._sendAdmin(message, onResponse=on_response)
|
||||
|
||||
|
||||
def redraw_main_ui_after_reconnect(stdscr: object) -> None:
|
||||
try:
|
||||
from contact.ui import contact_ui
|
||||
@@ -538,7 +559,27 @@ def settings_menu(stdscr: object, interface: object) -> None:
|
||||
)
|
||||
if confirmation == "Yes":
|
||||
interface = reconnect_after_admin_action(
|
||||
stdscr, interface, interface.localNode.factoryReset, "Factory Reset Requested by menu"
|
||||
stdscr,
|
||||
interface,
|
||||
lambda: request_factory_reset(interface.localNode, full=True),
|
||||
"Factory Reset Requested by menu",
|
||||
)
|
||||
menu = rebuild_menu_at_current_path(interface, menu_state)
|
||||
menu_state.start_index.pop()
|
||||
continue
|
||||
|
||||
elif selected_option == "factory_reset_config":
|
||||
confirmation = get_list_input(
|
||||
t("ui.confirm.factory_reset_config", default="Are you sure you want to Factory Reset Config?"),
|
||||
None,
|
||||
["Yes", "No"],
|
||||
)
|
||||
if confirmation == "Yes":
|
||||
interface = reconnect_after_admin_action(
|
||||
stdscr,
|
||||
interface,
|
||||
lambda: request_factory_reset(interface.localNode, full=False),
|
||||
"Factory Reset Config Requested by menu",
|
||||
)
|
||||
menu = rebuild_menu_at_current_path(interface, menu_state)
|
||||
menu_state.start_index.pop()
|
||||
|
||||
@@ -133,6 +133,7 @@ def generate_menu_from_protobuf(interface: object) -> Dict[str, Any]:
|
||||
"Reset Node DB": None,
|
||||
"Shutdown": None,
|
||||
"Factory Reset": None,
|
||||
"factory_reset_config": None,
|
||||
"Exit": None,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from argparse import Namespace
|
||||
from types import SimpleNamespace
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
@@ -63,3 +64,49 @@ class ControlUiTests(unittest.TestCase):
|
||||
get_channels.assert_called_once_with()
|
||||
refresh_node_list.assert_called_once_with()
|
||||
handle_resize.assert_called_once_with(stdscr, False)
|
||||
|
||||
def test_request_factory_reset_uses_library_helper_when_supported(self) -> None:
|
||||
node = mock.Mock()
|
||||
|
||||
control_ui.request_factory_reset(node)
|
||||
|
||||
node.factoryReset.assert_called_once_with(full=False)
|
||||
node.ensureSessionKey.assert_not_called()
|
||||
node._sendAdmin.assert_not_called()
|
||||
|
||||
def test_request_factory_reset_uses_library_helper_for_full_reset_when_supported(self) -> None:
|
||||
node = mock.Mock()
|
||||
|
||||
control_ui.request_factory_reset(node, full=True)
|
||||
|
||||
node.factoryReset.assert_called_once_with(full=True)
|
||||
node.ensureSessionKey.assert_not_called()
|
||||
node._sendAdmin.assert_not_called()
|
||||
|
||||
def test_request_factory_reset_falls_back_to_int_valued_admin_message(self) -> None:
|
||||
node = mock.Mock()
|
||||
node.factoryReset.side_effect = TypeError(
|
||||
"Field meshtastic.protobuf.AdminMessage.factory_reset_config: Expected an int, got a boolean."
|
||||
)
|
||||
node.iface = SimpleNamespace(localNode=node)
|
||||
|
||||
control_ui.request_factory_reset(node)
|
||||
|
||||
node.ensureSessionKey.assert_called_once_with()
|
||||
sent_message = node._sendAdmin.call_args.args[0]
|
||||
self.assertEqual(sent_message.factory_reset_config, 1)
|
||||
self.assertIsNone(node._sendAdmin.call_args.kwargs["onResponse"])
|
||||
|
||||
def test_request_factory_reset_full_falls_back_to_int_valued_admin_message(self) -> None:
|
||||
node = mock.Mock()
|
||||
node.factoryReset.side_effect = TypeError(
|
||||
"Field meshtastic.protobuf.AdminMessage.factory_reset_device: Expected an int, got a boolean."
|
||||
)
|
||||
node.iface = SimpleNamespace(localNode=node)
|
||||
|
||||
control_ui.request_factory_reset(node, full=True)
|
||||
|
||||
node.ensureSessionKey.assert_called_once_with()
|
||||
sent_message = node._sendAdmin.call_args.args[0]
|
||||
self.assertEqual(sent_message.factory_reset_device, 1)
|
||||
self.assertIsNone(node._sendAdmin.call_args.kwargs["onResponse"])
|
||||
|
||||
28
tests/test_menus.py
Normal file
28
tests/test_menus.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from types import SimpleNamespace
|
||||
import unittest
|
||||
|
||||
from meshtastic.protobuf import config_pb2, module_config_pb2
|
||||
|
||||
from contact.ui.menus import generate_menu_from_protobuf
|
||||
|
||||
|
||||
class MenusTests(unittest.TestCase):
|
||||
def test_main_menu_includes_factory_reset_config_after_factory_reset(self) -> None:
|
||||
local_node = SimpleNamespace(
|
||||
localConfig=config_pb2.Config(),
|
||||
moduleConfig=module_config_pb2.ModuleConfig(),
|
||||
getChannelByChannelIndex=lambda _: None,
|
||||
)
|
||||
interface = SimpleNamespace(
|
||||
localNode=local_node,
|
||||
getMyNodeInfo=lambda: {
|
||||
"user": {"longName": "Test User", "shortName": "TU", "isLicensed": False},
|
||||
"position": {"latitude": 0.0, "longitude": 0.0, "altitude": 0},
|
||||
},
|
||||
)
|
||||
|
||||
menu = generate_menu_from_protobuf(interface)
|
||||
keys = list(menu["Main Menu"].keys())
|
||||
|
||||
self.assertLess(keys.index("Factory Reset"), keys.index("factory_reset_config"))
|
||||
self.assertEqual(keys[keys.index("Factory Reset") + 1], "factory_reset_config")
|
||||
Reference in New Issue
Block a user