Improve error handling in API responses

Enhance the error handling mechanism in API responses to provide clearer and more informative messages to users. This change aims to improve user experience and debugging capabilities.
This commit is contained in:
pdxlocations
2026-07-24 10:14:49 -07:00
parent 954bd6ee82
commit c699845c81
4 changed files with 51 additions and 1 deletions
+2
View File
@@ -361,6 +361,7 @@ override_console_serial_port, "Override console serial port", "If set to true, t
[module.external_notification]
title, "External Notification"
ringtone, "Ringtone", "RTTTL ringtone played by supported external-notification hardware."
enabled, "Enabled", "Enables the module."
output_ms, "Length", "Specifies how long in milliseconds you would like your GPIOs to be active. In case of the repeat option, this is the duration of every tone and pause."
output, "Output GPIO", "Define the output pin GPIO setting Defaults to EXT_NOTIFY_OUT if set for the board. In standalone devices this pin should drive the LED to match the UI."
@@ -411,6 +412,7 @@ device_telemetry_enabled, "Device telemetry enabled", "Enable the Device Telemet
[module.canned_message]
title, "Canned Message"
messages, "Messages", "Pipe-separated canned messages, for example: Hi|On my way|Yes|No."
rotary1_enabled, "Rotary encoder enabled", "Enable the default rotary encoder."
inputbroker_pin_a, "Input broker pin A", "GPIO Pin Value (1-39) For encoder port A."
inputbroker_pin_b, "Input broker pin B", "GPIO Pin Value (1-39) For encoder port B."
+18
View File
@@ -609,6 +609,24 @@ def settings_menu(stdscr: object, interface: object) -> None:
# Fetch human-readable name from field_mapping
human_readable_name = field_mapping.get(full_key, selected_option)
if field is None and selected_option in ("ringtone", "messages"):
is_ringtone = selected_option == "ringtone"
getter_name = "get_ringtone" if is_ringtone else "get_canned_message"
setter_name = "set_ringtone" if is_ringtone else "set_canned_message"
getter = getattr(interface.localNode, getter_name, None)
setter = getattr(interface.localNode, setter_name, None)
fetched_value = getter() if callable(getter) else None
current_value = fetched_value if fetched_value is not None else current_value
new_value = get_text_input(
f"{human_readable_name} is currently: {current_value}", selected_option, None
)
if new_value is not None and new_value != current_value and callable(setter):
setter(new_value)
menu_state.current_menu[selected_option] = (field, new_value)
menu_state.start_index.pop()
menu_state.need_redraw = True
continue
if selected_option in ["longName", "shortName", "isLicensed"]:
if selected_option in ["longName", "shortName"]:
new_value = get_text_input(
+13 -1
View File
@@ -118,7 +118,19 @@ def generate_menu_from_protobuf(interface: object) -> Dict[str, Any]:
# Add Module Settings
module = module_config_pb2.ModuleConfig()
current_module_config = interface.localNode.moduleConfig if interface else None
menu_structure["Main Menu"]["Module Settings"] = extract_fields(module, current_module_config)
module_settings = extract_fields(module, current_module_config)
if interface and interface.localNode:
# These values use dedicated admin requests rather than ModuleConfig
# fields, so expose them alongside their related module settings.
module_settings.setdefault("external_notification", {})["ringtone"] = (
None,
getattr(interface.localNode, "ringtone", "") or "",
)
module_settings.setdefault("canned_message", {})["messages"] = (
None,
getattr(interface.localNode, "cannedPluginMessage", "") or "",
)
menu_structure["Main Menu"]["Module Settings"] = module_settings
# Add App Settings
menu_structure["Main Menu"]["App Settings"] = {"Open": "app_settings"}
+18
View File
@@ -26,3 +26,21 @@ class MenusTests(unittest.TestCase):
self.assertLess(keys.index("Factory Reset"), keys.index("factory_reset_config"))
self.assertEqual(keys[keys.index("Factory Reset") + 1], "factory_reset_config")
def test_module_settings_include_ringtone_and_canned_messages(self) -> None:
local_node = SimpleNamespace(
localConfig=config_pb2.Config(),
moduleConfig=module_config_pb2.ModuleConfig(),
ringtone="tone",
cannedPluginMessage="Hi|Bye",
getChannelByChannelIndex=lambda _: None,
)
interface = SimpleNamespace(
localNode=local_node,
getMyNodeInfo=lambda: {"position": {"latitude": 0.0, "longitude": 0.0, "altitude": 0}},
)
module_settings = generate_menu_from_protobuf(interface)["Main Menu"]["Module Settings"]
self.assertEqual(module_settings["external_notification"]["ringtone"], (None, "tone"))
self.assertEqual(module_settings["canned_message"]["messages"], (None, "Hi|Bye"))