├── .github └── FUNDING.yml ├── .gitignore ├── .metadata ├── LICENSE ├── README.md ├── all_lint_rules.yaml ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ ├── google-services.json │ ├── proguard-rules.pro │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── meshx │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-hdpi │ │ │ └── splash.png │ │ │ ├── drawable-mdpi │ │ │ └── splash.png │ │ │ ├── drawable-night-v21 │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── drawable-night │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── drawable-v21 │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── drawable-xhdpi │ │ │ └── splash.png │ │ │ ├── drawable-xxhdpi │ │ │ └── splash.png │ │ │ ├── drawable-xxxhdpi │ │ │ └── splash.png │ │ │ ├── drawable │ │ │ ├── background.png │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-night-v31 │ │ │ └── styles.xml │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ ├── values-v31 │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets ├── meshx.png └── meshx.svg ├── firebase.json ├── flutter_native_splash.yaml ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-50x50@1x.png │ │ │ ├── Icon-App-50x50@2x.png │ │ │ ├── Icon-App-57x57@1x.png │ │ │ ├── Icon-App-57x57@2x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-72x72@1x.png │ │ │ ├── Icon-App-72x72@2x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ ├── LaunchBackground.imageset │ │ │ ├── Contents.json │ │ │ ├── background.png │ │ │ └── darkbackground.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── GoogleService-Info.plist │ ├── Info.plist │ ├── Runner-Bridging-Header.h │ └── Runner.entitlements ├── RunnerTests │ └── RunnerTests.swift └── firebase_app_id_file.json ├── lib ├── constants │ ├── app_constants.dart │ ├── ble_constants.dart │ ├── meshtastic_constants.dart │ └── telemetry_intervals.dart ├── exceptions │ └── mesh_radio_exception.dart ├── firebase_options.dart ├── main.dart ├── models │ ├── ble_characteristics.dart │ ├── chat_type.dart │ ├── mesh_channel.dart │ ├── mesh_channel.freezed.dart │ ├── mesh_node.dart │ ├── mesh_node.freezed.dart │ ├── mesh_radio.dart │ ├── radio_configuration.dart │ ├── radio_configuration.freezed.dart │ ├── radio_connection_status.dart │ ├── radio_connector_state.dart │ ├── radio_scan_state.dart │ ├── radio_scan_state.freezed.dart │ ├── telemetry_state.dart │ ├── telemetry_state.freezed.dart │ ├── text_message.dart │ ├── text_message.freezed.dart │ ├── text_message_status.dart │ ├── timed_telemetry.dart │ ├── traceroute_response.dart │ └── traceroute_response.freezed.dart ├── protobufs │ ├── generated │ │ ├── meshtastic │ │ │ ├── admin.pb.dart │ │ │ ├── admin.pbenum.dart │ │ │ ├── admin.pbjson.dart │ │ │ ├── admin.pbserver.dart │ │ │ ├── apponly.pb.dart │ │ │ ├── apponly.pbenum.dart │ │ │ ├── apponly.pbjson.dart │ │ │ ├── apponly.pbserver.dart │ │ │ ├── atak.pb.dart │ │ │ ├── atak.pbenum.dart │ │ │ ├── atak.pbjson.dart │ │ │ ├── atak.pbserver.dart │ │ │ ├── cannedmessages.pb.dart │ │ │ ├── cannedmessages.pbenum.dart │ │ │ ├── cannedmessages.pbjson.dart │ │ │ ├── cannedmessages.pbserver.dart │ │ │ ├── channel.pb.dart │ │ │ ├── channel.pbenum.dart │ │ │ ├── channel.pbjson.dart │ │ │ ├── channel.pbserver.dart │ │ │ ├── clientonly.pb.dart │ │ │ ├── clientonly.pbenum.dart │ │ │ ├── clientonly.pbjson.dart │ │ │ ├── clientonly.pbserver.dart │ │ │ ├── config.pb.dart │ │ │ ├── config.pbenum.dart │ │ │ ├── config.pbjson.dart │ │ │ ├── config.pbserver.dart │ │ │ ├── connection_status.pb.dart │ │ │ ├── connection_status.pbenum.dart │ │ │ ├── connection_status.pbjson.dart │ │ │ ├── connection_status.pbserver.dart │ │ │ ├── deviceonly.pb.dart │ │ │ ├── deviceonly.pbenum.dart │ │ │ ├── deviceonly.pbjson.dart │ │ │ ├── deviceonly.pbserver.dart │ │ │ ├── localonly.pb.dart │ │ │ ├── localonly.pbenum.dart │ │ │ ├── localonly.pbjson.dart │ │ │ ├── localonly.pbserver.dart │ │ │ ├── mesh.pb.dart │ │ │ ├── mesh.pbenum.dart │ │ │ ├── mesh.pbjson.dart │ │ │ ├── mesh.pbserver.dart │ │ │ ├── module_config.pb.dart │ │ │ ├── module_config.pbenum.dart │ │ │ ├── module_config.pbjson.dart │ │ │ ├── module_config.pbserver.dart │ │ │ ├── mqtt.pb.dart │ │ │ ├── mqtt.pbenum.dart │ │ │ ├── mqtt.pbjson.dart │ │ │ ├── mqtt.pbserver.dart │ │ │ ├── paxcount.pb.dart │ │ │ ├── paxcount.pbenum.dart │ │ │ ├── paxcount.pbjson.dart │ │ │ ├── paxcount.pbserver.dart │ │ │ ├── portnums.pb.dart │ │ │ ├── portnums.pbenum.dart │ │ │ ├── portnums.pbjson.dart │ │ │ ├── portnums.pbserver.dart │ │ │ ├── remote_hardware.pb.dart │ │ │ ├── remote_hardware.pbenum.dart │ │ │ ├── remote_hardware.pbjson.dart │ │ │ ├── remote_hardware.pbserver.dart │ │ │ ├── rtttl.pb.dart │ │ │ ├── rtttl.pbenum.dart │ │ │ ├── rtttl.pbjson.dart │ │ │ ├── rtttl.pbserver.dart │ │ │ ├── storeforward.pb.dart │ │ │ ├── storeforward.pbenum.dart │ │ │ ├── storeforward.pbjson.dart │ │ │ ├── storeforward.pbserver.dart │ │ │ ├── telemetry.pb.dart │ │ │ ├── telemetry.pbenum.dart │ │ │ ├── telemetry.pbjson.dart │ │ │ ├── telemetry.pbserver.dart │ │ │ ├── xmodem.pb.dart │ │ │ ├── xmodem.pbenum.dart │ │ │ ├── xmodem.pbjson.dart │ │ │ └── xmodem.pbserver.dart │ │ ├── nanopb.pb.dart │ │ ├── nanopb.pbenum.dart │ │ ├── nanopb.pbjson.dart │ │ └── nanopb.pbserver.dart │ ├── meshtastic │ │ ├── admin.options │ │ ├── admin.proto │ │ ├── apponly.options │ │ ├── apponly.proto │ │ ├── atak.options │ │ ├── atak.proto │ │ ├── cannedmessages.options │ │ ├── cannedmessages.proto │ │ ├── channel.options │ │ ├── channel.proto │ │ ├── clientonly.options │ │ ├── clientonly.proto │ │ ├── config.options │ │ ├── config.proto │ │ ├── connection_status.options │ │ ├── connection_status.proto │ │ ├── deviceonly.options │ │ ├── deviceonly.proto │ │ ├── localonly.proto │ │ ├── mesh.options │ │ ├── mesh.proto │ │ ├── module_config.options │ │ ├── module_config.proto │ │ ├── mqtt.options │ │ ├── mqtt.proto │ │ ├── paxcount.proto │ │ ├── portnums.proto │ │ ├── remote_hardware.proto │ │ ├── rtttl.options │ │ ├── rtttl.proto │ │ ├── storeforward.options │ │ ├── storeforward.proto │ │ ├── telemetry.options │ │ ├── telemetry.proto │ │ ├── xmodem.options │ │ └── xmodem.proto │ └── nanopb.proto ├── providers │ ├── ack_waiting_radio_writer.dart │ ├── ack_waiting_radio_writer.g.dart │ ├── ble │ │ ├── ble_characteristics_finder.dart │ │ ├── ble_characteristics_finder.g.dart │ │ ├── ble_permissions_requester.dart │ │ ├── ble_permissions_requester.g.dart │ │ ├── ble_radio_connector.dart │ │ ├── ble_radio_connector.g.dart │ │ ├── ble_radio_scanner.dart │ │ └── ble_radio_scanner.g.dart │ ├── breadcrumb_logger.dart │ ├── breadcrumb_logger.g.dart │ ├── channel_service.dart │ ├── channel_service.g.dart │ ├── node │ │ ├── hops_away.dart │ │ ├── hops_away.g.dart │ │ ├── node_search.dart │ │ ├── node_search.g.dart │ │ ├── node_service.dart │ │ └── node_service.g.dart │ ├── notifications.dart │ ├── notifications.g.dart │ ├── radio_config │ │ ├── radio_config_downloader_service.dart │ │ ├── radio_config_downloader_service.g.dart │ │ ├── radio_config_service.dart │ │ ├── radio_config_service.g.dart │ │ ├── radio_config_uploader_service.dart │ │ └── radio_config_uploader_service.g.dart │ ├── radio_connector_service.dart │ ├── radio_connector_service.g.dart │ ├── radio_reader.dart │ ├── radio_reader.g.dart │ ├── radio_writer.dart │ ├── radio_writer.g.dart │ ├── reconnector.dart │ ├── reconnector.g.dart │ ├── repository │ │ ├── sqflite.dart │ │ ├── sqflite.g.dart │ │ ├── telemetry_repository.dart │ │ ├── telemetry_repository.g.dart │ │ ├── text_message_repository.dart │ │ └── text_message_repository.g.dart │ ├── router.dart │ ├── router.g.dart │ ├── tcp │ │ ├── tcp_radio_connector.dart │ │ ├── tcp_radio_connector.g.dart │ │ ├── tcp_radio_scanner.dart │ │ └── tcp_radio_scanner.g.dart │ ├── telemetry │ │ ├── telemetry_latest_streamer.dart │ │ ├── telemetry_latest_streamer.g.dart │ │ ├── telemetry_receiver.dart │ │ ├── telemetry_receiver.g.dart │ │ ├── telemetry_saver.dart │ │ ├── telemetry_saver.g.dart │ │ ├── telemetry_streamer.dart │ │ └── telemetry_streamer.g.dart │ ├── text_message │ │ ├── text_message_receiver_service.dart │ │ ├── text_message_receiver_service.g.dart │ │ ├── text_message_sender_service.dart │ │ ├── text_message_sender_service.g.dart │ │ ├── text_message_status_service.dart │ │ ├── text_message_status_service.g.dart │ │ ├── text_message_stream_service.dart │ │ └── text_message_stream_service.g.dart │ ├── traceroute.dart │ ├── traceroute.g.dart │ └── wrap │ │ ├── flutter_blue_plus_mockable.dart │ │ ├── flutter_blue_plus_mockable.g.dart │ │ ├── local_platform.dart │ │ ├── local_platform.g.dart │ │ ├── permissions.dart │ │ ├── permissions.g.dart │ │ ├── socket.dart │ │ └── socket.g.dart ├── repository │ ├── telemetry_repository.dart │ └── text_message_repository.dart ├── screens │ ├── channel_list.dart │ ├── chat.dart │ ├── config │ │ ├── bt_config.dart │ │ ├── channel_qr_scanner.dart │ │ ├── channels_config.dart │ │ ├── lora_config.dart │ │ ├── telemetry_config.dart │ │ └── user_config.dart │ ├── map.dart │ ├── node_info.dart │ ├── nodes.dart │ ├── radio_config.dart │ ├── radio_connection.dart │ ├── tab_parent.dart │ ├── telemetry_log.dart │ └── utils │ │ ├── confirmation_dialog.dart │ │ └── snackbar_message.dart ├── services │ ├── ack_waiting_radio_writer.dart │ ├── ble │ │ ├── ble_characteristics_finder.dart │ │ ├── ble_permissions_requester.dart │ │ ├── ble_radio_reader.dart │ │ └── ble_radio_writer.dart │ ├── breadcrumb_logger.dart │ ├── interfaces │ │ ├── radio_connector.dart │ │ ├── radio_reader.dart │ │ └── radio_writer.dart │ ├── null │ │ ├── null_reader.dart │ │ └── null_writer.dart │ ├── radio_config │ │ ├── radio_config_downloader_service.dart │ │ └── radio_config_uploader_service.dart │ ├── tcp │ │ ├── tcp_radio_reader.dart │ │ └── tcp_radio_writer.dart │ ├── telemetry │ │ └── telemetry_receiver.dart │ ├── text_message │ │ ├── text_message_receiver_service.dart │ │ └── text_message_stream_service.dart │ └── wrap │ │ ├── flutter_blue_plus_mockable.dart │ │ ├── permissions.dart │ │ └── socket_mockable.dart ├── theme.dart ├── utils │ ├── extensions.dart │ └── splay_tree_map_with_default.dart └── widgets │ ├── app_bar_connection_indicator.dart │ ├── channel_card.dart │ ├── channel_input_form.dart │ ├── channel_qr_show.dart │ ├── connected_radio.dart │ ├── connection_icon.dart │ ├── manual_network_address_input.dart │ ├── message_bubble.dart │ ├── message_input.dart │ ├── message_list.dart │ ├── node_card.dart │ ├── radio_choice_tile.dart │ ├── scroll_button.dart │ ├── text_message_status_indicator.dart │ └── traceroute_modal.dart ├── macos ├── .gitignore ├── Flutter │ ├── Flutter-Debug.xcconfig │ ├── Flutter-Release.xcconfig │ └── GeneratedPluginRegistrant.swift ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── app_icon_1024.png │ │ │ ├── app_icon_128.png │ │ │ ├── app_icon_16.png │ │ │ ├── app_icon_256.png │ │ │ ├── app_icon_32.png │ │ │ ├── app_icon_512.png │ │ │ └── app_icon_64.png │ ├── Base.lproj │ │ └── MainMenu.xib │ ├── Configs │ │ ├── AppInfo.xcconfig │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── Warnings.xcconfig │ ├── DebugProfile.entitlements │ ├── GoogleService-Info.plist │ ├── Info.plist │ ├── MainFlutterWindow.swift │ └── Release.entitlements ├── RunnerTests │ └── RunnerTests.swift └── firebase_app_id_file.json ├── pubspec.lock ├── pubspec.yaml ├── sonar-project.properties ├── test ├── common.dart ├── mock_stream.dart ├── models │ └── chat_type.dart ├── providers │ ├── ble │ │ ├── ble_radio_connector_test.dart │ │ ├── ble_radio_connector_test.mocks.dart │ │ ├── ble_radio_scanner_test.dart │ │ └── ble_radio_scanner_test.mocks.dart │ ├── channel_service_test.dart │ ├── channel_service_test.mocks.dart │ ├── node │ │ ├── node_search_test.dart │ │ ├── node_service_test.dart │ │ └── node_service_test.mocks.dart │ ├── radio_config │ │ ├── radio_config_service_test.dart │ │ └── radio_config_service_test.mocks.dart │ ├── radio_connector_service_test.dart │ ├── radio_connector_service_test.mocks.dart │ ├── radio_reader_test.dart │ ├── radio_reader_test.mocks.dart │ ├── radio_writer_test.dart │ ├── radio_writer_test.mocks.dart │ ├── reconnector_test.dart │ ├── reconnector_test.mocks.dart │ ├── tcp │ │ ├── tcp_radio_connector_test.dart │ │ └── tcp_radio_connector_test.mocks.dart │ └── text_message │ │ ├── text_message_sender_service_test.dart │ │ ├── text_message_sender_service_test.mocks.dart │ │ ├── text_message_status_service_test.dart │ │ └── text_message_status_service_test.mocks.dart └── services │ ├── ble │ ├── ble_characteristics_finder_test.dart │ ├── ble_characteristics_finder_test.mocks.dart │ ├── ble_permissions_requester_test.dart │ ├── ble_permissions_requester_test.mocks.dart │ ├── ble_radio_reader_test.dart │ ├── ble_radio_reader_test.mocks.dart │ ├── ble_radio_writer_test.dart │ └── ble_radio_writer_test.mocks.dart │ ├── queued_radio_writer_test.dart │ ├── queued_radio_writer_test.mocks.dart │ ├── radio_config │ ├── radio_config_downloader_service_test.dart │ ├── radio_config_downloader_service_test.mocks.dart │ ├── radio_config_uploader_service_test.dart │ └── radio_config_uploader_service_test.mocks.dart │ ├── tcp │ ├── tcp_radio_reader_test.dart │ ├── tcp_radio_reader_test.mocks.dart │ ├── tcp_radio_writer_test.dart │ └── tcp_radio_writer_test.mocks.dart │ └── text_message │ ├── text_message_receiver_service_test.dart │ ├── text_message_receiver_service_test.mocks.dart │ ├── text_message_stream_service_test.dart │ └── text_message_stream_service_test.mocks.dart └── windows ├── .gitignore ├── CMakeLists.txt ├── flutter ├── CMakeLists.txt ├── generated_plugin_registrant.cc ├── generated_plugin_registrant.h └── generated_plugins.cmake └── runner ├── CMakeLists.txt ├── Runner.rc ├── flutter_window.cpp ├── flutter_window.h ├── main.cpp ├── resource.h ├── resources └── app_icon.ico ├── runner.exe.manifest ├── utils.cpp ├── utils.h ├── win32_window.cpp └── win32_window.h /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: meshtastic 4 | patreon: 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: 10 | issuehunt: # Replace with a single IssueHunt username 11 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 12 | polar: # Replace with a single Polar username 13 | buy_me_a_coffee: 14 | custom: 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | **/doc/api/ 26 | **/ios/Flutter/.last_build_id 27 | .dart_tool/ 28 | .flutter-plugins 29 | .flutter-plugins-dependencies 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Symbolication related 35 | app.*.symbols 36 | 37 | # Obfuscation related 38 | app.*.map.json 39 | 40 | # Android Studio will place build artifacts here 41 | /android/app/debug 42 | /android/app/profile 43 | /android/app/release 44 | 45 | # Sonar 46 | .scannerwork/ 47 | tests.output 48 | 49 | coverage/lcov.info 50 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "54e66469a933b60ddf175f858f82eaeb97e48c8d" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d 17 | base_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d 18 | - platform: windows 19 | create_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d 20 | base_revision: 54e66469a933b60ddf175f858f82eaeb97e48c8d 21 | 22 | # User provided section 23 | 24 | # List of Local paths (relative to this file) that should be 25 | # ignored by the migrate tool. 26 | # 27 | # Files that are not part of the templates will be ignored by default. 28 | unmanaged_files: 29 | - 'lib/main.dart' 30 | - 'ios/Runner.xcodeproj/project.pbxproj' 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # multimesh 2 | 3 | A cross-platform Meshtastic® client based on Flutter 4 | 5 | ## Screenshots 6 |

7 | 8 | 9 | 10 | 11 | 12 | 13 |

14 | 15 | ## Nightly builds 16 | [![Nightly builds](https://app.bitrise.io/app/39797fbe-e2bc-4938-9ef2-6552671f060c/status.svg?token=LySOjGHyY7cfnRRCLOtSpw&branch=main)](https://app.bitrise.io/app/39797fbe-e2bc-4938-9ef2-6552671f060c?commit_message=Nightly) 17 | 18 | ## Legal 19 | Meshtastic® is a registered trademark of Meshtastic LLC. Meshtastic software components are released under various licenses, see GitHub for details. No warranty is provided - use at your own risk. 20 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "194789291232", 4 | "project_id": "multimesh-9b515", 5 | "storage_bucket": "multimesh-9b515.appspot.com" 6 | }, 7 | "client": [ 8 | { 9 | "client_info": { 10 | "mobilesdk_app_id": "1:194789291232:android:401ede243a4a4fda7c8c85", 11 | "android_client_info": { 12 | "package_name": "xyz.paulocode.meshx" 13 | } 14 | }, 15 | "oauth_client": [], 16 | "api_key": [ 17 | { 18 | "current_key": "AIzaSyAEfgkOUPw7pNrfchQWmjp_xFhtiZFatyI" 19 | } 20 | ], 21 | "services": { 22 | "appinvite_service": { 23 | "other_platform_oauth_client": [] 24 | } 25 | } 26 | } 27 | ], 28 | "configuration_version": "1" 29 | } -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/meshx/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package xyz.paulocode.meshx 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity : FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/android/app/src/main/res/drawable-hdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/android/app/src/main/res/drawable-mdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-v21/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/android/app/src/main/res/drawable-night-v21/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/android/app/src/main/res/drawable-night/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-night/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/android/app/src/main/res/drawable-v21/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/android/app/src/main/res/drawable-xhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/android/app/src/main/res/drawable-xxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/android/app/src/main/res/drawable-xxxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/android/app/src/main/res/drawable/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night-v31/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 19 | 22 | 23 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-v31/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | 19 | 22 | 23 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.8.0' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | // START: FlutterFire Configuration 10 | classpath 'com.google.gms:google-services:4.3.15' 11 | classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1' 12 | // END: FlutterFire Configuration 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | google() 20 | mavenCentral() 21 | } 22 | } 23 | 24 | rootProject.buildDir = '../build' 25 | subprojects { 26 | project.buildDir = "${rootProject.buildDir}/${project.name}" 27 | } 28 | subprojects { 29 | project.evaluationDependsOn(':app') 30 | } 31 | 32 | tasks.register("clean", Delete) { 33 | delete rootProject.buildDir 34 | } 35 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.4-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | } 9 | settings.ext.flutterSdkPath = flutterSdkPath() 10 | 11 | includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") 12 | 13 | repositories { 14 | google() 15 | mavenCentral() 16 | gradlePluginPortal() 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false 21 | } 22 | } 23 | 24 | plugins { 25 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 26 | id "com.android.application" version "7.3.0" apply false 27 | } 28 | 29 | include ":app" 30 | -------------------------------------------------------------------------------- /assets/meshx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/assets/meshx.png -------------------------------------------------------------------------------- /assets/meshx.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 10 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | {"flutter":{"platforms":{"android":{"default":{"projectId":"multimesh-9b515","appId":"1:194789291232:android:401ede243a4a4fda7c8c85","fileOutput":"android/app/google-services.json"}},"ios":{"default":{"projectId":"multimesh-9b515","appId":"1:194789291232:ios:b601f3edbb6827e17c8c85","uploadDebugSymbols":true,"fileOutput":"ios/Runner/GoogleService-Info.plist"}},"macos":{"default":{"projectId":"multimesh-9b515","appId":"1:194789291232:ios:b601f3edbb6827e17c8c85","uploadDebugSymbols":true,"fileOutput":"macos/Runner/GoogleService-Info.plist"}},"dart":{"lib/firebase_options.dart":{"projectId":"multimesh-9b515","configurations":{"android":"1:194789291232:android:401ede243a4a4fda7c8c85","ios":"1:194789291232:ios:b601f3edbb6827e17c8c85","macos":"1:194789291232:ios:b601f3edbb6827e17c8c85"}}}}}} -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '12.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | target 'RunnerTests' do 36 | inherit! :search_paths 37 | end 38 | end 39 | 40 | post_install do |installer| 41 | installer.pods_project.targets.each do |target| 42 | flutter_additional_ios_build_settings(target) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | SwiftFlutterForegroundTaskPlugin.setPluginRegistrantCallback(registerPlugins) 12 | if #available(iOS 10.0, *) { 13 | UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate 14 | } 15 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 16 | } 17 | } 18 | 19 | func registerPlugins(registry: FlutterPluginRegistry) { 20 | GeneratedPluginRegistrant.register(with: registry) 21 | } 22 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "background.png", 5 | "idiom": "universal" 6 | }, 7 | { 8 | "appearances": [ 9 | { 10 | "appearance": "luminosity", 11 | "value": "dark" 12 | } 13 | ], 14 | "filename": "darkbackground.png", 15 | "idiom": "universal" 16 | } 17 | ], 18 | "info": { 19 | "author": "xcode", 20 | "version": 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "LaunchImage.png", 5 | "idiom": "universal", 6 | "scale": "1x" 7 | }, 8 | { 9 | "filename": "LaunchImage@2x.png", 10 | "idiom": "universal", 11 | "scale": "2x" 12 | }, 13 | { 14 | "filename": "LaunchImage@3x.png", 15 | "idiom": "universal", 16 | "scale": "3x" 17 | } 18 | ], 19 | "info": { 20 | "author": "xcode", 21 | "version": 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in 4 | this directory. 5 | 6 | You can also do it by opening your Flutter project's Xcode project 7 | with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and 8 | dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /ios/Runner/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | API_KEY 6 | AIzaSyCm2mkgrd8WFEj81l8ix16I5QUCkqUap_U 7 | GCM_SENDER_ID 8 | 194789291232 9 | PLIST_VERSION 10 | 1 11 | BUNDLE_ID 12 | xyz.paulocode.meshx 13 | PROJECT_ID 14 | multimesh-9b515 15 | STORAGE_BUCKET 16 | multimesh-9b515.appspot.com 17 | IS_ADS_ENABLED 18 | 19 | IS_ANALYTICS_ENABLED 20 | 21 | IS_APPINVITE_ENABLED 22 | 23 | IS_GCM_ENABLED 24 | 25 | IS_SIGNIN_ENABLED 26 | 27 | GOOGLE_APP_ID 28 | 1:194789291232:ios:b601f3edbb6827e17c8c85 29 | 30 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | #import 3 | -------------------------------------------------------------------------------- /ios/Runner/Runner.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | aps-environment 6 | development 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /ios/firebase_app_id_file.json: -------------------------------------------------------------------------------- 1 | { 2 | "file_generated_by": "FlutterFire CLI", 3 | "purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory", 4 | "GOOGLE_APP_ID": "1:194789291232:ios:b601f3edbb6827e17c8c85", 5 | "FIREBASE_PROJECT_ID": "multimesh-9b515", 6 | "GCM_SENDER_ID": "194789291232" 7 | } -------------------------------------------------------------------------------- /lib/constants/app_constants.dart: -------------------------------------------------------------------------------- 1 | import 'package:platform/platform.dart'; 2 | 3 | const BATCH_NUM_MESSAGES_TO_LOAD = 100; 4 | const _platform = LocalPlatform(); 5 | // ignore: non_constant_identifier_names 6 | final CAN_UPLOAD_LOGS = !_platform.isWindows && !_platform.isLinux; 7 | -------------------------------------------------------------------------------- /lib/constants/ble_constants.dart: -------------------------------------------------------------------------------- 1 | const MESHTASTIC_BLE_SERVICE = '6ba1b218-15a8-461f-9fa8-5dcae273eafd'; 2 | const MESHTASTIC_TORADIO_CHARACTERISTIC = 3 | 'f75c76d2-129e-4dad-a1dd-7866124401e7'; 4 | const MESHTASTIC_FROMRADIO_CHARACTERISTIC = 5 | '2c55e69e-4993-11ed-b878-0242ac120002'; 6 | const MESHTASTIC_FROMNUM_CHARACTERISTIC = 7 | 'ed9da18c-a800-4f66-a670-aa7547e34453'; 8 | const TO_BROADCAST = 4294967295; 9 | -------------------------------------------------------------------------------- /lib/constants/meshtastic_constants.dart: -------------------------------------------------------------------------------- 1 | const MESHTASTIC_MAX_CHANNELS = 8; 2 | const MESHTASTIC_TCP_PORT = 4403; 3 | const MESHTASTIC_STREAM_START1 = 0x94; 4 | const MESHTASTIC_STREAM_START2 = 0xC3; 5 | const MESHTASTIC_MAX_PACKET_LEN = 512; 6 | const MESHTASTIC_MAX_HOPS = 7; 7 | const MESHTASTIC_PREFIX_URL = 'https://meshtastic.org/e/#'; 8 | -------------------------------------------------------------------------------- /lib/constants/telemetry_intervals.dart: -------------------------------------------------------------------------------- 1 | enum TelemetryIntervals { 2 | t15m, 3 | t30m, 4 | t1h, 5 | t2h, 6 | t3h, 7 | t6h, 8 | t12h, 9 | t18h, 10 | t24h, 11 | t36h, 12 | t48h, 13 | t72h, 14 | } 15 | 16 | extension IntervalNames on TelemetryIntervals { 17 | String get displayName { 18 | return name 19 | .replaceAll('t', '') 20 | .replaceAll('m', ' minutes') 21 | .replaceAll('h', ' hours'); 22 | } 23 | 24 | int get seconds { 25 | if (name.contains('m')) { 26 | final minutes = name.replaceAll('t', '').replaceAll('m', ''); 27 | return int.parse(minutes) * 60; 28 | } else if (name.contains('h')) { 29 | final minutes = name.replaceAll('t', '').replaceAll('h', ''); 30 | return int.parse(minutes) * 60 * 60; 31 | } else { 32 | return 15 * 60; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/exceptions/mesh_radio_exception.dart: -------------------------------------------------------------------------------- 1 | class MeshRadioException implements Exception { 2 | const MeshRadioException({String? msg}) : msg = msg ?? 'Unknown error'; 3 | 4 | final String msg; 5 | 6 | @override 7 | String toString() => 'MeshRadioException: $msg'; 8 | } 9 | -------------------------------------------------------------------------------- /lib/models/ble_characteristics.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_blue_plus/flutter_blue_plus.dart'; 2 | 3 | class BleCharacteristics { 4 | BleCharacteristics({ 5 | required this.toRadio, 6 | required this.fromRadio, 7 | required this.fromNum, 8 | }); 9 | 10 | final BluetoothCharacteristic toRadio; 11 | final BluetoothCharacteristic fromRadio; 12 | final BluetoothCharacteristic fromNum; 13 | } 14 | -------------------------------------------------------------------------------- /lib/models/chat_type.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../constants/ble_constants.dart'; 4 | 5 | @immutable 6 | sealed class ChatType { 7 | const ChatType({required this.toNode, required this.channel}); 8 | final int toNode; 9 | final int channel; 10 | 11 | @override 12 | bool operator ==(Object other) => 13 | identical(this, other) || 14 | other is ChatType && 15 | runtimeType == other.runtimeType && 16 | toNode == other.toNode && 17 | channel == other.channel; 18 | 19 | @override 20 | int get hashCode => toNode.hashCode ^ channel.hashCode; 21 | } 22 | 23 | @immutable 24 | class DirectMessageChat extends ChatType { 25 | const DirectMessageChat({required int dmNode}) 26 | : super(toNode: dmNode, channel: 0); 27 | } 28 | 29 | @immutable 30 | class ChannelChat extends ChatType { 31 | const ChannelChat({super.toNode = TO_BROADCAST, required super.channel}); 32 | } 33 | -------------------------------------------------------------------------------- /lib/models/mesh_channel.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | import '../protobufs/generated/meshtastic/channel.pb.dart'; 4 | part 'mesh_channel.freezed.dart'; 5 | 6 | @freezed 7 | class MeshChannel with _$MeshChannel { 8 | const factory MeshChannel({ 9 | required String name, 10 | @Default(false) bool hasBlankActualName, 11 | required Channel_Role role, 12 | required List key, 13 | required int index, 14 | required bool uplinkEnabled, 15 | required bool downlinkEnabled, 16 | }) = _MeshChannel; 17 | 18 | const MeshChannel._(); 19 | 20 | bool get used => role != Channel_Role.DISABLED; 21 | } 22 | -------------------------------------------------------------------------------- /lib/models/mesh_node.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | part 'mesh_node.freezed.dart'; 3 | 4 | @freezed 5 | class MeshNode with _$MeshNode { 6 | const factory MeshNode({ 7 | required int nodeNum, 8 | required String longName, 9 | String? hwModel, 10 | required String shortName, 11 | required int channel, 12 | required String id, 13 | int? batteryLevel, 14 | required DateTime lastSeen, 15 | @Default(false) bool hasUnreadMessages, 16 | }) = _MeshNode; 17 | 18 | const MeshNode._(); 19 | } 20 | -------------------------------------------------------------------------------- /lib/models/mesh_radio.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_blue_plus/flutter_blue_plus.dart'; 2 | 3 | sealed class MeshRadio { 4 | String get name; 5 | String get remoteId; 6 | } 7 | 8 | class BleMeshRadio extends MeshRadio { 9 | BleMeshRadio({required this.device}) : super(); 10 | 11 | final BluetoothDevice device; 12 | 13 | @override 14 | String get name => device.advName; 15 | 16 | @override 17 | String get remoteId => device.remoteId.str; 18 | } 19 | 20 | class TcpMeshRadio extends MeshRadio { 21 | TcpMeshRadio({required this.address}) : super(); 22 | 23 | final String address; 24 | 25 | @override 26 | String get name => address; 27 | 28 | @override 29 | String get remoteId => address; 30 | } 31 | -------------------------------------------------------------------------------- /lib/models/radio_configuration.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | import '../protobufs/generated/meshtastic/config.pb.dart'; 4 | import '../protobufs/generated/meshtastic/mesh.pb.dart'; 5 | import '../protobufs/generated/meshtastic/mesh.pbserver.dart'; 6 | import '../protobufs/generated/meshtastic/module_config.pb.dart'; 7 | 8 | part 'radio_configuration.freezed.dart'; 9 | 10 | @freezed 11 | class RadioConfiguration with _$RadioConfiguration { 12 | const factory RadioConfiguration({ 13 | @Default(0) int myNodeNum, 14 | required Config_LoRaConfig loraConfig, 15 | required Config_BluetoothConfig bluetoothConfig, 16 | required ModuleConfig_TelemetryConfig telemetryConfig, 17 | required NodeInfo myNodeInfo, 18 | @Default(false) bool hasOwnNodeInfo, 19 | @Default(false) bool configDownloaded, 20 | }) = _RadioConfiguration; 21 | 22 | const RadioConfiguration._(); 23 | } 24 | -------------------------------------------------------------------------------- /lib/models/radio_connection_status.dart: -------------------------------------------------------------------------------- 1 | enum RadioConnectionStatus { disconnected, connecting, connected, error } 2 | -------------------------------------------------------------------------------- /lib/models/radio_scan_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | import 'mesh_radio.dart'; 4 | 5 | part 'radio_scan_state.freezed.dart'; 6 | 7 | @freezed 8 | class RadioScanState with _$RadioScanState { 9 | const factory RadioScanState({ 10 | @Default(false) bool scanning, 11 | @Default([]) List meshRadios, 12 | }) = _RadioScanState; 13 | 14 | const RadioScanState._(); 15 | } 16 | -------------------------------------------------------------------------------- /lib/models/telemetry_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | part 'telemetry_state.freezed.dart'; 3 | 4 | @freezed 5 | class TelemetryState with _$TelemetryState { 6 | const factory TelemetryState({ 7 | double? temp, 8 | double? relativeHumidity, 9 | double? barometricPressure, 10 | double? gasResistance, 11 | }) = _TelemetryState; 12 | 13 | const TelemetryState._(); 14 | } 15 | -------------------------------------------------------------------------------- /lib/models/text_message.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | import '../protobufs/generated/meshtastic/mesh.pb.dart'; 4 | import 'text_message_status.dart'; 5 | 6 | part 'text_message.freezed.dart'; 7 | 8 | @freezed 9 | class TextMessage with _$TextMessage { 10 | const factory TextMessage({ 11 | @Default(0) int packetId, 12 | required String text, 13 | required int from, 14 | required int to, 15 | required int channel, 16 | required DateTime time, 17 | @Default(TextMessageStatus.SENDING) TextMessageStatus state, 18 | required int owner, 19 | @Default(Routing_Error.NONE) Routing_Error routingError, 20 | }) = _TextMessage; 21 | 22 | const TextMessage._(); 23 | } 24 | -------------------------------------------------------------------------------- /lib/models/text_message_status.dart: -------------------------------------------------------------------------------- 1 | enum TextMessageStatus { 2 | SENDING, 3 | RADIO_ERROR, 4 | // do not delete since it 5 | // deleting will change 6 | // existing DB mapping 7 | @Deprecated('Use RADIO_ERROR instead') 8 | MAX_RETRANSMIT, 9 | OK, 10 | RECVD_BY_RADIO, 11 | IMPLICIT_ACK, // TODO 12 | } 13 | -------------------------------------------------------------------------------- /lib/models/timed_telemetry.dart: -------------------------------------------------------------------------------- 1 | import '../protobufs/generated/meshtastic/telemetry.pb.dart'; 2 | 3 | class TimedTelemetry { 4 | TimedTelemetry({required this.timeReceived, required this.telemetry}); 5 | 6 | final DateTime timeReceived; 7 | final Telemetry telemetry; 8 | } 9 | -------------------------------------------------------------------------------- /lib/models/traceroute_response.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | part 'traceroute_response.freezed.dart'; 3 | 4 | @freezed 5 | class TracerouteResponse with _$TracerouteResponse { 6 | const factory TracerouteResponse({ 7 | List? route, 8 | DateTime? successTime, 9 | required DateTime attemptTime, 10 | }) = _TracerouteResponse; 11 | 12 | const TracerouteResponse._(); 13 | } 14 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/admin.pbserver.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/admin.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names 9 | // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes 10 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 11 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 12 | 13 | export 'admin.pb.dart'; 14 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/apponly.pbenum.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/apponly.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names, library_prefixes 9 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 10 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 11 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/apponly.pbjson.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/apponly.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names, library_prefixes 9 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 10 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 11 | 12 | import 'dart:convert' as $convert; 13 | import 'dart:core' as $core; 14 | import 'dart:typed_data' as $typed_data; 15 | 16 | @$core.Deprecated('Use channelSetDescriptor instead') 17 | const ChannelSet$json = { 18 | '1': 'ChannelSet', 19 | '2': [ 20 | { 21 | '1': 'settings', 22 | '3': 1, 23 | '4': 3, 24 | '5': 11, 25 | '6': '.meshtastic.ChannelSettings', 26 | '10': 'settings' 27 | }, 28 | { 29 | '1': 'lora_config', 30 | '3': 2, 31 | '4': 1, 32 | '5': 11, 33 | '6': '.meshtastic.Config.LoRaConfig', 34 | '10': 'loraConfig' 35 | }, 36 | ], 37 | }; 38 | 39 | /// Descriptor for `ChannelSet`. Decode as a `google.protobuf.DescriptorProto`. 40 | final $typed_data.Uint8List channelSetDescriptor = $convert.base64Decode( 41 | 'CgpDaGFubmVsU2V0EjcKCHNldHRpbmdzGAEgAygLMhsubWVzaHRhc3RpYy5DaGFubmVsU2V0dG' 42 | 'luZ3NSCHNldHRpbmdzEj4KC2xvcmFfY29uZmlnGAIgASgLMh0ubWVzaHRhc3RpYy5Db25maWcu' 43 | 'TG9SYUNvbmZpZ1IKbG9yYUNvbmZpZw=='); 44 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/apponly.pbserver.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/apponly.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names 9 | // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes 10 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 11 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 12 | 13 | export 'apponly.pb.dart'; 14 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/atak.pbserver.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/atak.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names 9 | // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes 10 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 11 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 12 | 13 | export 'atak.pb.dart'; 14 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/cannedmessages.pbenum.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/cannedmessages.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names, library_prefixes 9 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 10 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 11 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/cannedmessages.pbjson.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/cannedmessages.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names, library_prefixes 9 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 10 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 11 | 12 | import 'dart:convert' as $convert; 13 | import 'dart:core' as $core; 14 | import 'dart:typed_data' as $typed_data; 15 | 16 | @$core.Deprecated('Use cannedMessageModuleConfigDescriptor instead') 17 | const CannedMessageModuleConfig$json = { 18 | '1': 'CannedMessageModuleConfig', 19 | '2': [ 20 | {'1': 'messages', '3': 1, '4': 1, '5': 9, '10': 'messages'}, 21 | ], 22 | }; 23 | 24 | /// Descriptor for `CannedMessageModuleConfig`. Decode as a `google.protobuf.DescriptorProto`. 25 | final $typed_data.Uint8List cannedMessageModuleConfigDescriptor = 26 | $convert.base64Decode( 27 | 'ChlDYW5uZWRNZXNzYWdlTW9kdWxlQ29uZmlnEhoKCG1lc3NhZ2VzGAEgASgJUghtZXNzYWdlcw' 28 | '=='); 29 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/cannedmessages.pbserver.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/cannedmessages.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names 9 | // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes 10 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 11 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 12 | 13 | export 'cannedmessages.pb.dart'; 14 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/channel.pbserver.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/channel.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names 9 | // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes 10 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 11 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 12 | 13 | export 'channel.pb.dart'; 14 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/clientonly.pbenum.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/clientonly.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names, library_prefixes 9 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 10 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 11 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/clientonly.pbserver.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/clientonly.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names 9 | // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes 10 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 11 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 12 | 13 | export 'clientonly.pb.dart'; 14 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/config.pbserver.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/config.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names 9 | // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes 10 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 11 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 12 | 13 | export 'config.pb.dart'; 14 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/connection_status.pbenum.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/connection_status.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names, library_prefixes 9 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 10 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 11 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/connection_status.pbserver.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/connection_status.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names 9 | // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes 10 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 11 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 12 | 13 | export 'connection_status.pb.dart'; 14 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/deviceonly.pbenum.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/deviceonly.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names, library_prefixes 9 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 10 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 11 | 12 | import 'dart:core' as $core; 13 | 14 | import 'package:protobuf/protobuf.dart' as $pb; 15 | 16 | /// 17 | /// Font sizes for the device screen 18 | class ScreenFonts extends $pb.ProtobufEnum { 19 | static const ScreenFonts FONT_SMALL = 20 | ScreenFonts._(0, _omitEnumNames ? '' : 'FONT_SMALL'); 21 | static const ScreenFonts FONT_MEDIUM = 22 | ScreenFonts._(1, _omitEnumNames ? '' : 'FONT_MEDIUM'); 23 | static const ScreenFonts FONT_LARGE = 24 | ScreenFonts._(2, _omitEnumNames ? '' : 'FONT_LARGE'); 25 | 26 | static const $core.List values = [ 27 | FONT_SMALL, 28 | FONT_MEDIUM, 29 | FONT_LARGE, 30 | ]; 31 | 32 | static final $core.Map<$core.int, ScreenFonts> _byValue = 33 | $pb.ProtobufEnum.initByValue(values); 34 | static ScreenFonts? valueOf($core.int value) => _byValue[value]; 35 | 36 | const ScreenFonts._($core.int v, $core.String n) : super(v, n); 37 | } 38 | 39 | const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names'); 40 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/deviceonly.pbserver.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/deviceonly.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names 9 | // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes 10 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 11 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 12 | 13 | export 'deviceonly.pb.dart'; 14 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/localonly.pbenum.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/localonly.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names, library_prefixes 9 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 10 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 11 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/localonly.pbserver.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/localonly.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names 9 | // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes 10 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 11 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 12 | 13 | export 'localonly.pb.dart'; 14 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/mesh.pbserver.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/mesh.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names 9 | // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes 10 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 11 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 12 | 13 | export 'mesh.pb.dart'; 14 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/module_config.pbserver.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/module_config.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names 9 | // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes 10 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 11 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 12 | 13 | export 'module_config.pb.dart'; 14 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/mqtt.pbenum.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/mqtt.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names, library_prefixes 9 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 10 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 11 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/mqtt.pbserver.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/mqtt.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names 9 | // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes 10 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 11 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 12 | 13 | export 'mqtt.pb.dart'; 14 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/paxcount.pbenum.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/paxcount.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names, library_prefixes 9 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 10 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 11 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/paxcount.pbjson.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/paxcount.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names, library_prefixes 9 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 10 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 11 | 12 | import 'dart:convert' as $convert; 13 | import 'dart:core' as $core; 14 | import 'dart:typed_data' as $typed_data; 15 | 16 | @$core.Deprecated('Use paxcountDescriptor instead') 17 | const Paxcount$json = { 18 | '1': 'Paxcount', 19 | '2': [ 20 | {'1': 'wifi', '3': 1, '4': 1, '5': 13, '10': 'wifi'}, 21 | {'1': 'ble', '3': 2, '4': 1, '5': 13, '10': 'ble'}, 22 | {'1': 'uptime', '3': 3, '4': 1, '5': 13, '10': 'uptime'}, 23 | ], 24 | }; 25 | 26 | /// Descriptor for `Paxcount`. Decode as a `google.protobuf.DescriptorProto`. 27 | final $typed_data.Uint8List paxcountDescriptor = $convert.base64Decode( 28 | 'CghQYXhjb3VudBISCgR3aWZpGAEgASgNUgR3aWZpEhAKA2JsZRgCIAEoDVIDYmxlEhYKBnVwdG' 29 | 'ltZRgDIAEoDVIGdXB0aW1l'); 30 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/paxcount.pbserver.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/paxcount.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names 9 | // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes 10 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 11 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 12 | 13 | export 'paxcount.pb.dart'; 14 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/portnums.pb.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/portnums.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names, library_prefixes 9 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 10 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 11 | 12 | import 'dart:core' as $core; 13 | 14 | export 'portnums.pbenum.dart'; 15 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/portnums.pbserver.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/portnums.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names 9 | // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes 10 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 11 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 12 | 13 | export 'portnums.pb.dart'; 14 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/remote_hardware.pbenum.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/remote_hardware.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names, library_prefixes 9 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 10 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 11 | 12 | import 'dart:core' as $core; 13 | 14 | import 'package:protobuf/protobuf.dart' as $pb; 15 | 16 | /// 17 | /// TODO: REPLACE 18 | class HardwareMessage_Type extends $pb.ProtobufEnum { 19 | static const HardwareMessage_Type UNSET = 20 | HardwareMessage_Type._(0, _omitEnumNames ? '' : 'UNSET'); 21 | static const HardwareMessage_Type WRITE_GPIOS = 22 | HardwareMessage_Type._(1, _omitEnumNames ? '' : 'WRITE_GPIOS'); 23 | static const HardwareMessage_Type WATCH_GPIOS = 24 | HardwareMessage_Type._(2, _omitEnumNames ? '' : 'WATCH_GPIOS'); 25 | static const HardwareMessage_Type GPIOS_CHANGED = 26 | HardwareMessage_Type._(3, _omitEnumNames ? '' : 'GPIOS_CHANGED'); 27 | static const HardwareMessage_Type READ_GPIOS = 28 | HardwareMessage_Type._(4, _omitEnumNames ? '' : 'READ_GPIOS'); 29 | static const HardwareMessage_Type READ_GPIOS_REPLY = 30 | HardwareMessage_Type._(5, _omitEnumNames ? '' : 'READ_GPIOS_REPLY'); 31 | 32 | static const $core.List values = [ 33 | UNSET, 34 | WRITE_GPIOS, 35 | WATCH_GPIOS, 36 | GPIOS_CHANGED, 37 | READ_GPIOS, 38 | READ_GPIOS_REPLY, 39 | ]; 40 | 41 | static final $core.Map<$core.int, HardwareMessage_Type> _byValue = 42 | $pb.ProtobufEnum.initByValue(values); 43 | static HardwareMessage_Type? valueOf($core.int value) => _byValue[value]; 44 | 45 | const HardwareMessage_Type._($core.int v, $core.String n) : super(v, n); 46 | } 47 | 48 | const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names'); 49 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/remote_hardware.pbjson.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/remote_hardware.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names, library_prefixes 9 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 10 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 11 | 12 | import 'dart:convert' as $convert; 13 | import 'dart:core' as $core; 14 | import 'dart:typed_data' as $typed_data; 15 | 16 | @$core.Deprecated('Use hardwareMessageDescriptor instead') 17 | const HardwareMessage$json = { 18 | '1': 'HardwareMessage', 19 | '2': [ 20 | { 21 | '1': 'type', 22 | '3': 1, 23 | '4': 1, 24 | '5': 14, 25 | '6': '.meshtastic.HardwareMessage.Type', 26 | '10': 'type' 27 | }, 28 | {'1': 'gpio_mask', '3': 2, '4': 1, '5': 4, '10': 'gpioMask'}, 29 | {'1': 'gpio_value', '3': 3, '4': 1, '5': 4, '10': 'gpioValue'}, 30 | ], 31 | '4': [HardwareMessage_Type$json], 32 | }; 33 | 34 | @$core.Deprecated('Use hardwareMessageDescriptor instead') 35 | const HardwareMessage_Type$json = { 36 | '1': 'Type', 37 | '2': [ 38 | {'1': 'UNSET', '2': 0}, 39 | {'1': 'WRITE_GPIOS', '2': 1}, 40 | {'1': 'WATCH_GPIOS', '2': 2}, 41 | {'1': 'GPIOS_CHANGED', '2': 3}, 42 | {'1': 'READ_GPIOS', '2': 4}, 43 | {'1': 'READ_GPIOS_REPLY', '2': 5}, 44 | ], 45 | }; 46 | 47 | /// Descriptor for `HardwareMessage`. Decode as a `google.protobuf.DescriptorProto`. 48 | final $typed_data.Uint8List hardwareMessageDescriptor = $convert.base64Decode( 49 | 'Cg9IYXJkd2FyZU1lc3NhZ2USNAoEdHlwZRgBIAEoDjIgLm1lc2h0YXN0aWMuSGFyZHdhcmVNZX' 50 | 'NzYWdlLlR5cGVSBHR5cGUSGwoJZ3Bpb19tYXNrGAIgASgEUghncGlvTWFzaxIdCgpncGlvX3Zh' 51 | 'bHVlGAMgASgEUglncGlvVmFsdWUibAoEVHlwZRIJCgVVTlNFVBAAEg8KC1dSSVRFX0dQSU9TEA' 52 | 'ESDwoLV0FUQ0hfR1BJT1MQAhIRCg1HUElPU19DSEFOR0VEEAMSDgoKUkVBRF9HUElPUxAEEhQK' 53 | 'EFJFQURfR1BJT1NfUkVQTFkQBQ=='); 54 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/remote_hardware.pbserver.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/remote_hardware.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names 9 | // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes 10 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 11 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 12 | 13 | export 'remote_hardware.pb.dart'; 14 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/rtttl.pbenum.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/rtttl.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names, library_prefixes 9 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 10 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 11 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/rtttl.pbjson.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/rtttl.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names, library_prefixes 9 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 10 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 11 | 12 | import 'dart:convert' as $convert; 13 | import 'dart:core' as $core; 14 | import 'dart:typed_data' as $typed_data; 15 | 16 | @$core.Deprecated('Use rTTTLConfigDescriptor instead') 17 | const RTTTLConfig$json = { 18 | '1': 'RTTTLConfig', 19 | '2': [ 20 | {'1': 'ringtone', '3': 1, '4': 1, '5': 9, '10': 'ringtone'}, 21 | ], 22 | }; 23 | 24 | /// Descriptor for `RTTTLConfig`. Decode as a `google.protobuf.DescriptorProto`. 25 | final $typed_data.Uint8List rTTTLConfigDescriptor = $convert 26 | .base64Decode('CgtSVFRUTENvbmZpZxIaCghyaW5ndG9uZRgBIAEoCVIIcmluZ3RvbmU='); 27 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/rtttl.pbserver.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/rtttl.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names 9 | // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes 10 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 11 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 12 | 13 | export 'rtttl.pb.dart'; 14 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/storeforward.pbserver.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/storeforward.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names 9 | // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes 10 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 11 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 12 | 13 | export 'storeforward.pb.dart'; 14 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/telemetry.pbserver.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/telemetry.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names 9 | // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes 10 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 11 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 12 | 13 | export 'telemetry.pb.dart'; 14 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/xmodem.pbenum.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/xmodem.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names, library_prefixes 9 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 10 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 11 | 12 | import 'dart:core' as $core; 13 | 14 | import 'package:protobuf/protobuf.dart' as $pb; 15 | 16 | class XModem_Control extends $pb.ProtobufEnum { 17 | static const XModem_Control NUL = 18 | XModem_Control._(0, _omitEnumNames ? '' : 'NUL'); 19 | static const XModem_Control SOH = 20 | XModem_Control._(1, _omitEnumNames ? '' : 'SOH'); 21 | static const XModem_Control STX = 22 | XModem_Control._(2, _omitEnumNames ? '' : 'STX'); 23 | static const XModem_Control EOT = 24 | XModem_Control._(4, _omitEnumNames ? '' : 'EOT'); 25 | static const XModem_Control ACK = 26 | XModem_Control._(6, _omitEnumNames ? '' : 'ACK'); 27 | static const XModem_Control NAK = 28 | XModem_Control._(21, _omitEnumNames ? '' : 'NAK'); 29 | static const XModem_Control CAN = 30 | XModem_Control._(24, _omitEnumNames ? '' : 'CAN'); 31 | static const XModem_Control CTRLZ = 32 | XModem_Control._(26, _omitEnumNames ? '' : 'CTRLZ'); 33 | 34 | static const $core.List values = [ 35 | NUL, 36 | SOH, 37 | STX, 38 | EOT, 39 | ACK, 40 | NAK, 41 | CAN, 42 | CTRLZ, 43 | ]; 44 | 45 | static final $core.Map<$core.int, XModem_Control> _byValue = 46 | $pb.ProtobufEnum.initByValue(values); 47 | static XModem_Control? valueOf($core.int value) => _byValue[value]; 48 | 49 | const XModem_Control._($core.int v, $core.String n) : super(v, n); 50 | } 51 | 52 | const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names'); 53 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/xmodem.pbjson.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/xmodem.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names, library_prefixes 9 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 10 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 11 | 12 | import 'dart:convert' as $convert; 13 | import 'dart:core' as $core; 14 | import 'dart:typed_data' as $typed_data; 15 | 16 | @$core.Deprecated('Use xModemDescriptor instead') 17 | const XModem$json = { 18 | '1': 'XModem', 19 | '2': [ 20 | { 21 | '1': 'control', 22 | '3': 1, 23 | '4': 1, 24 | '5': 14, 25 | '6': '.meshtastic.XModem.Control', 26 | '10': 'control' 27 | }, 28 | {'1': 'seq', '3': 2, '4': 1, '5': 13, '10': 'seq'}, 29 | {'1': 'crc16', '3': 3, '4': 1, '5': 13, '10': 'crc16'}, 30 | {'1': 'buffer', '3': 4, '4': 1, '5': 12, '10': 'buffer'}, 31 | ], 32 | '4': [XModem_Control$json], 33 | }; 34 | 35 | @$core.Deprecated('Use xModemDescriptor instead') 36 | const XModem_Control$json = { 37 | '1': 'Control', 38 | '2': [ 39 | {'1': 'NUL', '2': 0}, 40 | {'1': 'SOH', '2': 1}, 41 | {'1': 'STX', '2': 2}, 42 | {'1': 'EOT', '2': 4}, 43 | {'1': 'ACK', '2': 6}, 44 | {'1': 'NAK', '2': 21}, 45 | {'1': 'CAN', '2': 24}, 46 | {'1': 'CTRLZ', '2': 26}, 47 | ], 48 | }; 49 | 50 | /// Descriptor for `XModem`. Decode as a `google.protobuf.DescriptorProto`. 51 | final $typed_data.Uint8List xModemDescriptor = $convert.base64Decode( 52 | 'CgZYTW9kZW0SNAoHY29udHJvbBgBIAEoDjIaLm1lc2h0YXN0aWMuWE1vZGVtLkNvbnRyb2xSB2' 53 | 'NvbnRyb2wSEAoDc2VxGAIgASgNUgNzZXESFAoFY3JjMTYYAyABKA1SBWNyYzE2EhYKBmJ1ZmZl' 54 | 'chgEIAEoDFIGYnVmZmVyIlMKB0NvbnRyb2wSBwoDTlVMEAASBwoDU09IEAESBwoDU1RYEAISBw' 55 | 'oDRU9UEAQSBwoDQUNLEAYSBwoDTkFLEBUSBwoDQ0FOEBgSCQoFQ1RSTFoQGg=='); 56 | -------------------------------------------------------------------------------- /lib/protobufs/generated/meshtastic/xmodem.pbserver.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: meshtastic/xmodem.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names 9 | // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes 10 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 11 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 12 | 13 | export 'xmodem.pb.dart'; 14 | -------------------------------------------------------------------------------- /lib/protobufs/generated/nanopb.pbserver.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated code. Do not modify. 3 | // source: nanopb.proto 4 | // 5 | // @dart = 2.12 6 | 7 | // ignore_for_file: annotate_overrides, camel_case_types, comment_references 8 | // ignore_for_file: constant_identifier_names 9 | // ignore_for_file: deprecated_member_use_from_same_package, library_prefixes 10 | // ignore_for_file: non_constant_identifier_names, prefer_final_fields 11 | // ignore_for_file: unnecessary_import, unnecessary_this, unused_import 12 | 13 | export 'nanopb.pb.dart'; 14 | -------------------------------------------------------------------------------- /lib/protobufs/meshtastic/admin.options: -------------------------------------------------------------------------------- 1 | *AdminMessage.payload_variant anonymous_oneof:true 2 | 3 | *AdminMessage.set_canned_message_module_messages max_size:201 4 | *AdminMessage.get_canned_message_module_messages_response max_size:201 5 | *AdminMessage.delete_file_request max_size:201 6 | 7 | *AdminMessage.set_ringtone_message max_size:231 8 | *AdminMessage.get_ringtone_response max_size:231 9 | 10 | *HamParameters.call_sign max_size:8 11 | *HamParameters.short_name max_size:6 12 | *NodeRemoteHardwarePinsResponse.node_remote_hardware_pins max_count:16 -------------------------------------------------------------------------------- /lib/protobufs/meshtastic/apponly.options: -------------------------------------------------------------------------------- 1 | *ChannelSet.settings max_count:8 2 | -------------------------------------------------------------------------------- /lib/protobufs/meshtastic/apponly.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package meshtastic; 4 | 5 | import "meshtastic/channel.proto"; 6 | import "meshtastic/config.proto"; 7 | 8 | option csharp_namespace = "Meshtastic.Protobufs"; 9 | option go_package = "github.com/meshtastic/go/generated"; 10 | option java_outer_classname = "AppOnlyProtos"; 11 | option java_package = "com.geeksville.mesh"; 12 | option swift_prefix = ""; 13 | 14 | /* 15 | * This is the most compact possible representation for a set of channels. 16 | * It includes only one PRIMARY channel (which must be first) and 17 | * any SECONDARY channels. 18 | * No DISABLED channels are included. 19 | * This abstraction is used only on the the 'app side' of the world (ie python, javascript and android etc) to show a group of Channels as a (long) URL 20 | */ 21 | message ChannelSet { 22 | /* 23 | * Channel list with settings 24 | */ 25 | repeated ChannelSettings settings = 1; 26 | 27 | /* 28 | * LoRa config 29 | */ 30 | Config.LoRaConfig lora_config = 2; 31 | } 32 | -------------------------------------------------------------------------------- /lib/protobufs/meshtastic/atak.options: -------------------------------------------------------------------------------- 1 | *Contact.callsign max_size:120 2 | *Contact.device_callsign max_size:120 3 | *Status.battery int_size:8 4 | *PLI.course int_size:16 5 | *GeoChat.message max_size:200 6 | *GeoChat.to max_size:120 -------------------------------------------------------------------------------- /lib/protobufs/meshtastic/cannedmessages.options: -------------------------------------------------------------------------------- 1 | *CannedMessageModuleConfig.messages max_size:201 2 | -------------------------------------------------------------------------------- /lib/protobufs/meshtastic/cannedmessages.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package meshtastic; 4 | 5 | option csharp_namespace = "Meshtastic.Protobufs"; 6 | option go_package = "github.com/meshtastic/go/generated"; 7 | option java_outer_classname = "CannedMessageConfigProtos"; 8 | option java_package = "com.geeksville.mesh"; 9 | option swift_prefix = ""; 10 | 11 | /* 12 | * Canned message module configuration. 13 | */ 14 | message CannedMessageModuleConfig { 15 | /* 16 | * Predefined messages for canned message module separated by '|' characters. 17 | */ 18 | string messages = 1; 19 | } 20 | -------------------------------------------------------------------------------- /lib/protobufs/meshtastic/channel.options: -------------------------------------------------------------------------------- 1 | *Channel.index int_size:8 2 | 3 | # 256 bit or 128 bit psk key 4 | *ChannelSettings.psk max_size:32 5 | *ChannelSettings.name max_size:12 6 | -------------------------------------------------------------------------------- /lib/protobufs/meshtastic/clientonly.options: -------------------------------------------------------------------------------- 1 | *DeviceProfile.long_name max_size:40 2 | *DeviceProfile.short_name max_size:5 -------------------------------------------------------------------------------- /lib/protobufs/meshtastic/clientonly.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package meshtastic; 4 | 5 | import "meshtastic/localonly.proto"; 6 | 7 | option csharp_namespace = "Meshtastic.Protobufs"; 8 | option go_package = "github.com/meshtastic/go/generated"; 9 | option java_outer_classname = "ClientOnlyProtos"; 10 | option java_package = "com.geeksville.mesh"; 11 | option swift_prefix = ""; 12 | 13 | /* 14 | * This abstraction is used to contain any configuration for provisioning a node on any client. 15 | * It is useful for importing and exporting configurations. 16 | */ 17 | message DeviceProfile { 18 | /* 19 | * Long name for the node 20 | */ 21 | optional string long_name = 1; 22 | 23 | /* 24 | * Short name of the node 25 | */ 26 | optional string short_name = 2; 27 | 28 | /* 29 | * The url of the channels from our node 30 | */ 31 | optional string channel_url = 3; 32 | 33 | /* 34 | * The Config of the node 35 | */ 36 | optional LocalConfig config = 4; 37 | 38 | /* 39 | * The ModuleConfig of the node 40 | */ 41 | optional LocalModuleConfig module_config = 5; 42 | } 43 | -------------------------------------------------------------------------------- /lib/protobufs/meshtastic/config.options: -------------------------------------------------------------------------------- 1 | # longest current is 45 chars, plan with a bit of buffer 2 | *DeviceConfig.tzdef max_size:65 3 | 4 | *NetworkConfig.wifi_ssid max_size:33 5 | *NetworkConfig.wifi_psk max_size:65 6 | *NetworkConfig.ntp_server max_size:33 7 | *NetworkConfig.rsyslog_server max_size:33 8 | 9 | # Max of three ignored nodes for our testing 10 | *LoRaConfig.ignore_incoming max_count:3 11 | 12 | *LoRaConfig.tx_power int_size:8 13 | *LoRaConfig.bandwidth int_size:16 14 | *LoRaConfig.coding_rate int_size:8 15 | *LoRaConfig.channel_num int_size:16 16 | 17 | *PowerConfig.device_battery_ina_address int_size:8 18 | -------------------------------------------------------------------------------- /lib/protobufs/meshtastic/connection_status.options: -------------------------------------------------------------------------------- 1 | *WifiConnectionStatus.ssid max_size:33 2 | -------------------------------------------------------------------------------- /lib/protobufs/meshtastic/deviceonly.options: -------------------------------------------------------------------------------- 1 | # options for nanopb 2 | # https://jpa.kapsi.fi/nanopb/docs/reference.html#proto-file-options 3 | 4 | # FIXME - max_count is actually 32 but we save/load this as one long string of preencoded MeshPacket bytes - not a big array in RAM 5 | *DeviceState.receive_queue max_count:1 6 | 7 | *ChannelFile.channels max_count:8 8 | 9 | *OEMStore.oem_text max_size:40 10 | *OEMStore.oem_icon_bits max_size:2048 11 | *OEMStore.oem_aes_key max_size:32 12 | 13 | *DeviceState.node_remote_hardware_pins max_count:12 14 | 15 | *NodeInfoLite.channel int_size:8 16 | *NodeInfoLite.hops_away int_size:8 17 | -------------------------------------------------------------------------------- /lib/protobufs/meshtastic/module_config.options: -------------------------------------------------------------------------------- 1 | *CannedMessageConfig.allow_input_source max_size:16 2 | 3 | *MQTTConfig.address max_size:64 4 | *MQTTConfig.username max_size:64 5 | *MQTTConfig.password max_size:64 6 | *MQTTConfig.root max_size:32 7 | 8 | *AudioConfig.ptt_pin int_size:8 9 | *AudioConfig.i2s_ws int_size:8 10 | *AudioConfig.i2s_sd int_size:8 11 | *AudioConfig.i2s_din int_size:8 12 | *AudioConfig.i2s_sck int_size:8 13 | 14 | *ExternalNotificationConfig.output_vibra int_size:8 15 | *ExternalNotificationConfig.output_buzzer int_size:8 16 | *ExternalNotificationConfig.nag_timeout int_size:16 17 | 18 | *RemoteHardwareConfig.available_pins max_count:4 19 | *RemoteHardwarePin.name max_size:15 20 | *RemoteHardwarePin.gpio_pin int_size:8 21 | 22 | *AmbientLightingConfig.current int_size:8 23 | *AmbientLightingConfig.red int_size:8 24 | *AmbientLightingConfig.green int_size:8 25 | *AmbientLightingConfig.blue int_size:8 26 | 27 | *DetectionSensorConfig.monitor_pin int_size:8 28 | *DetectionSensorConfig.name max_size:20 29 | -------------------------------------------------------------------------------- /lib/protobufs/meshtastic/mqtt.options: -------------------------------------------------------------------------------- 1 | *ServiceEnvelope.packet type:FT_POINTER 2 | *ServiceEnvelope.channel_id type:FT_POINTER 3 | *ServiceEnvelope.gateway_id type:FT_POINTER 4 | 5 | *MapReport.long_name max_size:40 6 | *MapReport.short_name max_size:5 7 | *MapReport.firmware_version max_size:18 8 | *MapReport.num_online_local_nodes int_size:16 -------------------------------------------------------------------------------- /lib/protobufs/meshtastic/paxcount.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package meshtastic; 4 | 5 | option csharp_namespace = "Meshtastic.Protobufs"; 6 | option go_package = "github.com/meshtastic/go/generated"; 7 | option java_outer_classname = "PaxcountProtos"; 8 | option java_package = "com.geeksville.mesh"; 9 | option swift_prefix = ""; 10 | 11 | /* 12 | * TODO: REPLACE 13 | */ 14 | message Paxcount { 15 | /* 16 | * seen Wifi devices 17 | */ 18 | uint32 wifi = 1; 19 | 20 | /* 21 | * Seen BLE devices 22 | */ 23 | uint32 ble = 2; 24 | 25 | /* 26 | * Uptime in seconds 27 | */ 28 | uint32 uptime = 3; 29 | } 30 | -------------------------------------------------------------------------------- /lib/protobufs/meshtastic/rtttl.options: -------------------------------------------------------------------------------- 1 | *RTTTLConfig.ringtone max_size:230 2 | -------------------------------------------------------------------------------- /lib/protobufs/meshtastic/rtttl.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package meshtastic; 4 | 5 | option csharp_namespace = "Meshtastic.Protobufs"; 6 | option go_package = "github.com/meshtastic/go/generated"; 7 | option java_outer_classname = "RTTTLConfigProtos"; 8 | option java_package = "com.geeksville.mesh"; 9 | option swift_prefix = ""; 10 | 11 | /* 12 | * Canned message module configuration. 13 | */ 14 | message RTTTLConfig { 15 | /* 16 | * Ringtone for PWM Buzzer in RTTTL Format. 17 | */ 18 | string ringtone = 1; 19 | } 20 | -------------------------------------------------------------------------------- /lib/protobufs/meshtastic/storeforward.options: -------------------------------------------------------------------------------- 1 | *StoreAndForward.text max_size:237 -------------------------------------------------------------------------------- /lib/protobufs/meshtastic/telemetry.options: -------------------------------------------------------------------------------- 1 | # options for nanopb 2 | # https://jpa.kapsi.fi/nanopb/docs/reference.html#proto-file-options 3 | 4 | *EnvironmentMetrics.iaq int_size:16 -------------------------------------------------------------------------------- /lib/protobufs/meshtastic/xmodem.options: -------------------------------------------------------------------------------- 1 | # options for nanopb 2 | # https://jpa.kapsi.fi/nanopb/docs/reference.html#proto-file-options 3 | 4 | *XModem.buffer max_size:128 5 | *XModem.seq int_size:16 6 | *XModem.crc16 int_size:16 7 | -------------------------------------------------------------------------------- /lib/protobufs/meshtastic/xmodem.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package meshtastic; 4 | 5 | option csharp_namespace = "Meshtastic.Protobufs"; 6 | option go_package = "github.com/meshtastic/go/generated"; 7 | option java_outer_classname = "XmodemProtos"; 8 | option java_package = "com.geeksville.mesh"; 9 | option swift_prefix = ""; 10 | 11 | message XModem { 12 | enum Control { 13 | NUL = 0; 14 | SOH = 1; 15 | STX = 2; 16 | EOT = 4; 17 | ACK = 6; 18 | NAK = 21; 19 | CAN = 24; 20 | CTRLZ = 26; 21 | } 22 | 23 | Control control = 1; 24 | uint32 seq = 2; 25 | uint32 crc16 = 3; 26 | bytes buffer = 4; 27 | } 28 | -------------------------------------------------------------------------------- /lib/providers/ack_waiting_radio_writer.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | import '../services/ack_waiting_radio_writer.dart'; 5 | import 'radio_config/radio_config_service.dart'; 6 | import 'radio_reader.dart'; 7 | import 'radio_writer.dart'; 8 | 9 | part 'ack_waiting_radio_writer.g.dart'; 10 | 11 | @Riverpod(keepAlive: true) 12 | AckWaitingRadioWriter ackWaitingRadioWriter(AckWaitingRadioWriterRef ref) { 13 | return AckWaitingRadioWriter( 14 | hopLimitProvider: () => 15 | ref.read(radioConfigServiceProvider).loraConfig.hopLimit, 16 | radioReader: ref.watch(radioReaderProvider), 17 | radioWriter: ref.watch(radioWriterProvider), 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /lib/providers/ack_waiting_radio_writer.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'ack_waiting_radio_writer.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$ackWaitingRadioWriterHash() => 10 | r'72beffedeb095194efdbd6bed0a679651c939b89'; 11 | 12 | /// See also [ackWaitingRadioWriter]. 13 | @ProviderFor(ackWaitingRadioWriter) 14 | final ackWaitingRadioWriterProvider = Provider.internal( 15 | ackWaitingRadioWriter, 16 | name: r'ackWaitingRadioWriterProvider', 17 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 18 | ? null 19 | : _$ackWaitingRadioWriterHash, 20 | dependencies: null, 21 | allTransitiveDependencies: null, 22 | ); 23 | 24 | typedef AckWaitingRadioWriterRef = ProviderRef; 25 | // ignore_for_file: type=lint 26 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 27 | -------------------------------------------------------------------------------- /lib/providers/ble/ble_characteristics_finder.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | import '../../services/ble/ble_characteristics_finder.dart'; 5 | 6 | part 'ble_characteristics_finder.g.dart'; 7 | 8 | @Riverpod(keepAlive: true) 9 | BleCharacteristicsFinder bleCharacteristicsFinder( 10 | BleCharacteristicsFinderRef ref, 11 | ) { 12 | return BleCharacteristicsFinder(); 13 | } 14 | -------------------------------------------------------------------------------- /lib/providers/ble/ble_characteristics_finder.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'ble_characteristics_finder.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$bleCharacteristicsFinderHash() => 10 | r'd59818a8ba370f3968e470d134bd16671f8f4a81'; 11 | 12 | /// See also [bleCharacteristicsFinder]. 13 | @ProviderFor(bleCharacteristicsFinder) 14 | final bleCharacteristicsFinderProvider = 15 | Provider.internal( 16 | bleCharacteristicsFinder, 17 | name: r'bleCharacteristicsFinderProvider', 18 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 19 | ? null 20 | : _$bleCharacteristicsFinderHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | typedef BleCharacteristicsFinderRef = ProviderRef; 26 | // ignore_for_file: type=lint 27 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 28 | -------------------------------------------------------------------------------- /lib/providers/ble/ble_permissions_requester.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | import '../../services/ble/ble_permissions_requester.dart'; 5 | import '../wrap/flutter_blue_plus_mockable.dart'; 6 | import '../wrap/local_platform.dart'; 7 | import '../wrap/permissions.dart'; 8 | 9 | part 'ble_permissions_requester.g.dart'; 10 | 11 | @Riverpod(keepAlive: true) 12 | BlePermissionsRequester blePermissionsRequester( 13 | BlePermissionsRequesterRef ref, 14 | ) { 15 | return BlePermissionsRequester( 16 | localPlatform: ref.watch(localPlatformProvider), 17 | flutterBluePlus: ref.watch(flutterBluePlusProvider), 18 | permissions: ref.watch(permissionsProvider), 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /lib/providers/ble/ble_permissions_requester.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'ble_permissions_requester.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$blePermissionsRequesterHash() => 10 | r'07fc3cc08fa359caf4ae3df64729884778ef06fe'; 11 | 12 | /// See also [blePermissionsRequester]. 13 | @ProviderFor(blePermissionsRequester) 14 | final blePermissionsRequesterProvider = 15 | Provider.internal( 16 | blePermissionsRequester, 17 | name: r'blePermissionsRequesterProvider', 18 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 19 | ? null 20 | : _$blePermissionsRequesterHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | typedef BlePermissionsRequesterRef = ProviderRef; 26 | // ignore_for_file: type=lint 27 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 28 | -------------------------------------------------------------------------------- /lib/providers/ble/ble_radio_connector.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'ble_radio_connector.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$bleRadioConnectorHash() => r'e4236e188ed8702db6ab5fbe76da09af1c26ca81'; 10 | 11 | /// See also [BleRadioConnector]. 12 | @ProviderFor(BleRadioConnector) 13 | final bleRadioConnectorProvider = 14 | NotifierProvider.internal( 15 | BleRadioConnector.new, 16 | name: r'bleRadioConnectorProvider', 17 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 18 | ? null 19 | : _$bleRadioConnectorHash, 20 | dependencies: null, 21 | allTransitiveDependencies: null, 22 | ); 23 | 24 | typedef _$BleRadioConnector = Notifier; 25 | // ignore_for_file: type=lint 26 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 27 | -------------------------------------------------------------------------------- /lib/providers/ble/ble_radio_scanner.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'ble_radio_scanner.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$bleRadioScannerHash() => r'fc430734b236a903c4b282041ba5b16eaa671e10'; 10 | 11 | /// See also [BleRadioScanner]. 12 | @ProviderFor(BleRadioScanner) 13 | final bleRadioScannerProvider = 14 | NotifierProvider.internal( 15 | BleRadioScanner.new, 16 | name: r'bleRadioScannerProvider', 17 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 18 | ? null 19 | : _$bleRadioScannerHash, 20 | dependencies: null, 21 | allTransitiveDependencies: null, 22 | ); 23 | 24 | typedef _$BleRadioScanner = Notifier; 25 | // ignore_for_file: type=lint 26 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 27 | -------------------------------------------------------------------------------- /lib/providers/breadcrumb_logger.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | 3 | import '../services/breadcrumb_logger.dart'; 4 | import 'wrap/local_platform.dart'; 5 | part 'breadcrumb_logger.g.dart'; 6 | 7 | @Riverpod(keepAlive: true) 8 | BreadcrumbLogger breadcrumbLogger(BreadcrumbLoggerRef ref) { 9 | final localPlatform = ref.read(localPlatformProvider); 10 | if (!localPlatform.isWindows && !localPlatform.isLinux) { 11 | return FirebaseLogger(); 12 | } else { 13 | return NullLogger(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/providers/breadcrumb_logger.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'breadcrumb_logger.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$breadcrumbLoggerHash() => r'87a8cd98c3746dbaa490c4662d2022bd4ac0637e'; 10 | 11 | /// See also [breadcrumbLogger]. 12 | @ProviderFor(breadcrumbLogger) 13 | final breadcrumbLoggerProvider = Provider.internal( 14 | breadcrumbLogger, 15 | name: r'breadcrumbLoggerProvider', 16 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 17 | ? null 18 | : _$breadcrumbLoggerHash, 19 | dependencies: null, 20 | allTransitiveDependencies: null, 21 | ); 22 | 23 | typedef BreadcrumbLoggerRef = ProviderRef; 24 | // ignore_for_file: type=lint 25 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 26 | -------------------------------------------------------------------------------- /lib/providers/channel_service.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'channel_service.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$channelServiceHash() => r'ded70bafca9778b46ec78810085145200d71724d'; 10 | 11 | /// See also [ChannelService]. 12 | @ProviderFor(ChannelService) 13 | final channelServiceProvider = 14 | NotifierProvider>.internal( 15 | ChannelService.new, 16 | name: r'channelServiceProvider', 17 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 18 | ? null 19 | : _$channelServiceHash, 20 | dependencies: null, 21 | allTransitiveDependencies: null, 22 | ); 23 | 24 | typedef _$ChannelService = Notifier>; 25 | // ignore_for_file: type=lint 26 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 27 | -------------------------------------------------------------------------------- /lib/providers/node/hops_away.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | 3 | import '../radio_reader.dart'; 4 | part 'hops_away.g.dart'; 5 | 6 | @Riverpod(keepAlive: true) 7 | class HopsAway extends _$HopsAway { 8 | @override 9 | int? build(int nodeNum) { 10 | final sub = 11 | ref.watch(radioReaderProvider).onPacketReceived().listen((event) { 12 | final packet = event.packet; 13 | if (packet.from == nodeNum && 14 | packet.hasHopLimit() && 15 | packet.hasHopStart()) { 16 | state = (packet.hopStart - packet.hopLimit).abs(); 17 | } 18 | }); 19 | ref.onDispose(sub.cancel); 20 | return null; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/providers/node/node_search.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | 3 | import '../../models/mesh_node.dart'; 4 | import 'node_service.dart'; 5 | 6 | part 'node_search.g.dart'; 7 | 8 | @riverpod 9 | Map nodeSearch(NodeSearchRef ref, String searchKey) { 10 | final searchKeyLowercase = searchKey.toLowerCase(); 11 | final nodes = ref.watch(nodeServiceProvider); 12 | if (searchKey.trim().isEmpty) { 13 | return nodes; 14 | } else { 15 | final result = {}; 16 | nodes.values 17 | .where( 18 | (element) => 19 | element.longName.toLowerCase().contains(searchKeyLowercase) || 20 | element.shortName.toLowerCase().contains(searchKeyLowercase), 21 | ) 22 | .forEach((element) { 23 | result[element.nodeNum] = element; 24 | }); 25 | return result; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/providers/node/node_service.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'node_service.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$nodeServiceHash() => r'685ebafd38255a204dd7e0bcf7c8fcf26e07f331'; 10 | 11 | /// See also [NodeService]. 12 | @ProviderFor(NodeService) 13 | final nodeServiceProvider = 14 | NotifierProvider>.internal( 15 | NodeService.new, 16 | name: r'nodeServiceProvider', 17 | debugGetCreateSourceHash: 18 | const bool.fromEnvironment('dart.vm.product') ? null : _$nodeServiceHash, 19 | dependencies: null, 20 | allTransitiveDependencies: null, 21 | ); 22 | 23 | typedef _$NodeService = Notifier>; 24 | // ignore_for_file: type=lint 25 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 26 | -------------------------------------------------------------------------------- /lib/providers/radio_config/radio_config_downloader_service.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | import '../../models/radio_connector_state.dart'; 5 | import '../../services/radio_config/radio_config_downloader_service.dart'; 6 | import '../ack_waiting_radio_writer.dart'; 7 | import '../radio_connector_service.dart'; 8 | import '../radio_reader.dart'; 9 | import 'radio_config_service.dart'; 10 | 11 | part 'radio_config_downloader_service.g.dart'; 12 | 13 | @Riverpod(keepAlive: true) 14 | RadioConfigDownloaderService radioConfigDownloaderService( 15 | RadioConfigDownloaderServiceRef ref, 16 | ) { 17 | final sub = ref.listen(radioConnectorServiceProvider, (_, next) { 18 | if (next is Connected) { 19 | ref.invalidateSelf(); 20 | } 21 | }); 22 | return RadioConfigDownloaderService( 23 | radioWriter: ref.watch(ackWaitingRadioWriterProvider), 24 | radioReader: ref.watch(radioReaderProvider), 25 | radioConnectorState: sub.read(), 26 | // riverpod requires us to read the notifier 27 | radioConfigServiceProvider: () => 28 | ref.read(radioConfigServiceProvider.notifier), 29 | disconnect: (errorMsg) => ref 30 | .read(radioConnectorServiceProvider.notifier) 31 | .disconnect(errorMsg: errorMsg), 32 | onDispose: ref.onDispose, 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /lib/providers/radio_config/radio_config_downloader_service.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'radio_config_downloader_service.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$radioConfigDownloaderServiceHash() => 10 | r'031e119af61a07b855f195b68ac90adcf57eeda9'; 11 | 12 | /// See also [radioConfigDownloaderService]. 13 | @ProviderFor(radioConfigDownloaderService) 14 | final radioConfigDownloaderServiceProvider = 15 | Provider.internal( 16 | radioConfigDownloaderService, 17 | name: r'radioConfigDownloaderServiceProvider', 18 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 19 | ? null 20 | : _$radioConfigDownloaderServiceHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | typedef RadioConfigDownloaderServiceRef 26 | = ProviderRef; 27 | // ignore_for_file: type=lint 28 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 29 | -------------------------------------------------------------------------------- /lib/providers/radio_config/radio_config_service.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'radio_config_service.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$radioConfigServiceHash() => 10 | r'8be8b8fe641b5fe6ef626b0ccebba2f2e05c4d4b'; 11 | 12 | /// See also [RadioConfigService]. 13 | @ProviderFor(RadioConfigService) 14 | final radioConfigServiceProvider = 15 | NotifierProvider.internal( 16 | RadioConfigService.new, 17 | name: r'radioConfigServiceProvider', 18 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 19 | ? null 20 | : _$radioConfigServiceHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | typedef _$RadioConfigService = Notifier; 26 | // ignore_for_file: type=lint 27 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 28 | -------------------------------------------------------------------------------- /lib/providers/radio_config/radio_config_uploader_service.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | import '../../services/radio_config/radio_config_uploader_service.dart'; 5 | import '../ack_waiting_radio_writer.dart'; 6 | import 'radio_config_service.dart'; 7 | 8 | part 'radio_config_uploader_service.g.dart'; 9 | 10 | @Riverpod(keepAlive: true) 11 | RadioConfigUploaderService radioConfigUploaderService( 12 | RadioConfigUploaderServiceRef ref, 13 | ) { 14 | return RadioConfigUploaderService( 15 | radioWriter: ref.watch(ackWaitingRadioWriterProvider), 16 | myNodeNum: ref 17 | .watch(radioConfigServiceProvider.select((value) => value.myNodeNum)), 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /lib/providers/radio_config/radio_config_uploader_service.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'radio_config_uploader_service.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$radioConfigUploaderServiceHash() => 10 | r'0745fcd74b46bcb69844809cdd7ea3eb6af36762'; 11 | 12 | /// See also [radioConfigUploaderService]. 13 | @ProviderFor(radioConfigUploaderService) 14 | final radioConfigUploaderServiceProvider = 15 | Provider.internal( 16 | radioConfigUploaderService, 17 | name: r'radioConfigUploaderServiceProvider', 18 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 19 | ? null 20 | : _$radioConfigUploaderServiceHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | typedef RadioConfigUploaderServiceRef = ProviderRef; 26 | // ignore_for_file: type=lint 27 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 28 | -------------------------------------------------------------------------------- /lib/providers/radio_connector_service.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'radio_connector_service.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$radioConnectorServiceHash() => 10 | r'4af18c4b66bb783995a15427f26c88e42bd0587b'; 11 | 12 | /// See also [RadioConnectorService]. 13 | @ProviderFor(RadioConnectorService) 14 | final radioConnectorServiceProvider = 15 | NotifierProvider.internal( 16 | RadioConnectorService.new, 17 | name: r'radioConnectorServiceProvider', 18 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 19 | ? null 20 | : _$radioConnectorServiceHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | typedef _$RadioConnectorService = Notifier; 26 | // ignore_for_file: type=lint 27 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 28 | -------------------------------------------------------------------------------- /lib/providers/radio_reader.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | 3 | import '../models/radio_connector_state.dart'; 4 | import '../services/ble/ble_radio_reader.dart'; 5 | import '../services/interfaces/radio_reader.dart'; 6 | import '../services/null/null_reader.dart'; 7 | import '../services/tcp/tcp_radio_reader.dart'; 8 | import 'radio_connector_service.dart'; 9 | 10 | part 'radio_reader.g.dart'; 11 | 12 | @Riverpod(keepAlive: true) 13 | RadioReader radioReader(RadioReaderRef ref) { 14 | // we can not ref.watch radioConnectorServiceProvider 15 | // because for offline view mode, we want to cache 16 | // states of providers that depend on radioReader 17 | final sub = ref.listen(radioConnectorServiceProvider, (_, next) { 18 | if (next is Connected) { 19 | ref.invalidateSelf(); 20 | } 21 | }); 22 | 23 | final connectorState = sub.read(); 24 | switch (connectorState) { 25 | case BleConnected(): 26 | return BleRadioReader( 27 | radioConnectorState: sub.read(), 28 | onDispose: ref.onDispose, 29 | ); 30 | case TcpConnected(): 31 | return TcpRadioReader( 32 | radioConnectorState: sub.read(), 33 | onDispose: ref.onDispose, 34 | ); 35 | case _: 36 | return NullReader(onDispose: ref.onDispose); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/providers/radio_reader.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'radio_reader.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$radioReaderHash() => r'e6fc0666c9234c8657e2be1473f57ce407510bab'; 10 | 11 | /// See also [radioReader]. 12 | @ProviderFor(radioReader) 13 | final radioReaderProvider = Provider.internal( 14 | radioReader, 15 | name: r'radioReaderProvider', 16 | debugGetCreateSourceHash: 17 | const bool.fromEnvironment('dart.vm.product') ? null : _$radioReaderHash, 18 | dependencies: null, 19 | allTransitiveDependencies: null, 20 | ); 21 | 22 | typedef RadioReaderRef = ProviderRef; 23 | // ignore_for_file: type=lint 24 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 25 | -------------------------------------------------------------------------------- /lib/providers/radio_writer.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | 3 | import '../models/radio_connector_state.dart'; 4 | import '../services/ble/ble_radio_writer.dart'; 5 | import '../services/interfaces/radio_writer.dart'; 6 | import '../services/null/null_writer.dart'; 7 | import '../services/tcp/tcp_radio_writer.dart'; 8 | import 'radio_connector_service.dart'; 9 | 10 | part 'radio_writer.g.dart'; 11 | 12 | @Riverpod(keepAlive: true) 13 | RadioWriter radioWriter(RadioWriterRef ref) { 14 | // it is safe to rebuild for every connector state 15 | // since this provider is abstracted by queuedRadioWriterProvider, 16 | // preserving offline view mode 17 | final connectorState = ref.watch(radioConnectorServiceProvider); 18 | late final RadioWriter radioWriter; 19 | switch (connectorState) { 20 | case BleConnected(): 21 | radioWriter = BleRadioWriter(connectorState); 22 | case TcpConnected(): 23 | radioWriter = TcpRadioWriter(connectorState); 24 | case _: 25 | radioWriter = NullWriter(); 26 | } 27 | 28 | return radioWriter; 29 | } 30 | -------------------------------------------------------------------------------- /lib/providers/radio_writer.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'radio_writer.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$radioWriterHash() => r'6f86a219c59566fc6b66e1358840ada981791602'; 10 | 11 | /// See also [radioWriter]. 12 | @ProviderFor(radioWriter) 13 | final radioWriterProvider = Provider.internal( 14 | radioWriter, 15 | name: r'radioWriterProvider', 16 | debugGetCreateSourceHash: 17 | const bool.fromEnvironment('dart.vm.product') ? null : _$radioWriterHash, 18 | dependencies: null, 19 | allTransitiveDependencies: null, 20 | ); 21 | 22 | typedef RadioWriterRef = ProviderRef; 23 | // ignore_for_file: type=lint 24 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 25 | -------------------------------------------------------------------------------- /lib/providers/reconnector.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | import '../models/radio_connector_state.dart'; 5 | import 'radio_connector_service.dart'; 6 | 7 | part 'reconnector.g.dart'; 8 | 9 | @Riverpod(keepAlive: true) 10 | class ReconnectorService extends _$ReconnectorService { 11 | final _logger = Logger(); 12 | bool _canReconnect = true; 13 | 14 | @override 15 | dynamic build({Duration delay = const Duration(seconds: 15)}) { 16 | ref.listen(radioConnectorServiceProvider, (prev, next) async { 17 | if (next is Connected) { 18 | _canReconnect = true; 19 | return; 20 | } 21 | 22 | if (prev is Connected && next is Disconnected) { 23 | if (!_canReconnect) { 24 | _logger.i('Not autoreconnecting to $prev'); 25 | return; 26 | } 27 | do { 28 | _logger.i('Reconnecting to $prev'); 29 | await ref 30 | .read(radioConnectorServiceProvider.notifier) 31 | .connect(prev.radio); 32 | await Future.delayed(delay); 33 | } while (ref.read(radioConnectorServiceProvider) is! Connected); 34 | _logger.i('Ended reconnect loop for $prev'); 35 | } 36 | }); 37 | return null; 38 | } 39 | 40 | void disableReconnectUntilNextDisconnect() { 41 | _canReconnect = false; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/providers/repository/sqflite.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'sqflite.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$sqfliteHash() => r'60fb2933fa1be75ccad7e55d2a8b9c5854e9ba35'; 10 | 11 | /// See also [sqflite]. 12 | @ProviderFor(sqflite) 13 | final sqfliteProvider = FutureProvider.internal( 14 | sqflite, 15 | name: r'sqfliteProvider', 16 | debugGetCreateSourceHash: 17 | const bool.fromEnvironment('dart.vm.product') ? null : _$sqfliteHash, 18 | dependencies: null, 19 | allTransitiveDependencies: null, 20 | ); 21 | 22 | typedef SqfliteRef = FutureProviderRef; 23 | // ignore_for_file: type=lint 24 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 25 | -------------------------------------------------------------------------------- /lib/providers/repository/telemetry_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | 3 | import '../../repository/telemetry_repository.dart'; 4 | import 'sqflite.dart'; 5 | 6 | part 'telemetry_repository.g.dart'; 7 | 8 | @Riverpod(keepAlive: true) 9 | TelemetryRepository telemetryRepository( 10 | TelemetryRepositoryRef ref, 11 | ) { 12 | return TelemetryRepository( 13 | database: ref.watch(sqfliteProvider).requireValue, 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /lib/providers/repository/telemetry_repository.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'telemetry_repository.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$telemetryRepositoryHash() => 10 | r'1247577e6b338829e0d6ed720446323794a5da2c'; 11 | 12 | /// See also [telemetryRepository]. 13 | @ProviderFor(telemetryRepository) 14 | final telemetryRepositoryProvider = Provider.internal( 15 | telemetryRepository, 16 | name: r'telemetryRepositoryProvider', 17 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 18 | ? null 19 | : _$telemetryRepositoryHash, 20 | dependencies: null, 21 | allTransitiveDependencies: null, 22 | ); 23 | 24 | typedef TelemetryRepositoryRef = ProviderRef; 25 | // ignore_for_file: type=lint 26 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 27 | -------------------------------------------------------------------------------- /lib/providers/repository/text_message_repository.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | import '../../repository/text_message_repository.dart'; 5 | import 'sqflite.dart'; 6 | 7 | part 'text_message_repository.g.dart'; 8 | 9 | @Riverpod(keepAlive: true) 10 | TextMessageRepository textMessageRepository( 11 | TextMessageRepositoryRef ref, 12 | ) { 13 | return TextMessageRepository( 14 | database: ref.watch(sqfliteProvider).requireValue, 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /lib/providers/repository/text_message_repository.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'text_message_repository.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$textMessageRepositoryHash() => 10 | r'a577d47df342beb955e88fb9c3295ccf2f283379'; 11 | 12 | /// See also [textMessageRepository]. 13 | @ProviderFor(textMessageRepository) 14 | final textMessageRepositoryProvider = Provider.internal( 15 | textMessageRepository, 16 | name: r'textMessageRepositoryProvider', 17 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 18 | ? null 19 | : _$textMessageRepositoryHash, 20 | dependencies: null, 21 | allTransitiveDependencies: null, 22 | ); 23 | 24 | typedef TextMessageRepositoryRef = ProviderRef; 25 | // ignore_for_file: type=lint 26 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 27 | -------------------------------------------------------------------------------- /lib/providers/router.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'router.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$goRouterHash() => r'04095a1145af658f847034a2151e55978add8407'; 10 | 11 | /// See also [goRouter]. 12 | @ProviderFor(goRouter) 13 | final goRouterProvider = AutoDisposeProvider.internal( 14 | goRouter, 15 | name: r'goRouterProvider', 16 | debugGetCreateSourceHash: 17 | const bool.fromEnvironment('dart.vm.product') ? null : _$goRouterHash, 18 | dependencies: null, 19 | allTransitiveDependencies: null, 20 | ); 21 | 22 | typedef GoRouterRef = AutoDisposeProviderRef; 23 | // ignore_for_file: type=lint 24 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 25 | -------------------------------------------------------------------------------- /lib/providers/tcp/tcp_radio_connector.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'tcp_radio_connector.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$tcpRadioConnectorHash() => r'ddf2eb2df9cac3537f279ac281ba51b1c82b57ea'; 10 | 11 | /// See also [TcpRadioConnector]. 12 | @ProviderFor(TcpRadioConnector) 13 | final tcpRadioConnectorProvider = 14 | NotifierProvider.internal( 15 | TcpRadioConnector.new, 16 | name: r'tcpRadioConnectorProvider', 17 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 18 | ? null 19 | : _$tcpRadioConnectorHash, 20 | dependencies: null, 21 | allTransitiveDependencies: null, 22 | ); 23 | 24 | typedef _$TcpRadioConnector = Notifier; 25 | // ignore_for_file: type=lint 26 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 27 | -------------------------------------------------------------------------------- /lib/providers/tcp/tcp_radio_scanner.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:logger/logger.dart'; 4 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 5 | 6 | import '../../constants/meshtastic_constants.dart'; 7 | import '../../models/mesh_radio.dart'; 8 | import '../../models/radio_scan_state.dart'; 9 | 10 | part 'tcp_radio_scanner.g.dart'; 11 | 12 | @Riverpod(keepAlive: true) 13 | class TcpRadioScanner extends _$TcpRadioScanner { 14 | final _logger = Logger(); 15 | 16 | @override 17 | RadioScanState build() { 18 | return const RadioScanState(); 19 | } 20 | 21 | Future scan() async { 22 | var errors = 0; 23 | state = state.copyWith(scanning: true); 24 | for (var i = 0; i < 10; i++) { 25 | final host = i == 0 ? 'meshtastic.local' : 'meshtastic-$i.local'; 26 | Socket? socket; 27 | try { 28 | socket = await Socket.connect( 29 | host, 30 | MESHTASTIC_TCP_PORT, 31 | timeout: const Duration(seconds: 5), 32 | ); 33 | _addIfNew(host); 34 | errors = 0; 35 | } catch (e) { 36 | await Future.delayed(const Duration(seconds: 1)); 37 | _logger.e(e.toString()); 38 | if (++errors > 2) { 39 | // there is likely no next device 40 | break; 41 | } 42 | } finally { 43 | await socket?.close(); 44 | } 45 | } 46 | state = state.copyWith(scanning: false); 47 | } 48 | 49 | void _addIfNew(String host) { 50 | if (state.meshRadios.where((element) => element.name == host).isEmpty) { 51 | state = state.copyWith( 52 | meshRadios: [TcpMeshRadio(address: host), ...state.meshRadios], 53 | ); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/providers/tcp/tcp_radio_scanner.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'tcp_radio_scanner.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$tcpRadioScannerHash() => r'877b2e1ee2e37d78ec88d2668278bfd95dd2d79e'; 10 | 11 | /// See also [TcpRadioScanner]. 12 | @ProviderFor(TcpRadioScanner) 13 | final tcpRadioScannerProvider = 14 | NotifierProvider.internal( 15 | TcpRadioScanner.new, 16 | name: r'tcpRadioScannerProvider', 17 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 18 | ? null 19 | : _$tcpRadioScannerHash, 20 | dependencies: null, 21 | allTransitiveDependencies: null, 22 | ); 23 | 24 | typedef _$TcpRadioScanner = Notifier; 25 | // ignore_for_file: type=lint 26 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 27 | -------------------------------------------------------------------------------- /lib/providers/telemetry/telemetry_receiver.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | 3 | import '../../services/telemetry/telemetry_receiver.dart'; 4 | import '../radio_config/radio_config_service.dart'; 5 | import '../radio_reader.dart'; 6 | import '../repository/telemetry_repository.dart'; 7 | part 'telemetry_receiver.g.dart'; 8 | 9 | @Riverpod(keepAlive: true) 10 | TelemetryReceiver telemetryReceiver(TelemetryReceiverRef ref) { 11 | return TelemetryReceiver( 12 | myNodeNum: ref 13 | .watch(radioConfigServiceProvider.select((value) => value.myNodeNum)), 14 | telemetryRepository: ref.watch(telemetryRepositoryProvider), 15 | radioReader: ref.watch(radioReaderProvider), 16 | onDispose: ref.onDispose, 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /lib/providers/telemetry/telemetry_receiver.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'telemetry_receiver.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$telemetryReceiverHash() => r'18e77b76782e4912d8927572eae3e3b6950c8c6e'; 10 | 11 | /// See also [telemetryReceiver]. 12 | @ProviderFor(telemetryReceiver) 13 | final telemetryReceiverProvider = Provider.internal( 14 | telemetryReceiver, 15 | name: r'telemetryReceiverProvider', 16 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 17 | ? null 18 | : _$telemetryReceiverHash, 19 | dependencies: null, 20 | allTransitiveDependencies: null, 21 | ); 22 | 23 | typedef TelemetryReceiverRef = ProviderRef; 24 | // ignore_for_file: type=lint 25 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 26 | -------------------------------------------------------------------------------- /lib/providers/text_message/text_message_receiver_service.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | import '../../services/text_message/text_message_receiver_service.dart'; 5 | import '../node/node_service.dart'; 6 | import '../notifications.dart'; 7 | import '../radio_config/radio_config_service.dart'; 8 | import '../radio_reader.dart'; 9 | import '../repository/text_message_repository.dart'; 10 | 11 | part 'text_message_receiver_service.g.dart'; 12 | 13 | @Riverpod(keepAlive: true) 14 | TextMessageReceiverService textMessageReceiverService( 15 | TextMessageReceiverServiceRef ref, 16 | ) { 17 | return TextMessageReceiverService( 18 | textMessageRepository: ref.watch(textMessageRepositoryProvider), 19 | radioReader: ref.watch(radioReaderProvider), 20 | // prevent rebuilds when there is a new node 21 | nodes: () => ref.read(nodeServiceProvider), 22 | nodeService: () => ref.read(nodeServiceProvider.notifier), 23 | configDownloaded: ref 24 | .watch(radioConfigServiceProvider.select((it) => it.configDownloaded)), 25 | myNodeNum: 26 | ref.watch(radioConfigServiceProvider.select((it) => it.myNodeNum)), 27 | showNotification: (title, text, callbackValue) async { 28 | ref.read( 29 | showNotificationProvider( 30 | title: title, 31 | text: text, 32 | callbackValue: callbackValue, 33 | ), 34 | ); 35 | }, 36 | onDispose: ref.onDispose, 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /lib/providers/text_message/text_message_receiver_service.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'text_message_receiver_service.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$textMessageReceiverServiceHash() => 10 | r'4499643b6a6d7414df3a86552cbbf96ed00a0017'; 11 | 12 | /// See also [textMessageReceiverService]. 13 | @ProviderFor(textMessageReceiverService) 14 | final textMessageReceiverServiceProvider = 15 | Provider.internal( 16 | textMessageReceiverService, 17 | name: r'textMessageReceiverServiceProvider', 18 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 19 | ? null 20 | : _$textMessageReceiverServiceHash, 21 | dependencies: null, 22 | allTransitiveDependencies: null, 23 | ); 24 | 25 | typedef TextMessageReceiverServiceRef = ProviderRef; 26 | // ignore_for_file: type=lint 27 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 28 | -------------------------------------------------------------------------------- /lib/providers/text_message/text_message_sender_service.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | 4 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 5 | 6 | import '../../models/chat_type.dart'; 7 | import '../../models/text_message.dart'; 8 | import '../../protobufs/generated/meshtastic/portnums.pb.dart'; 9 | import '../ack_waiting_radio_writer.dart'; 10 | import '../radio_config/radio_config_service.dart'; 11 | import '../repository/text_message_repository.dart'; 12 | import 'text_message_stream_service.dart'; 13 | 14 | part 'text_message_sender_service.g.dart'; 15 | 16 | @riverpod 17 | Future sendTextMessage( 18 | SendTextMessageRef ref, { 19 | required ChatType chatType, 20 | required String text, 21 | }) async { 22 | final radioWriter = ref.watch(ackWaitingRadioWriterProvider); 23 | final myNodeNum = 24 | ref.watch(radioConfigServiceProvider.select((it) => it.myNodeNum)); 25 | final textMessageRepository = ref.watch(textMessageRepositoryProvider); 26 | final textMessageStreamService = 27 | ref.watch(textMessageStreamServiceProvider(chatType: chatType)); 28 | 29 | final message = TextMessage( 30 | text: text, 31 | from: myNodeNum, 32 | to: chatType.toNode, 33 | channel: chatType.channel, 34 | time: DateTime.now(), 35 | owner: myNodeNum, 36 | packetId: radioWriter.generatePacketId(), 37 | ); 38 | 39 | await textMessageRepository.add(textMessage: message); 40 | await textMessageStreamService.onNewMessage(message); 41 | 42 | await radioWriter.sendMeshPacket( 43 | channel: message.channel, 44 | to: message.to, 45 | wantAck: true, 46 | portNum: PortNum.TEXT_MESSAGE_APP, 47 | payload: utf8.encode(text), 48 | id: message.packetId, 49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /lib/providers/text_message/text_message_stream_service.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | import '../../models/chat_type.dart'; 5 | import '../../services/text_message/text_message_stream_service.dart'; 6 | import '../radio_config/radio_config_service.dart'; 7 | import '../repository/text_message_repository.dart'; 8 | import 'text_message_receiver_service.dart'; 9 | 10 | part 'text_message_stream_service.g.dart'; 11 | 12 | @Riverpod(keepAlive: true) 13 | TextMessageStreamService textMessageStreamService( 14 | TextMessageStreamServiceRef ref, { 15 | required ChatType chatType, 16 | }) { 17 | return TextMessageStreamService( 18 | chatType: chatType, 19 | myNodeNum: 20 | ref.watch(radioConfigServiceProvider.select((it) => it.myNodeNum)), 21 | textMessageRepository: ref.watch(textMessageRepositoryProvider), 22 | textMessageReceiverService: ref.watch(textMessageReceiverServiceProvider), 23 | onDispose: ref.onDispose, 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /lib/providers/traceroute.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:logger/logger.dart'; 4 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 5 | 6 | import '../models/traceroute_response.dart'; 7 | import '../protobufs/generated/meshtastic/mesh.pb.dart'; 8 | import '../protobufs/generated/meshtastic/portnums.pb.dart'; 9 | import 'ack_waiting_radio_writer.dart'; 10 | import 'node/node_service.dart'; 11 | import 'radio_reader.dart'; 12 | part 'traceroute.g.dart'; 13 | 14 | @riverpod 15 | class Traceroute extends _$Traceroute { 16 | @override 17 | TracerouteResponse build(int nodeNum) { 18 | final logger = Logger(); 19 | final radioWriter = ref.watch(ackWaitingRadioWriterProvider); 20 | final node = ref.read(nodeServiceProvider)[nodeNum]; 21 | final packetId = radioWriter.generatePacketId(); 22 | 23 | final listener = 24 | ref.watch(radioReaderProvider).onPacketReceived().listen((packet) { 25 | final decoded = packet.packet.decoded; 26 | if (decoded.requestId == packetId && 27 | decoded.portnum == PortNum.TRACEROUTE_APP) { 28 | final routing = RouteDiscovery.fromBuffer(decoded.payload); 29 | logger.i(routing); 30 | state = 31 | state.copyWith(successTime: DateTime.now(), route: routing.route); 32 | } 33 | }); 34 | 35 | ref.onDispose(listener.cancel); 36 | 37 | radioWriter.sendMeshPacket( 38 | to: nodeNum, 39 | channel: node?.channel ?? 0, 40 | portNum: PortNum.TRACEROUTE_APP, 41 | priority: MeshPacket_Priority.UNSET, 42 | wantResponse: true, 43 | id: packetId, 44 | ); 45 | 46 | final link = ref.keepAlive(); 47 | final timer = Timer(const Duration(minutes: 10), link.close); 48 | 49 | ref.onDispose(timer.cancel); 50 | 51 | return TracerouteResponse(attemptTime: DateTime.now()); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/providers/wrap/flutter_blue_plus_mockable.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | import '../../services/wrap/flutter_blue_plus_mockable.dart'; 5 | 6 | part 'flutter_blue_plus_mockable.g.dart'; 7 | 8 | @Riverpod(keepAlive: true) 9 | FlutterBluePlusMockable flutterBluePlus(FlutterBluePlusRef ref) { 10 | return FlutterBluePlusMockable(); 11 | } 12 | -------------------------------------------------------------------------------- /lib/providers/wrap/flutter_blue_plus_mockable.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'flutter_blue_plus_mockable.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$flutterBluePlusHash() => r'1226eabf0f75127e6a49ca2b2c15529146b1a1bd'; 10 | 11 | /// See also [flutterBluePlus]. 12 | @ProviderFor(flutterBluePlus) 13 | final flutterBluePlusProvider = Provider.internal( 14 | flutterBluePlus, 15 | name: r'flutterBluePlusProvider', 16 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 17 | ? null 18 | : _$flutterBluePlusHash, 19 | dependencies: null, 20 | allTransitiveDependencies: null, 21 | ); 22 | 23 | typedef FlutterBluePlusRef = ProviderRef; 24 | // ignore_for_file: type=lint 25 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 26 | -------------------------------------------------------------------------------- /lib/providers/wrap/local_platform.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | import 'package:platform/platform.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | 5 | part 'local_platform.g.dart'; 6 | 7 | @Riverpod(keepAlive: true) 8 | LocalPlatform localPlatform(LocalPlatformRef ref) { 9 | return const LocalPlatform(); 10 | } 11 | -------------------------------------------------------------------------------- /lib/providers/wrap/local_platform.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'local_platform.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$localPlatformHash() => r'f7dd04122f85ee2892eb39a4c6c81f0b30f8d5e5'; 10 | 11 | /// See also [localPlatform]. 12 | @ProviderFor(localPlatform) 13 | final localPlatformProvider = Provider.internal( 14 | localPlatform, 15 | name: r'localPlatformProvider', 16 | debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') 17 | ? null 18 | : _$localPlatformHash, 19 | dependencies: null, 20 | allTransitiveDependencies: null, 21 | ); 22 | 23 | typedef LocalPlatformRef = ProviderRef; 24 | // ignore_for_file: type=lint 25 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 26 | -------------------------------------------------------------------------------- /lib/providers/wrap/permissions.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | import '../../services/wrap/permissions.dart'; 5 | import 'local_platform.dart'; 6 | 7 | part 'permissions.g.dart'; 8 | 9 | @Riverpod(keepAlive: true) 10 | Permissions permissions(PermissionsRef ref) { 11 | return Permissions(localPlatform: ref.watch(localPlatformProvider)); 12 | } 13 | -------------------------------------------------------------------------------- /lib/providers/wrap/permissions.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'permissions.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$permissionsHash() => r'5f3f178de6159d8a7a8398d1ac3cfa20ca128d6c'; 10 | 11 | /// See also [permissions]. 12 | @ProviderFor(permissions) 13 | final permissionsProvider = Provider.internal( 14 | permissions, 15 | name: r'permissionsProvider', 16 | debugGetCreateSourceHash: 17 | const bool.fromEnvironment('dart.vm.product') ? null : _$permissionsHash, 18 | dependencies: null, 19 | allTransitiveDependencies: null, 20 | ); 21 | 22 | typedef PermissionsRef = ProviderRef; 23 | // ignore_for_file: type=lint 24 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 25 | -------------------------------------------------------------------------------- /lib/providers/wrap/socket.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | import '../../services/wrap/socket_mockable.dart'; 5 | part 'socket.g.dart'; 6 | 7 | @Riverpod(keepAlive: true) 8 | SocketMockable socket(SocketRef ref) { 9 | return SocketMockable(); 10 | } 11 | -------------------------------------------------------------------------------- /lib/providers/wrap/socket.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'socket.dart'; 4 | 5 | // ************************************************************************** 6 | // RiverpodGenerator 7 | // ************************************************************************** 8 | 9 | String _$socketHash() => r'89dd7fe7992657983e64d74ef7dfa785c7628854'; 10 | 11 | /// See also [socket]. 12 | @ProviderFor(socket) 13 | final socketProvider = Provider.internal( 14 | socket, 15 | name: r'socketProvider', 16 | debugGetCreateSourceHash: 17 | const bool.fromEnvironment('dart.vm.product') ? null : _$socketHash, 18 | dependencies: null, 19 | allTransitiveDependencies: null, 20 | ); 21 | 22 | typedef SocketRef = ProviderRef; 23 | // ignore_for_file: type=lint 24 | // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member 25 | -------------------------------------------------------------------------------- /lib/screens/channel_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | 4 | import '../providers/channel_service.dart'; 5 | import '../widgets/channel_card.dart'; 6 | 7 | class ChannelListScreen extends ConsumerWidget { 8 | const ChannelListScreen({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context, WidgetRef ref) { 12 | final channels = ref.watch(channelServiceProvider); 13 | return Scaffold( 14 | appBar: AppBar( 15 | title: const Text('Channel List'), 16 | ), 17 | body: Padding( 18 | padding: const EdgeInsets.symmetric(horizontal: 16), 19 | child: ListView.builder( 20 | itemCount: channels.length, 21 | itemBuilder: (context, index) { 22 | final channel = channels[index]; 23 | if (!channel.used) { 24 | return Container(); 25 | } 26 | return ChannelCard( 27 | channel: channel, 28 | index: index, 29 | ); 30 | }, 31 | ), 32 | ), 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/screens/chat.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | 4 | import '../models/chat_type.dart'; 5 | import '../providers/channel_service.dart'; 6 | import '../providers/node/node_service.dart'; 7 | import '../widgets/app_bar_connection_indicator.dart'; 8 | import '../widgets/message_input.dart'; 9 | import '../widgets/message_list.dart'; 10 | 11 | class ChatScreen extends ConsumerWidget { 12 | const ChatScreen({super.key, required ChatType chatType}) 13 | : _chatType = chatType; 14 | 15 | final ChatType _chatType; 16 | 17 | @override 18 | Widget build(BuildContext context, WidgetRef ref) { 19 | final channels = ref.watch(channelServiceProvider); 20 | final nodes = ref.watch(nodeServiceProvider); 21 | final title = switch (_chatType) { 22 | DirectMessageChat() => nodes[_chatType.toNode]?.longName ?? '', 23 | ChannelChat() => channels[_chatType.channel].name, 24 | }; 25 | if (_chatType is DirectMessageChat) { 26 | WidgetsBinding.instance.addPostFrameCallback((_) async { 27 | ref 28 | .read(nodeServiceProvider.notifier) 29 | .unsetHasUnreadMessages(_chatType.toNode); 30 | }); 31 | } 32 | return Scaffold( 33 | appBar: AppBarWithConnectionIndicator(title: title), 34 | body: Padding( 35 | padding: const EdgeInsets.only(left: 8, right: 8, bottom: 8), 36 | child: Column( 37 | children: [ 38 | MessageList( 39 | chatType: _chatType, 40 | ), 41 | MessageInput( 42 | chatType: _chatType, 43 | ), 44 | ], 45 | ), 46 | ), 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /lib/screens/map.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class MapScreen extends StatelessWidget { 4 | const MapScreen({super.key}); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return Scaffold( 9 | appBar: AppBar( 10 | title: const Text('Map'), 11 | ), 12 | body: Container(), 13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/screens/nodes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | 4 | import '../providers/node/node_search.dart'; 5 | import '../widgets/node_card.dart'; 6 | 7 | class NodesScreen extends ConsumerStatefulWidget { 8 | const NodesScreen({super.key}); 9 | 10 | @override 11 | ConsumerState createState() => _NodesScreenState(); 12 | } 13 | 14 | class _NodesScreenState extends ConsumerState { 15 | String _searchKey = ''; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | final nodes = ref.watch(nodeSearchProvider(_searchKey)).values.toList(); 20 | return Scaffold( 21 | appBar: AppBar( 22 | title: const Text('Nodes'), 23 | ), 24 | body: Padding( 25 | padding: const EdgeInsets.symmetric(horizontal: 16), 26 | child: Column( 27 | children: [ 28 | TextField( 29 | textInputAction: TextInputAction.search, 30 | onChanged: (value) => setState(() { 31 | _searchKey = value; 32 | }), 33 | decoration: const InputDecoration( 34 | hintText: 'Search node name', 35 | filled: true, 36 | ), 37 | ), 38 | const SizedBox( 39 | height: 16, 40 | ), 41 | Expanded( 42 | child: ListView.builder( 43 | itemCount: nodes.length, 44 | itemBuilder: (context, index) { 45 | final node = nodes[index]; 46 | return NodeCard( 47 | node: node, 48 | showChevron: true, 49 | ); 50 | }, 51 | ), 52 | ), 53 | ], 54 | ), 55 | ), 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/screens/utils/confirmation_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:go_router/go_router.dart'; 3 | 4 | Future showConfirmationDialog( 5 | BuildContext context, 6 | String msg, { 7 | String negative = 'Cancel', 8 | String positive = 'Continue', 9 | String title = 'Confirmation', 10 | }) async { 11 | if (!context.mounted) { 12 | return false; 13 | } 14 | return await showDialog( 15 | context: context, 16 | barrierDismissible: false, 17 | builder: (context) { 18 | return AlertDialog( 19 | title: Text(title), 20 | content: Text(msg), 21 | actions: [ 22 | TextButton( 23 | child: Text(negative), 24 | onPressed: () { 25 | context.pop(false); 26 | }, 27 | ), 28 | TextButton( 29 | child: Text(positive), 30 | onPressed: () { 31 | context.pop(true); 32 | }, 33 | ), 34 | ], 35 | ); 36 | }, 37 | ) ?? 38 | false; 39 | } 40 | -------------------------------------------------------------------------------- /lib/screens/utils/snackbar_message.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | void showSnackBarMessage( 4 | BuildContext context, 5 | String message, { 6 | bool error = false, 7 | }) { 8 | if (!context.mounted) { 9 | return; 10 | } 11 | final colorScheme = Theme.of(context).colorScheme; 12 | final snackBar = SnackBar( 13 | backgroundColor: error ? colorScheme.error : colorScheme.secondary, 14 | content: Text( 15 | message, 16 | style: Theme.of(context).textTheme.bodyLarge!.copyWith( 17 | color: error ? colorScheme.onError : colorScheme.onSecondary, 18 | ), 19 | ), 20 | ); 21 | ScaffoldMessenger.of(context).showSnackBar(snackBar); 22 | } 23 | -------------------------------------------------------------------------------- /lib/services/ble/ble_characteristics_finder.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_blue_plus/flutter_blue_plus.dart'; 2 | 3 | import '../../constants/ble_constants.dart'; 4 | import '../../exceptions/mesh_radio_exception.dart'; 5 | import '../../models/ble_characteristics.dart'; 6 | 7 | class BleCharacteristicsFinder { 8 | Future findCharacteristics(BluetoothDevice device) async { 9 | final services = await device.discoverServices(); 10 | final service = services.firstWhere( 11 | (element) => element.uuid.str == MESHTASTIC_BLE_SERVICE, 12 | orElse: () => 13 | throw const MeshRadioException(msg: 'Not a Meshtastic device'), 14 | ); 15 | 16 | final toRadio = service.characteristics.firstWhere( 17 | (element) => element.uuid.str == MESHTASTIC_TORADIO_CHARACTERISTIC, 18 | ); 19 | final fromRadio = service.characteristics.firstWhere( 20 | (element) => element.uuid.str == MESHTASTIC_FROMRADIO_CHARACTERISTIC, 21 | ); 22 | final fromNum = service.characteristics.firstWhere( 23 | (element) => element.uuid.str == MESHTASTIC_FROMNUM_CHARACTERISTIC, 24 | ); 25 | 26 | return BleCharacteristics( 27 | toRadio: toRadio, 28 | fromRadio: fromRadio, 29 | fromNum: fromNum, 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/services/ble/ble_permissions_requester.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | import 'package:platform/platform.dart'; 3 | 4 | import '../wrap/flutter_blue_plus_mockable.dart'; 5 | import '../wrap/permissions.dart'; 6 | 7 | class BlePermissionsRequester { 8 | BlePermissionsRequester({ 9 | required LocalPlatform localPlatform, 10 | required FlutterBluePlusMockable flutterBluePlus, 11 | required Permissions permissions, 12 | }) : _localPlatform = localPlatform, 13 | _flutterBluePlus = flutterBluePlus, 14 | _permissions = permissions; 15 | 16 | final LocalPlatform _localPlatform; 17 | final FlutterBluePlusMockable _flutterBluePlus; 18 | final Permissions _permissions; 19 | final _logger = Logger(); 20 | 21 | Future request() async { 22 | await _permissions.locationWhenInUseRequest(); 23 | if (_localPlatform.isAndroid) { 24 | try { 25 | await _flutterBluePlus.turnOn(); 26 | } catch (e) { 27 | _logger.e(e); 28 | return false; 29 | } 30 | } 31 | return true; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/services/ble/ble_radio_writer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_blue_plus/flutter_blue_plus.dart'; 2 | 3 | import '../../exceptions/mesh_radio_exception.dart'; 4 | import '../../models/radio_connector_state.dart'; 5 | import '../interfaces/radio_writer.dart'; 6 | 7 | class BleRadioWriter implements RadioWriter { 8 | BleRadioWriter(BleConnected connectorState) 9 | : _to = connectorState.bleCharacteristics.toRadio; 10 | 11 | final BluetoothCharacteristic _to; 12 | 13 | @override 14 | Future write(List value) async { 15 | try { 16 | await _to.write(value); 17 | } on FlutterBluePlusException catch (e) { 18 | throw MeshRadioException(msg: e.description); 19 | } catch (e) { 20 | throw const MeshRadioException(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/services/interfaces/radio_connector.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 2 | 3 | import '../../models/mesh_radio.dart'; 4 | import '../../models/radio_connector_state.dart'; 5 | 6 | abstract class RadioConnector 7 | implements Notifier { 8 | @override 9 | RadioConnectorState build(); 10 | 11 | Future disconnect({String? errorMsg}); 12 | 13 | Future connect(T radio); 14 | } 15 | -------------------------------------------------------------------------------- /lib/services/interfaces/radio_reader.dart: -------------------------------------------------------------------------------- 1 | import '../../protobufs/generated/meshtastic/mesh.pb.dart'; 2 | 3 | abstract class RadioReader { 4 | Stream onPacketReceived(); 5 | } 6 | 7 | abstract class ForceReadableRadioReader extends RadioReader { 8 | void forceRead(); 9 | } 10 | -------------------------------------------------------------------------------- /lib/services/interfaces/radio_writer.dart: -------------------------------------------------------------------------------- 1 | abstract class RadioWriter { 2 | Future write(List value); 3 | } 4 | -------------------------------------------------------------------------------- /lib/services/null/null_reader.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import '../../protobufs/generated/meshtastic/mesh.pb.dart'; 4 | import '../interfaces/radio_reader.dart'; 5 | 6 | class NullReader implements RadioReader { 7 | NullReader({ 8 | required void Function(void Function() cb) onDispose, 9 | }) { 10 | onDispose(_streamController.close); 11 | } 12 | 13 | // ignore: close_sinks 14 | final _streamController = StreamController.broadcast(); 15 | 16 | @override 17 | Stream onPacketReceived() => _streamController.stream; 18 | } 19 | -------------------------------------------------------------------------------- /lib/services/null/null_writer.dart: -------------------------------------------------------------------------------- 1 | import '../interfaces/radio_writer.dart'; 2 | 3 | class NullWriter implements RadioWriter { 4 | @override 5 | Future write(List value) async {} 6 | } 7 | -------------------------------------------------------------------------------- /lib/services/tcp/tcp_radio_writer.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import '../../constants/meshtastic_constants.dart'; 4 | import '../../models/radio_connector_state.dart'; 5 | import '../interfaces/radio_writer.dart'; 6 | 7 | class TcpRadioWriter implements RadioWriter { 8 | TcpRadioWriter(TcpConnected connectorState) : _socket = connectorState.socket; 9 | final IOSink _socket; 10 | @override 11 | Future write(List payload) async { 12 | final payloadLen = payload.length; 13 | final packet = [ 14 | MESHTASTIC_STREAM_START1, 15 | MESHTASTIC_STREAM_START2, 16 | payloadLen >> 8, 17 | payloadLen & 0xff, 18 | ...payload, 19 | ]; 20 | _socket.add(packet); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/services/wrap/permissions.dart: -------------------------------------------------------------------------------- 1 | import 'package:permission_handler/permission_handler.dart'; 2 | import 'package:platform/platform.dart'; 3 | 4 | class Permissions { 5 | Permissions({required LocalPlatform localPlatform}) 6 | : _localPlatform = localPlatform; 7 | 8 | final LocalPlatform _localPlatform; 9 | 10 | Future locationWhenInUseRequest() async { 11 | if (_localPlatform.isIOS || _localPlatform.isAndroid) { 12 | await Permission.locationWhenInUse.request(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/services/wrap/socket_mockable.dart: -------------------------------------------------------------------------------- 1 | // coverage:ignore-file 2 | import 'dart:io'; 3 | 4 | class SocketMockable { 5 | Future connect( 6 | dynamic host, 7 | int port, { 8 | dynamic sourceAddress, 9 | int sourcePort = 0, 10 | Duration? timeout, 11 | }) async { 12 | return Socket.connect( 13 | host, 14 | port, 15 | sourceAddress: sourceAddress, 16 | sourcePort: sourcePort, 17 | timeout: timeout, 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | const seedColor = Color(0x0067e491); 4 | 5 | final theme = ThemeData( 6 | colorScheme: ColorScheme.fromSeed(seedColor: seedColor), 7 | useMaterial3: true, 8 | ); 9 | 10 | final darkTheme = ThemeData( 11 | colorScheme: 12 | ColorScheme.fromSeed(seedColor: seedColor, brightness: Brightness.dark), 13 | useMaterial3: true, 14 | ); 15 | -------------------------------------------------------------------------------- /lib/utils/extensions.dart: -------------------------------------------------------------------------------- 1 | extension StringExtensions on String { 2 | String capitalize() { 3 | if (isEmpty) return this; 4 | return '${this[0].toUpperCase()}${substring(1).toLowerCase()}'; 5 | } 6 | 7 | String snakeToUpperCamelCase() { 8 | final parts = split('_'); 9 | return parts.map((part) => part.capitalize()).join(); 10 | } 11 | } 12 | 13 | extension TempConversion on double { 14 | double cToF() { 15 | return (1.8 * this) + 32; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/widgets/app_bar_connection_indicator.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | 4 | import '../models/radio_connector_state.dart'; 5 | import '../providers/radio_connector_service.dart'; 6 | 7 | class AppBarWithConnectionIndicator extends ConsumerWidget 8 | implements PreferredSizeWidget { 9 | const AppBarWithConnectionIndicator({ 10 | super.key, 11 | required this.title, 12 | }); 13 | 14 | final String title; 15 | 16 | @override 17 | Widget build(BuildContext context, WidgetRef ref) { 18 | final radioConnectorState = ref.watch(radioConnectorServiceProvider); 19 | return AppBar( 20 | title: Text(title), 21 | actions: [ 22 | Padding( 23 | padding: const EdgeInsets.symmetric(horizontal: 25), 24 | child: radioConnectorState is Connected 25 | ? Icon( 26 | Icons.cloud_done_outlined, 27 | color: Theme.of(context).colorScheme.primary, 28 | ) 29 | : Icon( 30 | Icons.cloud_off, 31 | color: Theme.of(context).colorScheme.error, 32 | ), 33 | ), 34 | ], 35 | ); 36 | } 37 | 38 | @override 39 | Size get preferredSize => const Size.fromHeight(kToolbarHeight); 40 | } 41 | -------------------------------------------------------------------------------- /lib/widgets/channel_qr_show.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:qr_flutter/qr_flutter.dart'; 4 | 5 | class ChannelQrShow extends StatefulWidget { 6 | const ChannelQrShow(this.url, {super.key}); 7 | 8 | final String url; 9 | @override 10 | State createState() => _ChannelQrShowState(); 11 | } 12 | 13 | class _ChannelQrShowState extends State { 14 | final _urlController = TextEditingController(); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | _urlController.text = '${widget.url.substring(0, 35)}...'; 19 | return Column( 20 | children: [ 21 | QrImageView( 22 | backgroundColor: Colors.white, 23 | data: widget.url, 24 | size: 300, 25 | ), 26 | const SizedBox( 27 | height: 8, 28 | ), 29 | InkWell( 30 | onTap: () async { 31 | await Clipboard.setData(ClipboardData(text: widget.url)); 32 | }, 33 | child: Column( 34 | children: [ 35 | TextField( 36 | enabled: false, 37 | controller: _urlController, 38 | textAlign: TextAlign.center, 39 | ), 40 | const SizedBox(height: 8), 41 | Text( 42 | 'Tap to Copy URL', 43 | style: Theme.of(context).textTheme.labelSmall, 44 | ), 45 | ], 46 | ), 47 | ), 48 | ], 49 | ); 50 | } 51 | 52 | @override 53 | void dispose() { 54 | _urlController.dispose(); 55 | super.dispose(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/widgets/connection_icon.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | 4 | import '../models/radio_connector_state.dart'; 5 | import '../providers/radio_config/radio_config_service.dart'; 6 | import '../providers/radio_connector_service.dart'; 7 | 8 | class ConnectionIcon extends ConsumerWidget { 9 | const ConnectionIcon(this._radioId, {super.key}); 10 | 11 | final String _radioId; 12 | 13 | @override 14 | Widget build(BuildContext context, WidgetRef ref) { 15 | final radioConfig = ref.watch(radioConfigServiceProvider); 16 | final radioConnectorState = ref.watch(radioConnectorServiceProvider); 17 | 18 | if (radioConnectorState is WithRadio && 19 | radioConnectorState.radioId != _radioId) { 20 | return const SizedBox( 21 | width: 1, 22 | ); 23 | } 24 | 25 | const loadingIndicator = Column( 26 | mainAxisAlignment: MainAxisAlignment.center, 27 | children: [ 28 | SizedBox( 29 | height: 20, 30 | width: 20, 31 | child: CircularProgressIndicator( 32 | strokeWidth: 3, 33 | ), 34 | ), 35 | ], 36 | ); 37 | 38 | switch (radioConnectorState) { 39 | case Connected(): 40 | if (!radioConfig.configDownloaded) { 41 | return loadingIndicator; 42 | } else { 43 | return Icon( 44 | Icons.circle, 45 | color: Theme.of(context).colorScheme.primary, 46 | ); 47 | } 48 | case ConnectionError(): 49 | return Icon( 50 | Icons.circle, 51 | color: Theme.of(context).colorScheme.error, 52 | ); 53 | case Connecting(): 54 | return loadingIndicator; 55 | default: 56 | return const SizedBox( 57 | height: 1, 58 | ); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/widgets/message_input.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | 4 | import '../models/chat_type.dart'; 5 | import '../providers/text_message/text_message_sender_service.dart'; 6 | 7 | class MessageInput extends ConsumerStatefulWidget { 8 | const MessageInput({ 9 | required this.chatType, 10 | super.key, 11 | }); 12 | 13 | final ChatType chatType; 14 | 15 | @override 16 | ConsumerState createState() => _MessageInputState(); 17 | } 18 | 19 | class _MessageInputState extends ConsumerState { 20 | final _textEditingController = TextEditingController(); 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return Row( 25 | children: [ 26 | Expanded( 27 | child: TextField( 28 | controller: _textEditingController, 29 | textInputAction: TextInputAction.send, 30 | onSubmitted: (_) => _send(), 31 | decoration: const InputDecoration(hintText: 'Input message...'), 32 | ), 33 | ), 34 | IconButton( 35 | onPressed: _send, 36 | icon: const Icon( 37 | Icons.send, 38 | size: 35, 39 | ), 40 | ), 41 | ], 42 | ); 43 | } 44 | 45 | void _send() { 46 | final text = _textEditingController.text.trim(); 47 | _textEditingController.clear(); 48 | if (text.isNotEmpty) { 49 | ref.read( 50 | sendTextMessageProvider( 51 | chatType: widget.chatType, 52 | text: text, 53 | ), 54 | ); 55 | } 56 | } 57 | 58 | @override 59 | void dispose() { 60 | _textEditingController.dispose(); 61 | super.dispose(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/widgets/radio_choice_tile.dart: -------------------------------------------------------------------------------- 1 | import 'package:easy_debounce/easy_throttle.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 4 | 5 | import '../models/mesh_radio.dart'; 6 | import '../models/radio_connector_state.dart'; 7 | import '../providers/radio_connector_service.dart'; 8 | import '../providers/reconnector.dart'; 9 | import 'connection_icon.dart'; 10 | 11 | class RadioChoiceTile extends ConsumerWidget { 12 | const RadioChoiceTile({ 13 | super.key, 14 | required MeshRadio radio, 15 | }) : _radio = radio; 16 | 17 | final MeshRadio _radio; 18 | 19 | @override 20 | Widget build(BuildContext context, WidgetRef ref) { 21 | final connectorState = ref.watch(radioConnectorServiceProvider); 22 | 23 | return ListTile( 24 | leading: ConnectionIcon(_radio.remoteId), 25 | onTap: () async { 26 | if (connectorState is Connecting) { 27 | return; 28 | } 29 | EasyThrottle.throttle( 30 | 'connect-throttler', 31 | const Duration(milliseconds: 1000), 32 | () async { 33 | ref 34 | .read(reconnectorServiceProvider().notifier) 35 | .disableReconnectUntilNextDisconnect(); 36 | await ref.read(radioConnectorServiceProvider.notifier).disconnect(); 37 | await ref 38 | .read(radioConnectorServiceProvider.notifier) 39 | .connect(_radio); 40 | }, 41 | ); 42 | }, 43 | title: Text(_radio.name), 44 | visualDensity: const VisualDensity(vertical: -2), 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/widgets/scroll_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ScrollButton extends StatelessWidget { 4 | const ScrollButton({ 5 | super.key, 6 | required void Function() scrollToBottom, 7 | required bool showNewMessageAlert, 8 | }) : _showNewMessageAlert = showNewMessageAlert, 9 | _scrollToBottom = scrollToBottom; 10 | 11 | final void Function() _scrollToBottom; 12 | final bool _showNewMessageAlert; 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Stack( 17 | children: [ 18 | CircleAvatar( 19 | backgroundColor: Theme.of(context).colorScheme.primaryContainer, 20 | radius: 30, 21 | child: IconButton( 22 | color: Theme.of(context).colorScheme.onPrimaryContainer, 23 | onPressed: _scrollToBottom, 24 | icon: const Icon( 25 | Icons.arrow_downward, 26 | size: 30, 27 | ), 28 | ), 29 | ), 30 | if (_showNewMessageAlert) 31 | Positioned( 32 | right: 0, 33 | child: Icon( 34 | size: 20, 35 | Icons.message_rounded, 36 | color: Theme.of(context).colorScheme.error, 37 | ), 38 | ), 39 | ], 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/widgets/text_message_status_indicator.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 3 | 4 | import '../models/text_message.dart'; 5 | import '../models/text_message_status.dart'; 6 | import '../providers/text_message/text_message_status_service.dart'; 7 | 8 | class TextMessageStatusIndicator extends ConsumerWidget { 9 | const TextMessageStatusIndicator({ 10 | super.key, 11 | required TextMessage textMessage, 12 | }) : _textMessage = textMessage; 13 | 14 | final TextMessage _textMessage; 15 | 16 | @override 17 | Widget build(BuildContext context, WidgetRef ref) { 18 | late TextMessageStatus? state; 19 | if (_textMessage.state != TextMessageStatus.SENDING) { 20 | state = _textMessage.state; 21 | } else { 22 | final statusUpdate = ref.watch( 23 | textMessageStatusServiceProvider(packetId: _textMessage.packetId), 24 | ); 25 | state = switch (statusUpdate) { 26 | AsyncValue(:final valueOrNull?) => valueOrNull.state, 27 | _ => null 28 | }; 29 | } 30 | 31 | final icon = switch (state) { 32 | TextMessageStatus.RADIO_ERROR => Icons.error_outline, 33 | TextMessageStatus.OK => Icons.check_circle, 34 | TextMessageStatus.RECVD_BY_RADIO => Icons.check_circle_outline, 35 | _ => null, 36 | }; 37 | 38 | return Icon( 39 | icon, 40 | size: 15, 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import file_saver 9 | import firebase_analytics 10 | import firebase_core 11 | import firebase_crashlytics 12 | import flutter_blue_plus 13 | import flutter_local_notifications 14 | import mobile_scanner 15 | import package_info_plus 16 | import path_provider_foundation 17 | import shared_preferences_foundation 18 | import sqflite 19 | 20 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 21 | FileSaverPlugin.register(with: registry.registrar(forPlugin: "FileSaverPlugin")) 22 | FLTFirebaseAnalyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAnalyticsPlugin")) 23 | FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) 24 | FLTFirebaseCrashlyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCrashlyticsPlugin")) 25 | FlutterBluePlusPlugin.register(with: registry.registrar(forPlugin: "FlutterBluePlusPlugin")) 26 | FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) 27 | MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin")) 28 | FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) 29 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) 30 | SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) 31 | SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) 32 | } 33 | -------------------------------------------------------------------------------- /macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.14' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) 34 | target 'RunnerTests' do 35 | inherit! :search_paths 36 | end 37 | end 38 | 39 | post_install do |installer| 40 | installer.pods_project.targets.each do |target| 41 | flutter_additional_macos_build_settings(target) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /macos/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - flutter_blue_plus (0.0.1): 3 | - FlutterMacOS 4 | - flutter_local_notifications (0.0.1): 5 | - FlutterMacOS 6 | - FlutterMacOS (1.0.0) 7 | - mobile_scanner (3.5.6): 8 | - FlutterMacOS 9 | - shared_preferences_foundation (0.0.1): 10 | - Flutter 11 | - FlutterMacOS 12 | - sqflite (0.0.3): 13 | - Flutter 14 | - FlutterMacOS 15 | 16 | DEPENDENCIES: 17 | - flutter_blue_plus (from `Flutter/ephemeral/.symlinks/plugins/flutter_blue_plus/macos`) 18 | - flutter_local_notifications (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos`) 19 | - FlutterMacOS (from `Flutter/ephemeral`) 20 | - mobile_scanner (from `Flutter/ephemeral/.symlinks/plugins/mobile_scanner/macos`) 21 | - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) 22 | - sqflite (from `Flutter/ephemeral/.symlinks/plugins/sqflite/darwin`) 23 | 24 | EXTERNAL SOURCES: 25 | flutter_blue_plus: 26 | :path: Flutter/ephemeral/.symlinks/plugins/flutter_blue_plus/macos 27 | flutter_local_notifications: 28 | :path: Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos 29 | FlutterMacOS: 30 | :path: Flutter/ephemeral 31 | mobile_scanner: 32 | :path: Flutter/ephemeral/.symlinks/plugins/mobile_scanner/macos 33 | shared_preferences_foundation: 34 | :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin 35 | sqflite: 36 | :path: Flutter/ephemeral/.symlinks/plugins/sqflite/darwin 37 | 38 | SPEC CHECKSUMS: 39 | flutter_blue_plus: e2868679021f19d12a8c0c58a33f1df66ec7fa6a 40 | flutter_local_notifications: 3805ca215b2fb7f397d78b66db91f6a747af52e4 41 | FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 42 | mobile_scanner: 54ceceae0c8da2457e26a362a6be5c61154b1829 43 | shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 44 | sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec 45 | 46 | PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 47 | 48 | COCOAPODS: 1.15.0 49 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = meshx 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = xyz.paulocode.meshx 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2024 xyz.paulocode. All rights reserved. 15 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.device.bluetooth 10 | 11 | com.apple.security.network.client 12 | 13 | com.apple.security.network.server 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /macos/Runner/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | API_KEY 6 | AIzaSyCm2mkgrd8WFEj81l8ix16I5QUCkqUap_U 7 | GCM_SENDER_ID 8 | 194789291232 9 | PLIST_VERSION 10 | 1 11 | BUNDLE_ID 12 | xyz.paulocode.meshx 13 | PROJECT_ID 14 | multimesh-9b515 15 | STORAGE_BUCKET 16 | multimesh-9b515.appspot.com 17 | IS_ADS_ENABLED 18 | 19 | IS_ANALYTICS_ENABLED 20 | 21 | IS_APPINVITE_ENABLED 22 | 23 | IS_GCM_ENABLED 24 | 25 | IS_SIGNIN_ENABLED 26 | 27 | GOOGLE_APP_ID 28 | 1:194789291232:ios:b601f3edbb6827e17c8c85 29 | 30 | -------------------------------------------------------------------------------- /macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import FlutterMacOS 2 | import Cocoa 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /macos/firebase_app_id_file.json: -------------------------------------------------------------------------------- 1 | { 2 | "file_generated_by": "FlutterFire CLI", 3 | "purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory", 4 | "GOOGLE_APP_ID": "1:194789291232:ios:93b753af9ce7084e7c8c85", 5 | "FIREBASE_PROJECT_ID": "multimesh-9b515", 6 | "GCM_SENDER_ID": "194789291232" 7 | } -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: multimesh 2 | description: "A cross-platform Meshtastic client based on Flutter" 3 | 4 | publish_to: 'none' 5 | 6 | version: 0.2.4+21 7 | 8 | environment: 9 | sdk: '>=3.2.3 <4.0.0' 10 | 11 | dependencies: 12 | chat_bubbles: ^1.6.0 13 | convert: ^3.1.1 14 | crypto: ^3.0.3 15 | csv: ^5.1.1 16 | easy_debounce: ^2.0.3 17 | file_saver: ^0.2.12 18 | firebase_analytics: ^10.10.1 19 | firebase_core: ^2.29.0 20 | firebase_crashlytics: ^3.5.1 21 | flutter: 22 | sdk: flutter 23 | flutter_blue_plus: ^1.31.7 24 | flutter_foreground_task: ^6.1.3 25 | flutter_local_notifications: ^17.0.0 26 | flutter_native_splash: ^2.3.10 27 | flutter_riverpod: ^2.4.9 28 | flutter_svg: ^2.0.9 29 | freezed_annotation: ^2.4.1 30 | go_router: ^13.2.0 31 | http: ^1.2.0 32 | intl: ^0.19.0 33 | json_annotation: ^4.8.1 34 | logger: ^2.0.2+1 35 | mobile_scanner: ^4.0.1 36 | mockito: ^5.4.4 37 | package_info_plus: ^8.0.0 38 | path: ^1.9.0 39 | permission_handler: ^11.1.0 40 | platform: ^3.1.4 41 | protobuf: ^3.1.0 42 | qr_flutter: ^4.1.0 43 | riverpod_annotation: ^2.3.3 44 | scrollview_observer: ^1.19.0 45 | shared_preferences: ^2.2.2 46 | shimmer: ^3.0.0 47 | sqflite: ^2.3.2 48 | sqflite_common_ffi: ^2.3.3 49 | tuple: ^2.0.2 50 | 51 | dev_dependencies: 52 | build_runner: ^2.4.8 53 | custom_lint: ^0.6.2 54 | flutter_launcher_icons: "^0.13.1" 55 | flutter_lints: ^3.0.1 56 | flutter_test: 57 | sdk: flutter 58 | freezed: ^2.4.6 59 | json_serializable: ^6.7.1 60 | riverpod_generator: ^2.3.9 61 | riverpod_lint: ^2.3.8 62 | test: ^1.24.9 63 | 64 | flutter: 65 | uses-material-design: true 66 | assets: 67 | - assets/ 68 | 69 | flutter_launcher_icons: 70 | android: "ic_launcher" 71 | ios: true 72 | image_path: "assets/meshx.png" 73 | min_sdk_android: 21 74 | remove_alpha_ios: true 75 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=meshx 2 | sonar.projectName=meshx 3 | sonar.projectVersion=0.1.0 4 | 5 | sonar.sources=lib,pubspec.yaml 6 | sonar.tests=test 7 | sonar.sourceEncoding=UTF-8 8 | 9 | sonar.exclusions=test/**/*_test.mocks.dart,lib/**/*.g.dart,lib/**/*.freezed.dart,lib/protobufs/generated/** 10 | -------------------------------------------------------------------------------- /test/common.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | ProviderContainer createContainer({ 5 | ProviderContainer? parent, 6 | List overrides = const [], 7 | List? observers, 8 | }) { 9 | final container = ProviderContainer( 10 | parent: parent, 11 | overrides: overrides, 12 | observers: observers, 13 | ); 14 | 15 | addTearDown(container.dispose); 16 | 17 | return container; 18 | } 19 | 20 | abstract class ShowNotification { 21 | Future showNotification({ 22 | required String title, 23 | required String text, 24 | required String callbackValue, 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /test/providers/node/node_search_test.dart: -------------------------------------------------------------------------------- 1 | // TODO 2 | void main() {} 3 | -------------------------------------------------------------------------------- /test/providers/node/node_service_test.mocks.dart: -------------------------------------------------------------------------------- 1 | // Mocks generated by Mockito 5.4.4 from annotations 2 | // in multimesh/test/providers/node/node_service_test.dart. 3 | // Do not manually edit this file. 4 | 5 | // ignore_for_file: no_leading_underscores_for_library_prefixes 6 | import 'dart:async' as _i3; 7 | 8 | import 'package:mockito/mockito.dart' as _i1; 9 | import 'package:multimesh/protobufs/generated/meshtastic/mesh.pb.dart' as _i4; 10 | import 'package:multimesh/services/interfaces/radio_reader.dart' as _i2; 11 | 12 | // ignore_for_file: type=lint 13 | // ignore_for_file: avoid_redundant_argument_values 14 | // ignore_for_file: avoid_setters_without_getters 15 | // ignore_for_file: comment_references 16 | // ignore_for_file: deprecated_member_use 17 | // ignore_for_file: deprecated_member_use_from_same_package 18 | // ignore_for_file: implementation_imports 19 | // ignore_for_file: invalid_use_of_visible_for_testing_member 20 | // ignore_for_file: prefer_const_constructors 21 | // ignore_for_file: unnecessary_parenthesis 22 | // ignore_for_file: camel_case_types 23 | // ignore_for_file: subtype_of_sealed_class 24 | 25 | /// A class which mocks [RadioReader]. 26 | /// 27 | /// See the documentation for Mockito's code generation for more information. 28 | class MockRadioReader extends _i1.Mock implements _i2.RadioReader { 29 | MockRadioReader() { 30 | _i1.throwOnMissingStub(this); 31 | } 32 | 33 | @override 34 | _i3.Stream<_i4.FromRadio> onPacketReceived() => (super.noSuchMethod( 35 | Invocation.method( 36 | #onPacketReceived, 37 | [], 38 | ), 39 | returnValue: _i3.Stream<_i4.FromRadio>.empty(), 40 | ) as _i3.Stream<_i4.FromRadio>); 41 | } 42 | -------------------------------------------------------------------------------- /test/providers/radio_config/radio_config_service_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_riverpod/flutter_riverpod.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | import 'package:mockito/annotations.dart'; 4 | import 'package:multimesh/protobufs/generated/meshtastic/config.pb.dart'; 5 | import 'package:multimesh/providers/radio_config/radio_config_service.dart'; 6 | import 'package:multimesh/services/radio_config/radio_config_uploader_service.dart'; 7 | 8 | import '../../common.dart'; 9 | 10 | @GenerateMocks([RadioConfigUploaderService]) 11 | void main() { 12 | late ProviderContainer container; 13 | 14 | setUp(() { 15 | container = createContainer(); 16 | }); 17 | 18 | test('set lora config', () { 19 | final loraConfig = Config_LoRaConfig(); 20 | container 21 | .read(radioConfigServiceProvider.notifier) 22 | .setLoraConfig(loraConfig); 23 | 24 | expect(container.read(radioConfigServiceProvider).loraConfig, loraConfig); 25 | }); 26 | 27 | test('clear config', () { 28 | final config1 = container.read(radioConfigServiceProvider); 29 | container.read(radioConfigServiceProvider.notifier).clear(); 30 | final config2 = container.read(radioConfigServiceProvider); 31 | 32 | expect(config1, equals(config2)); 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /test/services/tcp/tcp_radio_writer_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter_test/flutter_test.dart'; 4 | import 'package:mockito/annotations.dart'; 5 | import 'package:mockito/mockito.dart'; 6 | import 'package:multimesh/constants/meshtastic_constants.dart'; 7 | import 'package:multimesh/models/mesh_radio.dart'; 8 | import 'package:multimesh/models/radio_connector_state.dart'; 9 | import 'package:multimesh/services/tcp/tcp_radio_writer.dart'; 10 | 11 | import 'tcp_radio_writer_test.mocks.dart'; 12 | 13 | @GenerateMocks([Socket]) 14 | void main() { 15 | late MockSocket socket; // ignore: close_sinks 16 | late TcpRadioWriter tcpRadioWriter; 17 | 18 | setUp(() { 19 | socket = MockSocket(); 20 | tcpRadioWriter = TcpRadioWriter( 21 | TcpConnected( 22 | socket: socket, 23 | recvStream: socket, 24 | radio: TcpMeshRadio(address: 'address'), 25 | ), 26 | ); 27 | }); 28 | 29 | test('write', () async { 30 | await tcpRadioWriter.write([1, 7, 2, 9]); 31 | verify( 32 | socket.add([ 33 | MESHTASTIC_STREAM_START1, 34 | MESHTASTIC_STREAM_START2, 35 | 0, // first byte of length 36 | 4, // second byte of length 37 | 1, // payload 38 | 7, 39 | 2, 40 | 9, 41 | ]), 42 | ); 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | void RegisterPlugins(flutter::PluginRegistry* registry) { 14 | FileSaverPluginRegisterWithRegistrar( 15 | registry->GetRegistrarForPlugin("FileSaverPlugin")); 16 | FirebaseCorePluginCApiRegisterWithRegistrar( 17 | registry->GetRegistrarForPlugin("FirebaseCorePluginCApi")); 18 | PermissionHandlerWindowsPluginRegisterWithRegistrar( 19 | registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); 20 | } 21 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | file_saver 7 | firebase_core 8 | permission_handler_windows 9 | ) 10 | 11 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 12 | ) 13 | 14 | set(PLUGIN_BUNDLED_LIBRARIES) 15 | 16 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 17 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 18 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 19 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 20 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 21 | endforeach(plugin) 22 | 23 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 24 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 25 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 26 | endforeach(ffi_plugin) 27 | -------------------------------------------------------------------------------- /windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} WIN32 10 | "flutter_window.cpp" 11 | "main.cpp" 12 | "utils.cpp" 13 | "win32_window.cpp" 14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 15 | "Runner.rc" 16 | "runner.exe.manifest" 17 | ) 18 | 19 | # Apply the standard set of build settings. This can be removed for applications 20 | # that need different build settings. 21 | apply_standard_settings(${BINARY_NAME}) 22 | 23 | # Add preprocessor definitions for the build version. 24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") 25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") 26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") 27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") 28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") 29 | 30 | # Disable Windows macros that collide with C++ standard library functions. 31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 32 | 33 | # Add dependency libraries and include directories. Add any application-specific 34 | # dependencies here. 35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") 37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 38 | 39 | # Run the Flutter tool portions of the build. This must not be removed. 40 | add_dependencies(${BINARY_NAME} flutter_assemble) 41 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.Create(L"multimesh", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paulocode/multimesh/d88a55776bd962225c6114673b3f20066e73f34f/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | --------------------------------------------------------------------------------