diff --git a/contact/localisations/en.ini b/contact/localisations/en.ini index b7f47d4..8642888 100644 --- a/contact/localisations/en.ini +++ b/contact/localisations/en.ini @@ -99,6 +99,7 @@ user, "User" longName, "Node long name", "If you are a licensed HAM operator and have enabled HAM mode, this must be set to your HAM operator call sign." shortName, "Node short name", "Must be up to 4 bytes. Usually this is 4 characters, if using latin characters and no emojis." isLicensed, "Enable licensed amateur (HAM) mode", "IMPORTANT: Read Meshtastic help documentation before enabling." +isUnmessagable, "Disable incoming messages", "When enabled, this node advertises that it cannot receive messages." [app_settings] title, "App Settings", "" diff --git a/contact/ui/control_ui.py b/contact/ui/control_ui.py index d755492..d387514 100644 --- a/contact/ui/control_ui.py +++ b/contact/ui/control_ui.py @@ -717,7 +717,7 @@ def settings_menu( menu_state.need_redraw = True continue - if selected_option in ["longName", "shortName", "isLicensed"]: + if selected_option in ["longName", "shortName", "isLicensed", "isUnmessagable"]: if selected_option in ["longName", "shortName"]: new_value = get_text_input( f"{human_readable_name} is currently: {current_value}", selected_option, None @@ -725,7 +725,7 @@ def settings_menu( new_value = current_value if new_value is None else new_value menu_state.current_menu[selected_option] = (field, new_value) - elif selected_option == "isLicensed": + elif selected_option in ["isLicensed", "isUnmessagable"]: new_value = get_list_input( f"{human_readable_name} is currently: {current_value}", str(current_value), diff --git a/contact/ui/menus.py b/contact/ui/menus.py index 070f81b..81fa439 100644 --- a/contact/ui/menus.py +++ b/contact/ui/menus.py @@ -76,6 +76,7 @@ def generate_menu_from_protobuf(interface: object, node: Any = None, include_app "longName": (None, current_user_config.get("longName", "Not Set")), "shortName": (None, current_user_config.get("shortName", "Not Set")), "isLicensed": (None, current_user_config.get("isLicensed", "False")), + "isUnmessagable": (None, current_user_config.get("isUnmessagable", "False")), } else: logging.info("User settings not found in Node Info") diff --git a/contact/utilities/save_to_radio.py b/contact/utilities/save_to_radio.py index f62feb3..a102526 100644 --- a/contact/utilities/save_to_radio.py +++ b/contact/utilities/save_to_radio.py @@ -29,7 +29,7 @@ LORA_REBOOT_KEYS = { "sx126x_rx_boosted_gain", } SECURITY_NON_REBOOT_KEYS = {"debug_log_api_enabled", "serial_enabled"} -USER_RECONNECT_KEYS = {"longName", "shortName", "isLicensed", "is_licensed"} +USER_RECONNECT_KEYS = {"longName", "shortName", "isLicensed", "is_licensed", "isUnmessagable", "is_unmessagable"} def _collect_changed_keys(modified_settings): @@ -143,12 +143,16 @@ def save_changes(interface, modified_settings, menu_state, node=None): long_name = modified_settings.get("longName") short_name = modified_settings.get("shortName") is_licensed = modified_settings.get("isLicensed") + is_unmessagable = modified_settings.get("isUnmessagable") is_licensed = is_licensed == "True" or is_licensed is True # Normalize boolean + if is_unmessagable is not None: + is_unmessagable = is_unmessagable == "True" or is_unmessagable is True - node.setOwner(long_name, short_name, is_licensed) + node.setOwner(long_name, short_name, is_licensed, is_unmessagable) logging.info( - f"Updated {config_category} with Long Name: {long_name}, Short Name: {short_name}, Licensed Mode: {is_licensed}" + f"Updated {config_category} with Long Name: {long_name}, Short Name: {short_name}, " + f"Licensed Mode: {is_licensed}, Unmessageable: {is_unmessagable}" ) return _requires_reconnect(menu_state, modified_settings) diff --git a/tests/test_menus.py b/tests/test_menus.py index 7c11094..ffae64d 100644 --- a/tests/test_menus.py +++ b/tests/test_menus.py @@ -44,3 +44,20 @@ class MenusTests(unittest.TestCase): self.assertEqual(module_settings["external_notification"]["ringtone"], (None, "tone")) self.assertEqual(module_settings["canned_message"]["messages"], (None, "Hi|Bye")) + + def test_user_settings_include_unmessageable_toggle(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", "isUnmessagable": True}, + "position": {}, + }, + ) + + user_settings = generate_menu_from_protobuf(interface)["Main Menu"]["User Settings"] + self.assertEqual(user_settings["isUnmessagable"], (None, True)) diff --git a/tests/test_save_to_radio.py b/tests/test_save_to_radio.py index 516925d..d52b19d 100644 --- a/tests/test_save_to_radio.py +++ b/tests/test_save_to_radio.py @@ -112,3 +112,12 @@ class SaveToRadioTests(unittest.TestCase): self.assertTrue(reconnect_required) node.setOwner.assert_called_once() + + def test_save_changes_sends_user_unmessageable_setting(self) -> None: + interface, node = self.build_interface() + menu_state = SimpleNamespace(menu_path=["Main Menu", "User Settings"]) + + reconnect_required = save_changes(interface, {"isUnmessagable": True}, menu_state) + + self.assertTrue(reconnect_required) + self.assertEqual(node.setOwner.call_args.args[3], True)