SYMBOL INDEX (379 symbols across 54 files) FILE: contact/__main__.py function prompt_region_if_unset (line 66) | def prompt_region_if_unset(args: object, stdscr: Optional[curses.window]... function close_interface (line 77) | def close_interface(interface: object, timeout_seconds: float = DEFAULT_... function interface_is_ready (line 109) | def interface_is_ready(interface: object) -> bool: function initialize_runtime_interface_with_retry (line 116) | def initialize_runtime_interface_with_retry(stdscr: curses.window, args:... function initialize_globals (line 135) | def initialize_globals(seed_demo: bool = False) -> None: function initialize_runtime_interface (line 159) | def initialize_runtime_interface(args: object): function main (line 166) | def main(stdscr: curses.window) -> None: function ensure_min_rows (line 210) | def ensure_min_rows(stdscr: curses.window, min_rows: int = 11) -> None: function start (line 228) | def start() -> None: FILE: contact/message_handlers/bot_handler.py function _get_bot_catch_words (line 13) | def _get_bot_catch_words() -> set[str]: function is_bot_message (line 23) | def is_bot_message(message: str) -> bool: function bot_respond (line 27) | def bot_respond(packet: Dict[str, Any], message: str, send_channel: int)... FILE: contact/message_handlers/rx_handler.py function schedule_notification_sound (line 16) | def schedule_notification_sound(delay: float = _SOUND_DEBOUNCE_SECONDS) ... function play_sound (line 66) | def play_sound(): function on_receive (line 106) | def on_receive(packet: Dict[str, Any], interface: Any) -> None: FILE: contact/message_handlers/tx_handler.py function onAckNak (line 27) | def onAckNak(packet: Dict[str, Any]) -> None: function on_response_traceroute (line 66) | def on_response_traceroute(packet: Dict[str, Any]) -> None: function send_message (line 167) | def send_message(message: str, destination: int = BROADCAST_NUM, channel... function send_traceroute (line 200) | def send_traceroute() -> None: FILE: contact/settings.py function close_interface (line 19) | def close_interface(interface: object) -> None: function main (line 26) | def main(stdscr: curses.window) -> None: function ensure_min_rows (line 66) | def ensure_min_rows(stdscr: curses.window, min_rows: int = 11) -> None: FILE: contact/ui/colors.py function setup_colors (line 16) | def setup_colors(reinit: bool = False) -> None: function get_color (line 33) | def get_color(category: str, bold: bool = False, reverse: bool = False, ... FILE: contact/ui/contact_ui.py function request_ui_redraw (line 36) | def request_ui_redraw( function process_pending_ui_updates (line 53) | def process_pending_ui_updates(stdscr: curses.window) -> None: function draw_window_arrows (line 84) | def draw_window_arrows(window_id: int) -> None: function compute_widths (line 103) | def compute_widths(total_w: int, focus: int): function paint_frame (line 116) | def paint_frame(win, selected: bool) -> None: function get_channel_row_color (line 123) | def get_channel_row_color(index: int) -> int: function get_node_row_color (line 131) | def get_node_row_color(index: int, highlight: bool = False) -> int: function refresh_node_selection (line 143) | def refresh_node_selection(old_index: int = -1, highlight: bool = False)... function refresh_main_window (line 166) | def refresh_main_window(window_id: int, selected: bool) -> None: function get_node_display_name (line 184) | def get_node_display_name(node_num: int, node: dict) -> str: function get_selected_channel_title (line 189) | def get_selected_channel_title() -> str: function get_window_title (line 199) | def get_window_title(window: int) -> str: function draw_frame_title (line 207) | def draw_frame_title(box: curses.window, title: str) -> None: function handle_resize (line 226) | def handle_resize(stdscr: curses.window, firstrun: bool) -> None: function drain_resize_events (line 340) | def drain_resize_events(input_win: curses.window) -> Union[str, int, None]: function main_ui (line 358) | def main_ui(stdscr: curses.window) -> None: function handle_up (line 466) | def handle_up() -> None: function handle_down (line 476) | def handle_down() -> None: function handle_home (line 486) | def handle_home() -> None: function handle_end (line 499) | def handle_end() -> None: function handle_pageup (line 512) | def handle_pageup() -> None: function handle_pagedown (line 526) | def handle_pagedown() -> None: function handle_leftright (line 542) | def handle_leftright(char: int) -> None: function handle_function_keys (line 560) | def handle_function_keys(char: int) -> None: function handle_enter (line 590) | def handle_enter(input_text: str) -> str: function handle_f5_key (line 637) | def handle_f5_key(stdscr: curses.window) -> None: function handle_ctrl_t (line 850) | def handle_ctrl_t(stdscr: curses.window) -> None: function handle_backspace (line 885) | def handle_backspace(entry_win: curses.window, input_text: str) -> str: function handle_backtick (line 897) | def handle_backtick(stdscr: curses.window) -> None: function handle_ctrl_p (line 911) | def handle_ctrl_p() -> None: function handle_ctrl_k (line 924) | def handle_ctrl_k(stdscr: curses.window) -> None: function handle_ctrl_b (line 952) | def handle_ctrl_b(stdscr: curses.window) -> None: function handle_ctrl_d (line 978) | def handle_ctrl_d() -> None: function handle_ctrl_fslash (line 1024) | def handle_ctrl_fslash() -> None: function handle_ctrl_f (line 1030) | def handle_ctrl_f(stdscr: curses.window) -> None: function handle_ctlr_g (line 1074) | def handle_ctlr_g(stdscr: curses.window) -> None: function draw_channel_list (line 1111) | def draw_channel_list() -> None: function draw_messages_window (line 1155) | def draw_messages_window(scroll_to_bottom: bool = False) -> None: function draw_node_list (line 1207) | def draw_node_list() -> None: function select_channel (line 1250) | def select_channel(idx: int) -> None: function scroll_channels (line 1272) | def scroll_channels(direction: int) -> None: function scroll_messages (line 1284) | def scroll_messages(direction: int) -> None: function select_node (line 1311) | def select_node(idx: int) -> None: function scroll_nodes (line 1326) | def scroll_nodes(direction: int) -> None: function draw_packetlog_win (line 1338) | def draw_packetlog_win() -> None: function search (line 1392) | def search(win: int) -> None: function refresh_pad (line 1448) | def refresh_pad(window: int) -> None: function add_notification (line 1520) | def add_notification(channel_number: int) -> None: function remove_notification (line 1525) | def remove_notification(channel_number: int) -> None: function draw_text_field (line 1530) | def draw_text_field(win: curses.window, text: str, color: int) -> None: function draw_centered_text_field (line 1551) | def draw_centered_text_field(win: curses.window, text: str, y_offset: in... FILE: contact/ui/control_ui.py function get_menu_width (line 42) | def get_menu_width() -> int: function _is_repeated_field (line 63) | def _is_repeated_field(field_desc) -> bool: function reload_translations (line 71) | def reload_translations() -> None: function get_translated_header (line 77) | def get_translated_header(menu_path: List[str]) -> str: function display_menu (line 92) | def display_menu() -> tuple[object, object]: function draw_help_window (line 197) | def draw_help_window( function get_input_type_for_field (line 220) | def get_input_type_for_field(field) -> type: function reconnect_interface_with_splash (line 229) | def reconnect_interface_with_splash(stdscr: object, interface: object) -... function reconnect_after_admin_action (line 244) | def reconnect_after_admin_action(stdscr: object, interface: object, acti... function request_factory_reset (line 250) | def request_factory_reset(node: object, full: bool = False): function redraw_main_ui_after_reconnect (line 270) | def redraw_main_ui_after_reconnect(stdscr: object) -> None: function settings_menu (line 282) | def settings_menu(stdscr: object, interface: object) -> None: function rebuild_menu_at_current_path (line 784) | def rebuild_menu_at_current_path(interface, menu_state): function set_region (line 794) | def set_region(interface: object) -> None: FILE: contact/ui/default_config.py function reload_config (line 17) | def reload_config() -> None: function _is_writable_dir (line 23) | def _is_writable_dir(path: str) -> bool: function _get_config_root (line 39) | def _get_config_root(preferred_dir: str, fallback_name: str = ".contact_... function get_localisation_options (line 75) | def get_localisation_options(localisations_path: Optional[str] = None) -... function get_localisation_file (line 92) | def get_localisation_file(language: str, localisations_path: Optional[st... function format_json_single_line_arrays (line 112) | def format_json_single_line_arrays(data: Dict[str, object], indent: int ... function update_dict (line 132) | def update_dict(default: Dict[str, object], actual: Dict[str, object]) -... function initialize_config (line 144) | def initialize_config() -> Dict[str, object]: function assign_config_variables (line 276) | def assign_config_variables(loaded_config: Dict[str, object]) -> None: FILE: contact/ui/dialog.py function dialog (line 9) | def dialog(title: str, message: str) -> None: FILE: contact/ui/menus.py function encode_if_bytes (line 10) | def encode_if_bytes(value: Any) -> str: function extract_fields (line 17) | def extract_fields( function generate_menu_from_protobuf (line 60) | def generate_menu_from_protobuf(interface: object) -> Dict[str, Any]: FILE: contact/ui/nav_utils.py function get_node_color (line 12) | def get_node_color(node_index: int, reverse: bool = False): function get_save_option_label (line 30) | def get_save_option_label() -> str: function move_highlight (line 33) | def move_highlight( function draw_arrows (line 152) | def draw_arrows( function update_help_window (line 170) | def update_help_window( function get_wrapped_help_text (line 237) | def get_wrapped_help_text( function text_width (line 330) | def text_width(text: str) -> int: function slice_to_width (line 334) | def slice_to_width(text: str, max_width: int) -> str: function pad_to_width (line 349) | def pad_to_width(text: str, width: int) -> str: function truncate_with_ellipsis (line 354) | def truncate_with_ellipsis(text: str, width: int) -> str: function split_text_to_width_chunks (line 364) | def split_text_to_width_chunks(text: str, width: int) -> List[str]: function wrap_text (line 379) | def wrap_text(text: str, wrap_width: int) -> List[str]: function move_main_highlight (line 418) | def move_main_highlight( function highlight_line (line 447) | def highlight_line( function draw_main_arrows (line 476) | def draw_main_arrows(win: object, max_index: int, window: int, **kwargs)... function get_msg_window_lines (line 501) | def get_msg_window_lines(messages_win, packetlog_win) -> None: FILE: contact/ui/splash.py function draw_splash (line 5) | def draw_splash(stdscr: object) -> None: FILE: contact/ui/ui_state.py class MenuState (line 6) | class MenuState: class ChatUIState (line 17) | class ChatUIState: class InterfaceState (line 46) | class InterfaceState: class AppState (line 52) | class AppState: FILE: contact/ui/user_config.py function reload_translations (line 23) | def reload_translations(language: Optional[str] = None) -> None: function get_app_settings_key (line 31) | def get_app_settings_key(menu_path: List[str], selected_key: str) -> str: function get_app_settings_path_parts (line 41) | def get_app_settings_path_parts(menu_path: List[str]) -> List[str]: function lookup_app_settings_label (line 50) | def lookup_app_settings_label(full_key: str, fallback: str) -> str: function get_app_settings_help_path_parts (line 61) | def get_app_settings_help_path_parts(menu_path: List[str]) -> List[str]: function get_app_settings_header (line 68) | def get_app_settings_header(menu_path: List[str]) -> str: function get_effective_width (line 84) | def get_effective_width() -> int: function edit_color_pair (line 89) | def edit_color_pair(key: str, display_label: str, current_value: List[st... function edit_value (line 116) | def edit_value(key: str, display_label: str, current_value: str) -> str: function display_menu (line 255) | def display_menu() -> tuple[Any, Any, List[str]]: function update_app_settings_help (line 369) | def update_app_settings_help(menu_win: curses.window, options: List[str]... function json_editor (line 385) | def json_editor(stdscr: curses.window, menu_state: Any) -> None: function save_json (line 568) | def save_json(file_path: str, data: Dict[str, Any]) -> None: function main (line 576) | def main(stdscr: curses.window) -> None: FILE: contact/utilities/arg_parser.py function setup_parser (line 4) | def setup_parser() -> ArgumentParser: FILE: contact/utilities/config_io.py function _is_repeated_field (line 12) | def _is_repeated_field(field_desc) -> bool: function traverseConfig (line 23) | def traverseConfig(config_root, config, interface_config) -> bool: function splitCompoundName (line 36) | def splitCompoundName(comp_name: str) -> List[str]: function setPref (line 45) | def setPref(config, comp_name, raw_val) -> bool: function config_import (line 137) | def config_import(interface, filename): function config_export (line 213) | def config_export(interface) -> str: FILE: contact/utilities/control_utils.py function transform_menu_path (line 5) | def transform_menu_path(menu_path: List[str]) -> List[str]: FILE: contact/utilities/db_handler.py function get_table_name (line 14) | def get_table_name(channel: str) -> str: function save_message_to_db (line 21) | def save_message_to_db(channel: str, user_id: str, message_text: str) ->... function update_ack_nak (line 55) | def update_ack_nak(channel: str, timestamp: int, message: str, ack: str)... function load_messages_from_db (line 78) | def load_messages_from_db() -> None: function init_nodedb (line 172) | def init_nodedb() -> None: function maybe_store_nodeinfo_in_db (line 202) | def maybe_store_nodeinfo_in_db(packet: Dict[str, object]) -> None: function update_node_info_in_db (line 221) | def update_node_info_in_db( function ensure_node_table_exists (line 301) | def ensure_node_table_exists() -> None: function ensure_table_exists (line 317) | def ensure_table_exists(table_name: str, schema: str) -> None: function get_name_from_database (line 332) | def get_name_from_database(user_id: int, type: str = "long") -> str: function is_chat_archived (line 368) | def is_chat_archived(user_id: int) -> int: FILE: contact/utilities/demo_data.py class DemoChannelSettings (line 19) | class DemoChannelSettings: class DemoChannel (line 24) | class DemoChannel: class DemoLoRaConfig (line 30) | class DemoLoRaConfig: class DemoLocalConfig (line 36) | class DemoLocalConfig: class DemoLocalNode (line 40) | class DemoLocalNode: method __init__ (line 41) | def __init__(self, interface: "DemoInterface", channels: List[DemoChan... method setFavorite (line 46) | def setFavorite(self, node_num: int) -> None: method removeFavorite (line 49) | def removeFavorite(self, node_num: int) -> None: method setIgnored (line 52) | def setIgnored(self, node_num: int) -> None: method removeIgnored (line 55) | def removeIgnored(self, node_num: int) -> None: method removeNode (line 58) | def removeNode(self, node_num: int) -> None: class DemoInterface (line 62) | class DemoInterface: method __init__ (line 63) | def __init__(self, nodes: Dict[int, Dict[str, object]], channels: List... method getMyNodeInfo (line 68) | def getMyNodeInfo(self) -> Dict[str, int]: method getNode (line 71) | def getNode(self, selector: str) -> DemoLocalNode: method close (line 76) | def close(self) -> None: function build_demo_interface (line 80) | def build_demo_interface() -> DemoInterface: function configure_demo_database (line 123) | def configure_demo_database(base_dir: str = "") -> str: function seed_demo_messages (line 136) | def seed_demo_messages() -> None: function _build_node (line 162) | def _build_node( function _demo_messages (line 212) | def _demo_messages() -> Dict[Union[str, int], List[Tuple[str, str, int, ... FILE: contact/utilities/emoji_utils.py function _regional_indicator_to_letter (line 21) | def _regional_indicator_to_letter(char: str) -> str: function _normalize_flag_emoji (line 25) | def _normalize_flag_emoji(text: str) -> str: function normalize_message_text (line 49) | def normalize_message_text(text: str) -> str: FILE: contact/utilities/i18n.py function _load_translations (line 10) | def _load_translations() -> None: function t (line 21) | def t(key: str, default: Optional[str] = None, **kwargs: object) -> str: function t_text (line 30) | def t_text(text: str, **kwargs: object) -> str: FILE: contact/utilities/ini_utils.py function parse_ini_file (line 5) | def parse_ini_file(ini_file_path: str) -> Tuple[Dict[str, str], Dict[str... FILE: contact/utilities/input_handlers.py function get_dialog_width (line 19) | def get_dialog_width() -> int: function invalid_input (line 28) | def invalid_input(window: curses.window, message: str, redraw_func: Opti... function get_text_input (line 41) | def get_text_input(prompt: str, selected_config: str, input_type: str) -... function get_admin_key_input (line 267) | def get_admin_key_input(current_value: List[bytes]) -> Optional[List[str]]: function get_repeated_input (line 373) | def get_repeated_input(current_value: List[str]) -> Optional[str]: function get_fixed32_input (line 463) | def get_fixed32_input(current_value: int) -> int: function get_list_input (line 557) | def get_list_input( FILE: contact/utilities/interfaces.py function initialize_interface (line 6) | def initialize_interface(args): function reconnect_interface (line 47) | def reconnect_interface(args, attempts: int = 15, delay_seconds: float =... FILE: contact/utilities/save_to_radio.py function _collect_changed_keys (line 35) | def _collect_changed_keys(modified_settings): function _requires_reconnect (line 45) | def _requires_reconnect(menu_state, modified_settings) -> bool: function save_changes (line 81) | def save_changes(interface, modified_settings, menu_state): FILE: contact/utilities/telemetry_beautifier.py function humanize_wind_direction (line 22) | def humanize_wind_direction(degrees): function get_chunks (line 49) | def get_chunks(data): FILE: contact/utilities/utils.py function _get_channel_name (line 13) | def _get_channel_name(device_channel, node): function get_channels (line 23) | def get_channels(): function get_node_list (line 61) | def get_node_list(): function refresh_node_list (line 90) | def refresh_node_list(): function get_nodeNum (line 98) | def get_nodeNum(): function decimal_to_hex (line 104) | def decimal_to_hex(decimal_number): function convert_to_camel_case (line 108) | def convert_to_camel_case(string): function get_time_val_units (line 114) | def get_time_val_units(time_delta): function get_readable_duration (line 142) | def get_readable_duration(seconds): function get_time_ago (line 148) | def get_time_ago(timestamp): function add_new_message (line 159) | def add_new_message(channel_id, prefix, message): function parse_protobuf (line 189) | def parse_protobuf(packet: dict) -> Union[str, dict]: FILE: contact/utilities/validation_rules.py function get_validation_for (line 19) | def get_validation_for(key: str) -> dict: FILE: tests/test_arg_parser.py class ArgParserTests (line 6) | class ArgParserTests(unittest.TestCase): method test_demo_screenshot_flag_is_supported (line 7) | def test_demo_screenshot_flag_is_supported(self) -> None: method test_demo_screenshot_defaults_to_false (line 11) | def test_demo_screenshot_defaults_to_false(self) -> None: FILE: tests/test_bot_handler.py class BotHandlerTests (line 10) | class BotHandlerTests(unittest.TestCase): method setUpClass (line 12) | def setUpClass(cls) -> None: method test_is_bot_message_uses_configured_catch_words (line 19) | def test_is_bot_message_uses_configured_catch_words(self) -> None: method test_is_bot_message_ignores_empty_config_values (line 25) | def test_is_bot_message_ignores_empty_config_values(self) -> None: FILE: tests/test_config_io.py class ConfigIoTests (line 6) | class ConfigIoTests(unittest.TestCase): method test_split_compound_name_preserves_multi_part_values (line 7) | def test_split_compound_name_preserves_multi_part_values(self) -> None: method test_split_compound_name_duplicates_single_part_values (line 10) | def test_split_compound_name_duplicates_single_part_values(self) -> None: method test_is_repeated_field_prefers_new_style_attribute (line 13) | def test_is_repeated_field_prefers_new_style_attribute(self) -> None: method test_is_repeated_field_falls_back_to_label_comparison (line 18) | def test_is_repeated_field_falls_back_to_label_comparison(self) -> None: FILE: tests/test_contact_ui.py class ContactUiTests (line 12) | class ContactUiTests(unittest.TestCase): method setUp (line 13) | def setUp(self) -> None: method tearDown (line 17) | def tearDown(self) -> None: method test_handle_backtick_refreshes_channels_after_settings_menu (line 21) | def test_handle_backtick_refreshes_channels_after_settings_menu(self) ... method test_process_pending_ui_updates_draws_requested_windows (line 41) | def test_process_pending_ui_updates_draws_requested_windows(self) -> N... method test_process_pending_ui_updates_full_redraw_uses_handle_resize (line 60) | def test_process_pending_ui_updates_full_redraw_uses_handle_resize(sel... method test_refresh_node_selection_reserves_scroll_arrow_column (line 73) | def test_refresh_node_selection_reserves_scroll_arrow_column(self) -> ... method test_draw_channel_list_reserves_scroll_arrow_column (line 98) | def test_draw_channel_list_reserves_scroll_arrow_column(self) -> None: method test_draw_node_list_reserves_scroll_arrow_column (line 117) | def test_draw_node_list_reserves_scroll_arrow_column(self) -> None: method test_handle_resize_single_pane_keeps_full_width_windows (line 139) | def test_handle_resize_single_pane_keeps_full_width_windows(self) -> N... method test_get_window_title_uses_selected_channel_only_for_messages_in_single_pane_mode (line 175) | def test_get_window_title_uses_selected_channel_only_for_messages_in_s... method test_refresh_pad_draws_selected_channel_title_on_message_frame (line 183) | def test_refresh_pad_draws_selected_channel_title_on_message_frame(sel... method test_search_ignores_no_input_from_curses (line 204) | def test_search_ignores_no_input_from_curses(self) -> None: method test_f5_node_details_ignores_no_input_from_curses (line 217) | def test_f5_node_details_ignores_no_input_from_curses(self) -> None: method test_f5_node_details_tolerates_none_metrics (line 257) | def test_f5_node_details_tolerates_none_metrics(self) -> None: FILE: tests/test_control_ui.py class ControlUiTests (line 12) | class ControlUiTests(unittest.TestCase): method setUp (line 13) | def setUp(self) -> None: method tearDown (line 16) | def tearDown(self) -> None: method test_reconnect_interface_with_splash_replaces_interface (line 19) | def test_reconnect_interface_with_splash_replaces_interface(self) -> N... method test_reconnect_after_admin_action_runs_action_then_reconnects (line 41) | def test_reconnect_after_admin_action_runs_action_then_reconnects(self... method test_redraw_main_ui_after_reconnect_refreshes_channels_nodes_and_layout (line 56) | def test_redraw_main_ui_after_reconnect_refreshes_channels_nodes_and_l... method test_request_factory_reset_uses_library_helper_when_supported (line 68) | def test_request_factory_reset_uses_library_helper_when_supported(self... method test_request_factory_reset_uses_library_helper_for_full_reset_when_supported (line 77) | def test_request_factory_reset_uses_library_helper_for_full_reset_when... method test_request_factory_reset_falls_back_to_int_valued_admin_message (line 86) | def test_request_factory_reset_falls_back_to_int_valued_admin_message(... method test_request_factory_reset_full_falls_back_to_int_valued_admin_message (line 100) | def test_request_factory_reset_full_falls_back_to_int_valued_admin_mes... FILE: tests/test_control_utils.py class ControlUtilsTests (line 6) | class ControlUtilsTests(unittest.TestCase): method test_transform_menu_path_applies_replacements_and_normalization (line 7) | def test_transform_menu_path_applies_replacements_and_normalization(se... method test_transform_menu_path_preserves_unmatched_entries (line 12) | def test_transform_menu_path_preserves_unmatched_entries(self) -> None: FILE: tests/test_db_handler.py class DbHandlerTests (line 15) | class DbHandlerTests(unittest.TestCase): method setUp (line 16) | def setUp(self) -> None: method tearDown (line 31) | def tearDown(self) -> None: method test_save_message_to_db_and_update_ack_roundtrip (line 36) | def test_save_message_to_db_and_update_ack_roundtrip(self) -> None: method test_update_node_info_in_db_fills_defaults_and_preserves_existing_values (line 48) | def test_update_node_info_in_db_fills_defaults_and_preserves_existing_... method test_get_name_from_database_returns_hex_when_user_is_missing (line 62) | def test_get_name_from_database_returns_hex_when_user_is_missing(self)... method test_load_messages_from_db_populates_channels_and_messages (line 69) | def test_load_messages_from_db_populates_channels_and_messages(self) -... method test_init_nodedb_inserts_nodes_from_interface (line 115) | def test_init_nodedb_inserts_nodes_from_interface(self) -> None: FILE: tests/test_default_config.py class DefaultConfigTests (line 7) | class DefaultConfigTests(unittest.TestCase): method test_get_localisation_options_filters_hidden_and_non_ini_files (line 8) | def test_get_localisation_options_filters_hidden_and_non_ini_files(sel... method test_get_localisation_file_normalizes_extensions_and_falls_back_to_english (line 16) | def test_get_localisation_file_normalizes_extensions_and_falls_back_to... method test_update_dict_only_adds_missing_values (line 25) | def test_update_dict_only_adds_missing_values(self) -> None: method test_format_json_single_line_arrays_keeps_arrays_inline (line 34) | def test_format_json_single_line_arrays_keeps_arrays_inline(self) -> N... FILE: tests/test_demo_data.py class DemoDataTests (line 14) | class DemoDataTests(unittest.TestCase): method setUp (line 15) | def setUp(self) -> None: method tearDown (line 19) | def tearDown(self) -> None: method test_build_demo_interface_exposes_expected_shape (line 23) | def test_build_demo_interface_exposes_expected_shape(self) -> None: method test_initialize_globals_seed_demo_populates_ui_state_and_db (line 30) | def test_initialize_globals_seed_demo_populates_ui_state_and_db(self) ... FILE: tests/test_dialog.py class _FakeWindow (line 8) | class _FakeWindow: method __init__ (line 9) | def __init__(self, height: int, width: int) -> None: method erase (line 16) | def erase(self) -> None: method bkgd (line 19) | def bkgd(self, *_args) -> None: method attrset (line 22) | def attrset(self, *_args) -> None: method border (line 25) | def border(self, *_args) -> None: method addstr (line 28) | def addstr(self, y: int, x: int, text: str, *_args) -> None: method derwin (line 31) | def derwin(self, height: int, width: int, _y: int, _x: int): method noutrefresh (line 36) | def noutrefresh(self) -> None: method keypad (line 39) | def keypad(self, *_args) -> None: method timeout (line 42) | def timeout(self, *_args) -> None: method getch (line 45) | def getch(self) -> int: method refresh (line 48) | def refresh(self) -> None: method getmaxyx (line 51) | def getmaxyx(self): class DialogTests (line 55) | class DialogTests(unittest.TestCase): method setUp (line 56) | def setUp(self) -> None: method tearDown (line 61) | def tearDown(self) -> None: method test_dialog_renders_full_message_when_width_is_sufficient (line 66) | def test_dialog_renders_full_message_when_width_is_sufficient(self) ->... FILE: tests/test_emoji_utils.py class EmojiUtilsTests (line 6) | class EmojiUtilsTests(unittest.TestCase): method test_strips_modifiers_from_keycaps_and_skin_tones (line 7) | def test_strips_modifiers_from_keycaps_and_skin_tones(self) -> None: method test_rewrites_flag_emoji_to_country_codes (line 10) | def test_rewrites_flag_emoji_to_country_codes(self) -> None: FILE: tests/test_i18n.py class I18nTests (line 13) | class I18nTests(unittest.TestCase): method setUp (line 14) | def setUp(self) -> None: method tearDown (line 19) | def tearDown(self) -> None: method test_t_loads_translation_file_and_formats_placeholders (line 24) | def test_t_loads_translation_file_and_formats_placeholders(self) -> None: method test_t_falls_back_to_default_and_returns_unformatted_text_on_error (line 35) | def test_t_falls_back_to_default_and_returns_unformatted_text_on_error... method test_loader_cache_is_reused_until_language_changes (line 48) | def test_loader_cache_is_reused_until_language_changes(self) -> None: method test_bot_ui_translation_keys_exist_in_all_locales (line 60) | def test_bot_ui_translation_keys_exist_in_all_locales(self) -> None: FILE: tests/test_ini_utils.py class IniUtilsTests (line 9) | class IniUtilsTests(unittest.TestCase): method test_parse_ini_file_reads_sections_fields_and_help_text (line 10) | def test_parse_ini_file_reads_sections_fields_and_help_text(self) -> N... method test_parse_ini_file_uses_builtin_help_fallback_when_i18n_fails (line 29) | def test_parse_ini_file_uses_builtin_help_fallback_when_i18n_fails(sel... FILE: tests/test_interfaces.py class InterfacesTests (line 8) | class InterfacesTests(unittest.TestCase): method test_reconnect_interface_retries_until_connection_succeeds (line 9) | def test_reconnect_interface_retries_until_connection_succeeds(self) -... method test_reconnect_interface_raises_after_exhausting_attempts (line 20) | def test_reconnect_interface_raises_after_exhausting_attempts(self) ->... FILE: tests/test_main.py class MainRuntimeTests (line 13) | class MainRuntimeTests(unittest.TestCase): method setUp (line 14) | def setUp(self) -> None: method tearDown (line 18) | def tearDown(self) -> None: method test_initialize_runtime_interface_uses_demo_branch (line 22) | def test_initialize_runtime_interface_uses_demo_branch(self) -> None: method test_initialize_runtime_interface_uses_live_branch_without_demo_flag (line 35) | def test_initialize_runtime_interface_uses_live_branch_without_demo_fl... method test_interface_is_ready_detects_missing_local_node (line 44) | def test_interface_is_ready_detects_missing_local_node(self) -> None: method test_initialize_runtime_interface_with_retry_retries_until_node_is_ready (line 48) | def test_initialize_runtime_interface_with_retry_retries_until_node_is... method test_initialize_runtime_interface_with_retry_returns_none_when_user_closes (line 64) | def test_initialize_runtime_interface_with_retry_returns_none_when_use... method test_prompt_region_if_unset_reinitializes_interface_after_confirmation (line 79) | def test_prompt_region_if_unset_reinitializes_interface_after_confirma... method test_prompt_region_if_unset_leaves_interface_unchanged_when_declined (line 98) | def test_prompt_region_if_unset_leaves_interface_unchanged_when_declin... method test_initialize_globals_resets_and_populates_runtime_state (line 113) | def test_initialize_globals_resets_and_populates_runtime_state(self) -... method test_ensure_min_rows_retries_until_terminal_is_large_enough (line 152) | def test_ensure_min_rows_retries_until_terminal_is_large_enough(self) ... method test_start_prints_help_and_exits_zero (line 165) | def test_start_prints_help_and_exits_zero(self) -> None: method test_start_runs_curses_wrapper_and_closes_interface (line 178) | def test_start_runs_curses_wrapper_and_closes_interface(self) -> None: method test_start_does_not_crash_when_wrapper_returns_without_interface (line 189) | def test_start_does_not_crash_when_wrapper_returns_without_interface(s... method test_main_returns_cleanly_when_user_closes_missing_node_dialog (line 198) | def test_main_returns_cleanly_when_user_closes_missing_node_dialog(sel... method test_start_handles_keyboard_interrupt (line 213) | def test_start_handles_keyboard_interrupt(self) -> None: method test_start_handles_keyboard_interrupt_with_no_interface (line 227) | def test_start_handles_keyboard_interrupt_with_no_interface(self) -> N... method test_start_handles_fatal_exception_and_exits_one (line 239) | def test_start_handles_fatal_exception_and_exits_one(self) -> None: FILE: tests/test_menus.py class MenusTests (line 9) | class MenusTests(unittest.TestCase): method test_main_menu_includes_factory_reset_config_after_factory_reset (line 10) | def test_main_menu_includes_factory_reset_config_after_factory_reset(s... FILE: tests/test_nav_utils.py class NavUtilsTests (line 9) | class NavUtilsTests(unittest.TestCase): method setUp (line 10) | def setUp(self) -> None: method test_wrap_text_splits_wide_characters_by_display_width (line 15) | def test_wrap_text_splits_wide_characters_by_display_width(self) -> None: method test_truncate_with_ellipsis_respects_display_width (line 18) | def test_truncate_with_ellipsis_respects_display_width(self) -> None: method test_highlight_line_reserves_scroll_arrow_column_for_nodes (line 21) | def test_highlight_line_reserves_scroll_arrow_column_for_nodes(self) -... FILE: tests/test_rx_handler.py class RxHandlerTests (line 11) | class RxHandlerTests(unittest.TestCase): method setUp (line 12) | def setUp(self) -> None: method tearDown (line 17) | def tearDown(self) -> None: method test_on_receive_text_message_refreshes_selected_channel (line 21) | def test_on_receive_text_message_refreshes_selected_channel(self) -> N... method test_on_receive_direct_message_adds_channel_and_notification (line 50) | def test_on_receive_direct_message_adds_channel_and_notification(self)... method test_on_receive_trims_packet_buffer_even_when_packet_is_undecoded (line 79) | def test_on_receive_trims_packet_buffer_even_when_packet_is_undecoded(... FILE: tests/test_save_to_radio.py class SaveToRadioTests (line 8) | class SaveToRadioTests(unittest.TestCase): method build_interface (line 9) | def build_interface(self): method test_save_changes_returns_true_for_lora_writes_that_require_reconnect (line 25) | def test_save_changes_returns_true_for_lora_writes_that_require_reconn... method test_save_changes_returns_false_when_nothing_changed (line 35) | def test_save_changes_returns_false_when_nothing_changed(self) -> None: method test_save_changes_returns_false_for_non_rebooting_security_fields (line 41) | def test_save_changes_returns_false_for_non_rebooting_security_fields(... method test_save_changes_returns_true_for_rebooting_security_fields (line 50) | def test_save_changes_returns_true_for_rebooting_security_fields(self)... method test_save_changes_returns_true_only_for_rebooting_device_fields (line 58) | def test_save_changes_returns_true_only_for_rebooting_device_fields(se... method test_save_changes_returns_true_for_network_settings (line 69) | def test_save_changes_returns_true_for_network_settings(self) -> None: method test_save_changes_returns_true_only_for_rebooting_power_fields (line 78) | def test_save_changes_returns_true_only_for_rebooting_power_fields(sel... method test_save_changes_returns_true_for_module_settings (line 89) | def test_save_changes_returns_true_for_module_settings(self) -> None: method test_save_changes_returns_true_for_user_name_changes (line 98) | def test_save_changes_returns_true_for_user_name_changes(self) -> None: method test_save_changes_returns_true_for_user_license_changes (line 107) | def test_save_changes_returns_true_for_user_license_changes(self) -> N... FILE: tests/test_settings.py class SettingsRuntimeTests (line 9) | class SettingsRuntimeTests(unittest.TestCase): method test_main_closes_interface_after_normal_settings_exit (line 10) | def test_main_closes_interface_after_normal_settings_exit(self) -> None: method test_main_closes_reconnected_interface_after_region_reset (line 29) | def test_main_closes_reconnected_interface_after_region_reset(self) ->... method test_main_closes_interface_when_settings_menu_raises (line 58) | def test_main_closes_interface_when_settings_menu_raises(self) -> None: FILE: tests/test_support.py function reset_singletons (line 8) | def reset_singletons() -> None: function restore_config (line 16) | def restore_config(saved: dict) -> None: function snapshot_config (line 21) | def snapshot_config(*keys: str) -> dict: function _reset_instance (line 25) | def _reset_instance(target: object, replacement: object) -> None: FILE: tests/test_telemetry_beautifier.py class TelemetryBeautifierTests (line 7) | class TelemetryBeautifierTests(unittest.TestCase): method test_humanize_wind_direction_handles_boundaries (line 8) | def test_humanize_wind_direction_handles_boundaries(self) -> None: method test_get_chunks_formats_known_and_unknown_values (line 14) | def test_get_chunks_formats_known_and_unknown_values(self) -> None: method test_get_chunks_formats_time_values (line 22) | def test_get_chunks_formats_time_values(self) -> None: FILE: tests/test_tx_handler.py class TxHandlerTests (line 14) | class TxHandlerTests(unittest.TestCase): method setUp (line 15) | def setUp(self) -> None: method tearDown (line 20) | def tearDown(self) -> None: method test_send_message_on_named_channel_tracks_ack_request (line 25) | def test_send_message_on_named_channel_tracks_ack_request(self) -> None: method test_send_message_to_direct_node_uses_node_as_destination (line 51) | def test_send_message_to_direct_node_uses_node_as_destination(self) ->... method test_on_ack_nak_updates_message_for_explicit_ack (line 73) | def test_on_ack_nak_updates_message_for_explicit_ack(self) -> None: method test_on_ack_nak_uses_implicit_marker_for_self_ack (line 92) | def test_on_ack_nak_uses_implicit_marker_for_self_ack(self) -> None: FILE: tests/test_utils.py class UtilsTests (line 12) | class UtilsTests(unittest.TestCase): method setUp (line 13) | def setUp(self) -> None: method tearDown (line 17) | def tearDown(self) -> None: method test_get_node_list_keeps_local_first_and_ignored_last (line 21) | def test_get_node_list_keeps_local_first_and_ignored_last(self) -> None: method test_add_new_message_groups_messages_by_hour (line 32) | def test_add_new_message_groups_messages_by_hour(self) -> None: method test_get_channels_populates_message_buckets_for_device_channels (line 51) | def test_get_channels_populates_message_buckets_for_device_channels(se... method test_get_channels_rebuilds_renamed_channels_and_preserves_messages (line 63) | def test_get_channels_rebuilds_renamed_channels_and_preserves_messages... method test_parse_protobuf_returns_string_payload_unchanged (line 85) | def test_parse_protobuf_returns_string_payload_unchanged(self) -> None: method test_parse_protobuf_returns_placeholder_for_text_messages (line 90) | def test_parse_protobuf_returns_placeholder_for_text_messages(self) ->... FILE: tests/test_validation_rules.py class ValidationRulesTests (line 6) | class ValidationRulesTests(unittest.TestCase): method test_get_validation_for_matches_exact_keys (line 7) | def test_get_validation_for_matches_exact_keys(self) -> None: method test_get_validation_for_matches_substrings (line 10) | def test_get_validation_for_matches_substrings(self) -> None: method test_get_validation_for_returns_empty_dict_for_unknown_key (line 13) | def test_get_validation_for_returns_empty_dict_for_unknown_key(self) -...