diff --git a/.github/workflows/on_pr.yaml b/.github/workflows/on_pr.yaml index a117fd8b7..74e308195 100644 --- a/.github/workflows/on_pr.yaml +++ b/.github/workflows/on_pr.yaml @@ -18,7 +18,7 @@ jobs: java-version: "12.x" - uses: subosito/flutter-action@v1 with: - flutter-version: '2.5.3' + channel: "stable" - name: Get dependencies run: flutter packages get - name: Check format diff --git a/CHANGELOG.md b/CHANGELOG.md index 838bbd31c..e350a096d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.10.0 + +First open source release ! :rocket: + +- add missing documentation +- remove unused API and dependency +- refresh `example/`, `ios/` and `android/` folders to be up to date with the latest Flutter version (2.10.3) + ## 0.9.1 - revert app's manifest changes diff --git a/LICENSE b/LICENSE index ba75c69f7..3a54095d4 100644 --- a/LICENSE +++ b/LICENSE @@ -1 +1,29 @@ -TODO: Add your license here. +BSD 3-Clause License + +Copyright (c) 2022, OZEO-DOOZ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index 70505cf98..5ec8a68fe 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,7 @@ # nordic_nrf_mesh +A Flutter plugin to enable mesh network management and communication using Nordic's SDKs. It also provides the ability to open BLE connection with mesh nodes using some other Flutter [plugin](https://pub.dev/packages/flutter_reactive_ble). -A new flutter plugin project. +## How to use +_This section will be updated soon !_ -## Getting Started - -This project is a starting point for a Flutter -[plug-in package](https://flutter.dev/developing-packages/), -a specialized package that includes platform-specific implementation code for -Android and/or iOS. - -For help getting started with Flutter, view our -[online documentation](https://flutter.dev/docs), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +Please refer to example app. diff --git a/analysis_options.yaml b/analysis_options.yaml index 8956af259..9fbbf911a 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -4,3 +4,7 @@ analyzer: exclude: - lib/**/*.g.dart - lib/**/*.freezed.dart +linter: + rules: + # futures should be either awaited or clearly marked as unawaited + - unawaited_futures diff --git a/android/src/main/kotlin/fr/dooz/nordic_nrf_mesh/NordicNrfMeshPlugin.kt b/android/src/main/kotlin/fr/dooz/nordic_nrf_mesh/NordicNrfMeshPlugin.kt index 69962559a..3c16a78e5 100644 --- a/android/src/main/kotlin/fr/dooz/nordic_nrf_mesh/NordicNrfMeshPlugin.kt +++ b/android/src/main/kotlin/fr/dooz/nordic_nrf_mesh/NordicNrfMeshPlugin.kt @@ -32,6 +32,7 @@ class NordicNrfMeshPlugin: FlutterPlugin, MethodCallHandler { methodChannel = MethodChannel(binaryMessenger, "$namespace/methods") flutterBinding = flutterPluginBinding; methodChannel.setMethodCallHandler(this); + meshManagerApi = DoozMeshManagerApi(flutterBinding.applicationContext, binaryMessenger) } companion object { @@ -48,10 +49,6 @@ class NordicNrfMeshPlugin: FlutterPlugin, MethodCallHandler { "getPlatformVersion" -> { result.success("Android ${android.os.Build.VERSION.RELEASE}") } - "createMeshManagerApi" -> { - meshManagerApi = DoozMeshManagerApi(flutterBinding.applicationContext, binaryMessenger) - result.success(null) - } else -> { result.notImplemented() } diff --git a/example/android/.gitignore b/example/android/.gitignore index bc2100d8f..6f568019d 100644 --- a/example/android/.gitignore +++ b/example/android/.gitignore @@ -5,3 +5,9 @@ gradle-wrapper.jar /gradlew.bat /local.properties GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 202c0ece3..f4047005e 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -22,29 +22,31 @@ if (flutterVersionName == null) { } apply plugin: 'com.android.application' -apply plugin: 'com.google.gms.google-services' -apply plugin: 'com.google.firebase.crashlytics' apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 29 + compileSdkVersion flutter.compileSdkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } sourceSets { main.java.srcDirs += 'src/main/kotlin' } - lintOptions { - disable 'InvalidPackage' - } - defaultConfig { applicationId "fr.dooz.nordic_nrf_mesh_example" minSdkVersion 23 - targetSdkVersion 29 + targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName - multiDexEnabled true } buildTypes { @@ -52,8 +54,6 @@ android { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. signingConfig signingConfigs.debug - minifyEnabled false - shrinkResources false } } } @@ -63,9 +63,6 @@ flutter { } dependencies { - implementation platform('com.google.firebase:firebase-bom:25.12.0') - implementation 'com.google.firebase:firebase-analytics-ktx' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'androidx.multidex:multidex:2.0.1' implementation project(':mesh') } diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 67a32aa0c..c2edd26e5 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,23 +1,12 @@ - - - - - - - - - - - diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/example/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 000000000..f74085f3f --- /dev/null +++ b/example/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/example/android/app/src/main/res/values-night/styles.xml b/example/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 000000000..3db14bb53 --- /dev/null +++ b/example/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml index 1f83a33fd..d460d1e92 100644 --- a/example/android/app/src/main/res/values/styles.xml +++ b/example/android/app/src/main/res/values/styles.xml @@ -1,7 +1,7 @@ - - diff --git a/example/android/build.gradle b/example/android/build.gradle index d610b0621..4256f9173 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,15 +1,13 @@ buildscript { - ext.kotlin_version = '1.3.50' + ext.kotlin_version = '1.6.10' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.3' + classpath 'com.android.tools.build:gradle:4.1.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.google.gms:google-services:4.3.3' - classpath 'com.google.firebase:firebase-crashlytics-gradle:2.3.0' } } diff --git a/example/android/gradle.properties b/example/android/gradle.properties index b83582f26..e52b37db6 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1,5 +1,4 @@ org.gradle.jvmargs=-Xmx1536M -android.enableR8=true android.useAndroidX=true android.enableJetifier=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index ed1489c28..bc6a58afd 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Apr 06 16:02:06 CEST 2021 +#Fri Jun 23 08:50:38 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle index 88787ff3f..6afee8623 100644 --- a/example/android/settings.gradle +++ b/example/android/settings.gradle @@ -1,16 +1,13 @@ include ':app' include ':mesh' project(':mesh').projectDir = file('../../Android-nRF-Mesh-Library-1/mesh') -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } -} +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory -} +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/example/android/settings_aar.gradle b/example/android/settings_aar.gradle deleted file mode 100644 index e7b4def49..000000000 --- a/example/android/settings_aar.gradle +++ /dev/null @@ -1 +0,0 @@ -include ':app' diff --git a/example/ios/.gitignore b/example/ios/.gitignore index b53b9af33..7a7f9873a 100644 --- a/example/ios/.gitignore +++ b/example/ios/.gitignore @@ -1,3 +1,4 @@ +**/dgph *.mode1v3 *.mode2v3 *.moved-aside @@ -18,17 +19,16 @@ Flutter/App.framework Flutter/Flutter.framework Flutter/Flutter.podspec Flutter/Generated.xcconfig +Flutter/ephemeral/ Flutter/app.flx Flutter/app.zip Flutter/flutter_assets/ Flutter/flutter_export_environment.sh ServiceDefinitions.json Runner/GeneratedPluginRegistrant.* -Runner/GoogleService-Info.plist # Exceptions to above rules. !default.mode1v3 !default.mode2v3 !default.pbxuser !default.perspectivev3 - diff --git a/example/ios/Flutter/.last_build_id b/example/ios/Flutter/.last_build_id deleted file mode 100644 index e95a63e53..000000000 --- a/example/ios/Flutter/.last_build_id +++ /dev/null @@ -1 +0,0 @@ -c45bc4e85553de00d3793a02573d1026 \ No newline at end of file diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index f2872cf47..9625e105d 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) + en CFBundleExecutable App CFBundleIdentifier @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 9.0 + 11.0 diff --git a/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig index e8efba114..ec97fc6f3 100644 --- a/example/ios/Flutter/Debug.xcconfig +++ b/example/ios/Flutter/Debug.xcconfig @@ -1,2 +1,2 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/example/ios/Flutter/Release.xcconfig b/example/ios/Flutter/Release.xcconfig index 399e9340e..c4855bfe2 100644 --- a/example/ios/Flutter/Release.xcconfig +++ b/example/ios/Flutter/Release.xcconfig @@ -1,2 +1,2 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/example/ios/Podfile b/example/ios/Podfile index b329adbf5..5315df336 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project - platform :ios, '11.0' +platform :ios, '11.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -27,11 +27,12 @@ require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelpe flutter_ios_podfile_setup - target 'Runner' do use_frameworks! use_modular_headers! - + #using static library linkage (to be able to use nRFMeshProvision when it is a git submodule of nrf_mesh_plugin) + use_frameworks! :linkage => :static + pod 'nRFMeshProvision', :path => '.symlinks/plugins/nordic_nrf_mesh/IOS-nRF-Mesh-Library' flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) end diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 715f61191..c7a12e836 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -34,77 +34,28 @@ PODS: - file_picker (0.0.1): - DKImagePickerController/PhotoGallery - Flutter - - Firebase/CoreOnly (8.3.0): - - FirebaseCore (= 8.3.0) - - Firebase/Crashlytics (8.3.0): - - Firebase/CoreOnly - - FirebaseCrashlytics (~> 8.3.0) - - firebase_core (1.4.0): - - Firebase/CoreOnly (= 8.3.0) - - Flutter - - firebase_crashlytics (2.1.1): - - Firebase/Crashlytics (= 8.3.0) - - firebase_core - - Flutter - - FirebaseCore (8.3.0): - - FirebaseCoreDiagnostics (~> 8.0) - - GoogleUtilities/Environment (~> 7.4) - - GoogleUtilities/Logger (~> 7.4) - - FirebaseCoreDiagnostics (8.8.0): - - GoogleDataTransport (~> 9.0) - - GoogleUtilities/Environment (~> 7.4) - - GoogleUtilities/Logger (~> 7.4) - - nanopb (~> 2.30908.0) - - FirebaseCrashlytics (8.3.0): - - FirebaseCore (~> 8.0) - - FirebaseInstallations (~> 8.0) - - GoogleDataTransport (~> 9.0) - - nanopb (~> 2.30908.0) - - PromisesObjC (~> 1.2) - - FirebaseInstallations (8.8.0): - - FirebaseCore (~> 8.0) - - GoogleUtilities/Environment (~> 7.4) - - GoogleUtilities/UserDefaults (~> 7.4) - - PromisesObjC (< 3.0, >= 1.2) - Flutter (1.0.0) - - GoogleDataTransport (9.1.0): - - GoogleUtilities/Environment (~> 7.2) - - nanopb (~> 2.30908.0) - - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/Environment (7.5.2): - - PromisesObjC (< 3.0, >= 1.2) - - GoogleUtilities/Logger (7.5.2): - - GoogleUtilities/Environment - - GoogleUtilities/UserDefaults (7.5.2): - - GoogleUtilities/Logger - - nanopb (2.30908.0): - - nanopb/decode (= 2.30908.0) - - nanopb/encode (= 2.30908.0) - - nanopb/decode (2.30908.0) - - nanopb/encode (2.30908.0) - nordic_nrf_mesh (0.0.1): - Flutter - nRFMeshProvision - nRFMeshProvision (3.1.4): - CryptoSwift (= 1.4.0) - - PromisesObjC (1.2.12) - - Protobuf (3.18.0) + - Protobuf (3.19.1) - reactive_ble_mobile (0.0.1): - Flutter - Protobuf (~> 3.5) - SwiftProtobuf (~> 1.0) - - SDWebImage (5.12.0): - - SDWebImage/Core (= 5.12.0) - - SDWebImage/Core (5.12.0) + - SDWebImage (5.12.1): + - SDWebImage/Core (= 5.12.1) + - SDWebImage/Core (5.12.1) - SwiftProtobuf (1.18.0) - SwiftyGif (5.4.0) DEPENDENCIES: - file_picker (from `.symlinks/plugins/file_picker/ios`) - - firebase_core (from `.symlinks/plugins/firebase_core/ios`) - - firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`) - Flutter (from `Flutter`) - nordic_nrf_mesh (from `.symlinks/plugins/nordic_nrf_mesh/ios`) + - nRFMeshProvision (from `.symlinks/plugins/nordic_nrf_mesh/IOS-nRF-Mesh-Library`) - reactive_ble_mobile (from `.symlinks/plugins/reactive_ble_mobile/ios`) SPEC REPOS: @@ -112,16 +63,6 @@ SPEC REPOS: - CryptoSwift - DKImagePickerController - DKPhotoGallery - - Firebase - - FirebaseCore - - FirebaseCoreDiagnostics - - FirebaseCrashlytics - - FirebaseInstallations - - GoogleDataTransport - - GoogleUtilities - - nanopb - - nRFMeshProvision - - PromisesObjC - Protobuf - SDWebImage - SwiftProtobuf @@ -130,14 +71,12 @@ SPEC REPOS: EXTERNAL SOURCES: file_picker: :path: ".symlinks/plugins/file_picker/ios" - firebase_core: - :path: ".symlinks/plugins/firebase_core/ios" - firebase_crashlytics: - :path: ".symlinks/plugins/firebase_crashlytics/ios" Flutter: :path: Flutter nordic_nrf_mesh: :path: ".symlinks/plugins/nordic_nrf_mesh/ios" + nRFMeshProvision: + :path: ".symlinks/plugins/nordic_nrf_mesh/IOS-nRF-Mesh-Library" reactive_ble_mobile: :path: ".symlinks/plugins/reactive_ble_mobile/ios" @@ -146,26 +85,15 @@ SPEC CHECKSUMS: DKImagePickerController: b5eb7f7a388e4643264105d648d01f727110fc3d DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 file_picker: 3e6c3790de664ccf9b882732d9db5eaf6b8d4eb1 - Firebase: 817b9171d0d51dccc458b94a5e8edff6b1dd323d - firebase_core: 72374607c8c6f5a0adc0559e5c5bf57deda975e3 - firebase_crashlytics: f5c47b951fb9b494f2bc4212c8aa8de3ed8417cf - FirebaseCore: a6dba751680d7033b9d3831e1cfc95ead0605118 - FirebaseCoreDiagnostics: fe77f42da6329d6d83d21fd9d621a6b704413bfc - FirebaseCrashlytics: 3d83f2e8a47f476f47c82ff4536b169df6781271 - FirebaseInstallations: 2563cb18a723ef9c6ef18318a49519b75dce613c Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a - GoogleDataTransport: 85fd18ff3019bb85d3f2c551d04c481dedf71fc9 - GoogleUtilities: 8de2a97a17e15b6b98e38e8770e2d129a57c0040 - nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96 - nordic_nrf_mesh: d9d7aa0a0e30f87ccbbc40afd9f89a3d3fe9dbf1 + nordic_nrf_mesh: 5b37621fef90bc1b077bc9b34d3241b3622a0605 nRFMeshProvision: 4b3e185fd13b077c997aadca6f54806f5c577cd7 - PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97 - Protobuf: 1a37ebea1338949e9ac35a3f06e80b3f536eec8d + Protobuf: 3724efa50cb2846d7ccebc8691c574e85fd74471 reactive_ble_mobile: 9ce6723d37ccf701dbffd202d487f23f5de03b4c - SDWebImage: 4ea20cca2986adc5aacde07aa686742fd4c67a37 + SDWebImage: 4dc3e42d9ec0c1028b960a33ac6b637bb432207b SwiftProtobuf: c3c12645230d9b09c72267e0de89468c5543bd86 SwiftyGif: 5d4af95df24caf1c570dbbcb32a3b8a0763bc6d7 -PODFILE CHECKSUM: f75ba5d8f49a47808aba0fe7df1fded8d962c2f8 +PODFILE CHECKSUM: 72e7578d6c04ade70fdc134ccf7d38a693d97cd0 COCOAPODS: 1.11.2 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 890a1b3f6..d6ccea463 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,19 +3,17 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 5DA9059326B2A351009C0D46 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5DA9059226B2A350009C0D46 /* GoogleService-Info.plist */; }; + 48B245D55A232AE47E752BD2 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 619ECBDD763224032BE6833B /* Pods_Runner.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - AD6461464436F7BB932E37BD /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 789E53EBFD539FEE4E201412 /* Pods_Runner.framework */; }; - FA01D202253EF0C20052F20A /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = FA01D201253EF0C20052F20A /* GoogleService-Info.plist */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -32,15 +30,12 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 0AC75AFA4808C24A7ADA6447 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 5DA9059226B2A350009C0D46 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; - 6A4671671C0BD8569EB42FE2 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 619ECBDD763224032BE6833B /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 789E53EBFD539FEE4E201412 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; @@ -49,8 +44,9 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - AB04D6364AC83E4CDB2A868F /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - FA01D201253EF0C20052F20A /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + A087C3D4A893D037CB39D63C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + B58F5B6FB4077D78452F1B7E /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + F98B41E9B0B2E099C3829143 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -58,13 +54,21 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - AD6461464436F7BB932E37BD /* Pods_Runner.framework in Frameworks */, + 48B245D55A232AE47E752BD2 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 5AAE71CD48D4EB4B9B56AD7B /* Frameworks */ = { + isa = PBXGroup; + children = ( + 619ECBDD763224032BE6833B /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -82,8 +86,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, - 9B3C55C95757CAF32BBE2907 /* Pods */, - E0269034E900295ACED45678 /* Frameworks */, + F19FD1A3318B146477BE567D /* Pods */, + 5AAE71CD48D4EB4B9B56AD7B /* Frameworks */, ); sourceTree = ""; }; @@ -98,13 +102,10 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( - 5DA9059226B2A350009C0D46 /* GoogleService-Info.plist */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 97C147021CF9000F007C117D /* Info.plist */, - FA01D201253EF0C20052F20A /* GoogleService-Info.plist */, - 97C146F11CF9000F007C117D /* Supporting Files */, 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, @@ -113,31 +114,17 @@ path = Runner; sourceTree = ""; }; - 97C146F11CF9000F007C117D /* Supporting Files */ = { + F19FD1A3318B146477BE567D /* Pods */ = { isa = PBXGroup; children = ( + A087C3D4A893D037CB39D63C /* Pods-Runner.debug.xcconfig */, + B58F5B6FB4077D78452F1B7E /* Pods-Runner.release.xcconfig */, + F98B41E9B0B2E099C3829143 /* Pods-Runner.profile.xcconfig */, ); - name = "Supporting Files"; - sourceTree = ""; - }; - 9B3C55C95757CAF32BBE2907 /* Pods */ = { - isa = PBXGroup; - children = ( - 6A4671671C0BD8569EB42FE2 /* Pods-Runner.debug.xcconfig */, - AB04D6364AC83E4CDB2A868F /* Pods-Runner.release.xcconfig */, - 0AC75AFA4808C24A7ADA6447 /* Pods-Runner.profile.xcconfig */, - ); + name = Pods; path = Pods; sourceTree = ""; }; - E0269034E900295ACED45678 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 789E53EBFD539FEE4E201412 /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -145,15 +132,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - FB171580745F5FA2A5C565BF /* [CP] Check Pods Manifest.lock */, + A960FC72D6D6022DCE1A315A /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - D347320FD9D15C68A2A87C59 /* [CP] Embed Pods Frameworks */, - DB686AE0253F0883007B6206 /* ShellScript */, + 198FAD7BBDC6ED0A352207CF /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -170,7 +156,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -205,78 +191,59 @@ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - FA01D202253EF0C20052F20A /* GoogleService-Info.plist in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - 5DA9059326B2A351009C0D46 /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + 198FAD7BBDC6ED0A352207CF /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputPaths = ( + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", ); - name = "Thin Binary"; - outputPaths = ( + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + showEnvVarsInLog = 0; }; - 9740EEB61CF901F6004384FC /* Run Script */ = { + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Run Script"; + name = "Thin Binary"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; - }; - D347320FD9D15C68A2A87C59 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - DB686AE0253F0883007B6206 /* ShellScript */ = { + 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( ); - outputFileListPaths = ( - ); + name = "Run Script"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "${PODS_ROOT}/FirebaseCrashlytics/run\n"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - FB171580745F5FA2A5C565BF /* [CP] Check Pods Manifest.lock */ = { + A960FC72D6D6022DCE1A315A /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -373,7 +340,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -388,7 +355,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = P54CUYXVB2; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -404,7 +371,6 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 1.0.2; PRODUCT_BUNDLE_IDENTIFIER = fr.dooz.nordicNrfMeshExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -460,7 +426,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -509,7 +475,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -526,7 +492,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = P54CUYXVB2; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -558,7 +524,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = P54CUYXVB2; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -574,7 +540,6 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 1.0.2; PRODUCT_BUNDLE_IDENTIFIER = fr.dooz.nordicNrfMeshExample; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 6ae77c2d2..c87d15a33 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + NordicNrfMeshExample CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -15,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - $(MARKETING_VERSION) + $(FLUTTER_BUILD_NAME) CFBundleSignature ???? CFBundleVersion - $(CURRENT_PROJECT_VERSION) + $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS NSBluetoothAlwaysUsageDescription diff --git a/example/lib/main.dart b/example/lib/main.dart index d804bc260..6d41bf154 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,29 +1,5 @@ -import 'dart:isolate'; - -import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/foundation.dart' show kDebugMode; -import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:nordic_nrf_mesh_example/src/app.dart'; -void main() async { - WidgetsFlutterBinding.ensureInitialized(); - - await Firebase.initializeApp(); - - if (kDebugMode) { - await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(false); - } - - FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError; - Isolate.current.addErrorListener(RawReceivePort((pair) async { - final List errorAndStacktrace = pair; - await FirebaseCrashlytics.instance.recordError( - errorAndStacktrace.first, - errorAndStacktrace.last, - ); - }).sendPort); - - runApp(const MyApp()); -} +void main() => runApp(const MyApp()); diff --git a/example/lib/src/views/home/home.dart b/example/lib/src/views/home/home.dart index c3bbaae18..5a033edce 100644 --- a/example/lib/src/views/home/home.dart +++ b/example/lib/src/views/home/home.dart @@ -76,7 +76,7 @@ class _PlatformVersion extends State { class MeshManagerApiWidget extends StatefulWidget { final NordicNrfMesh nordicNrfMesh; - final ValueChanged onNewMeshNetwork; + final ValueChanged onNewMeshNetwork; const MeshManagerApiWidget({Key? key, required this.nordicNrfMesh, required this.onNewMeshNetwork}) : super(key: key); @@ -129,14 +129,14 @@ class _MeshManagerApiWidgetState extends State { ), TextButton( onPressed: () async { - final provisionerList = await _meshNetwork!.provisionerList; + final provisionerList = await _meshNetwork!.provisioners; debugPrint('# of provs : ${provisionerList.length}'); }, child: const Text('get provisioner list'), ), TextButton( onPressed: () async { - var provUUIDs = await _meshNetwork!.provisionerList; + var provUUIDs = await _meshNetwork!.provisioners; for (var value in provUUIDs) { debugPrint('$value'); } @@ -160,7 +160,7 @@ class _MeshManagerApiWidgetState extends State { setState(() { _meshNetwork = null; }); - widget.onNewMeshNetwork(_meshNetwork!); + widget.onNewMeshNetwork(_meshNetwork); } catch (err) { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Caught error: $err'))); } diff --git a/example/lib/src/views/scan_and_provisionning/scan_and_provisioning.dart b/example/lib/src/views/scan_and_provisionning/scan_and_provisioning.dart index ed0c8fffa..abc9ab2ec 100644 --- a/example/lib/src/views/scan_and_provisionning/scan_and_provisioning.dart +++ b/example/lib/src/views/scan_and_provisionning/scan_and_provisioning.dart @@ -62,11 +62,13 @@ class _ScanningAndProvisioningState extends State { meshProvisioningUuid, ], ).listen((device) async { - _serviceData[device.id] = Uuid.parse( - _meshManagerApi.getDeviceUuid(device.serviceData[_meshManagerApi.meshProvisioningUuidServiceKey]!.toList())); - setState(() { - _devices.add(device); - }); + _serviceData[device.id] = + Uuid.parse(_meshManagerApi.getDeviceUuid(device.serviceData[meshProvisioningUuid]!.toList())); + if (_devices.every((d) => d.id != device.id)) { + setState(() { + _devices.add(device); + }); + } }); setState(() { isScanning = true; diff --git a/example/pubspec.lock b/example/pubspec.lock index 6dc9a514c..5042ca25a 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -7,21 +7,21 @@ packages: name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "25.0.0" + version: "31.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "2.2.0" + version: "2.8.0" archive: dependency: transitive description: name: archive url: "https://pub.dartlang.org" source: hosted - version: "3.1.2" + version: "3.1.6" args: dependency: transitive description: @@ -35,7 +35,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.8.1" + version: "2.8.2" boolean_selector: dependency: transitive description: @@ -49,7 +49,7 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" charcode: dependency: transitive description: @@ -127,41 +127,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.4" - firebase_core: - dependency: "direct main" - description: - name: firebase_core - url: "https://pub.dartlang.org" - source: hosted - version: "1.4.0" - firebase_core_platform_interface: - dependency: transitive - description: - name: firebase_core_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "4.0.1" - firebase_core_web: - dependency: transitive - description: - name: firebase_core_web - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - firebase_crashlytics: - dependency: "direct main" - description: - name: firebase_crashlytics - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.1" - firebase_crashlytics_platform_interface: - dependency: transitive - description: - name: firebase_crashlytics_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "3.1.0" fixnum: dependency: transitive description: @@ -298,7 +263,14 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10" + version: "0.12.11" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" meta: dependency: transitive description: @@ -326,7 +298,7 @@ packages: path: ".." relative: true source: path - version: "0.9.1" + version: "0.10.0" package_config: dependency: transitive description: @@ -354,7 +326,7 @@ packages: name: platform url: "https://pub.dartlang.org" source: hosted - version: "3.0.0" + version: "3.1.0" plugin_platform_interface: dependency: transitive description: @@ -375,7 +347,7 @@ packages: name: process url: "https://pub.dartlang.org" source: hosted - version: "4.2.3" + version: "4.2.4" protobuf: dependency: transitive description: @@ -513,21 +485,21 @@ packages: name: test url: "https://pub.dartlang.org" source: hosted - version: "1.17.10" + version: "1.19.5" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.2" + version: "0.4.8" test_core: dependency: transitive description: name: test_core url: "https://pub.dartlang.org" source: hosted - version: "0.4.0" + version: "0.4.9" typed_data: dependency: transitive description: @@ -541,14 +513,14 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" vm_service: dependency: transitive description: name: vm_service url: "https://pub.dartlang.org" source: hosted - version: "7.1.1" + version: "7.5.0" watcher: dependency: transitive description: @@ -585,5 +557,5 @@ packages: source: hosted version: "3.1.0" sdks: - dart: ">=2.12.0 <3.0.0" + dart: ">=2.14.0 <3.0.0" flutter: ">=2.0.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 3754106bb..e6e9d10da 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -1,6 +1,6 @@ name: nordic_nrf_mesh_example description: Demonstrates how to use the nordic_nrf_mesh plugin. -version: 1.0.2 +version: 1.1.0 publish_to: "none" # Remove this line if you wish to publish to pub.dev @@ -12,8 +12,6 @@ dependencies: flutter: sdk: flutter - firebase_core: ^1.4.0 - firebase_crashlytics: ^2.1.1 file_picker: ^3.0.4 nordic_nrf_mesh: diff --git a/ios/.gitignore b/ios/.gitignore index aa479fd3c..0c885071e 100644 --- a/ios/.gitignore +++ b/ios/.gitignore @@ -34,4 +34,5 @@ Icon? .tags* /Flutter/Generated.xcconfig +/Flutter/ephemeral/ /Flutter/flutter_export_environment.sh \ No newline at end of file diff --git a/ios/Classes/Plugin/Channels/PluginChannel.swift b/ios/Classes/Plugin/Channels/PluginChannel.swift index 1a0cf49f4..5bd7d2006 100644 --- a/ios/Classes/Plugin/Channels/PluginChannel.swift +++ b/ios/Classes/Plugin/Channels/PluginChannel.swift @@ -7,15 +7,9 @@ import Foundation -//enum PluginMethodChannel: String{ -// case getPlatformVersion -// case createMeshManagerApi -//} - enum PluginMethodChannel { case getPlatformVersion - case createMeshManagerApi case error(_ error: Error) @@ -24,9 +18,6 @@ enum PluginMethodChannel { switch call.method { case "getPlatformVersion": self = .getPlatformVersion - case "createMeshManagerApi": - self = .createMeshManagerApi - default: self = .error(FlutterCallError.notImplemented) } diff --git a/ios/Classes/Plugin/SwiftNordicNrfMeshPlugin.swift b/ios/Classes/Plugin/SwiftNordicNrfMeshPlugin.swift index bc9062734..3ab0be4cc 100644 --- a/ios/Classes/Plugin/SwiftNordicNrfMeshPlugin.swift +++ b/ios/Classes/Plugin/SwiftNordicNrfMeshPlugin.swift @@ -11,6 +11,7 @@ public class SwiftNordicNrfMeshPlugin: NSObject, FlutterPlugin { init(messenger: FlutterBinaryMessenger) { self.messenger = messenger + self.meshManagerApi = DoozMeshManagerApi(messenger: messenger) super.init() } @@ -48,11 +49,6 @@ public class SwiftNordicNrfMeshPlugin: NSObject, FlutterPlugin { let systemVersion = "\(UIDevice.current.systemName) \(UIDevice.current.systemVersion)" result(systemVersion) break - case .createMeshManagerApi: - self.meshManagerApi = DoozMeshManagerApi(messenger: messenger) - result(nil) - break } - } } diff --git a/ios/nordic_nrf_mesh.podspec b/ios/nordic_nrf_mesh.podspec index a57ab2137..384e19dce 100644 --- a/ios/nordic_nrf_mesh.podspec +++ b/ios/nordic_nrf_mesh.podspec @@ -4,20 +4,20 @@ # Pod::Spec.new do |s| s.name = 'nordic_nrf_mesh' - s.version = '0.0.1' - s.summary = 'A new flutter plugin project.' + s.version = '0.10.0' + s.summary = 'A Flutter plugin to enable mesh network management and communication using Nordic SDKs. It also provides the ability to open BLE connection with mesh nodes using some other Flutter plugin.' s.description = <<-DESC -A new flutter plugin project. +A Flutter plugin to enable mesh network management and communication using Nordic SDKs. It also provides the ability to open BLE connection with mesh nodes using some other Flutter plugin. DESC - s.homepage = 'http://example.com' + s.homepage = 'http://dooz-domotique.com' s.license = { :file => '../LICENSE' } - s.author = { 'Your Company' => 'email@example.com' } + s.author = { 'OZEO-DOOZ' => 'contact@dooz-domotique.com' } s.source = { :path => '.' } s.source_files = 'Classes/**/*' s.dependency 'Flutter' s.dependency 'nRFMeshProvision' - s.platform = :ios, '10.0' + s.platform = :ios, '11.0' # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported. s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } diff --git a/lib/nordic_nrf_mesh.dart b/lib/nordic_nrf_mesh.dart index 3b435d32f..a963b290b 100644 --- a/lib/nordic_nrf_mesh.dart +++ b/lib/nordic_nrf_mesh.dart @@ -1,3 +1,6 @@ +/// A Flutter plugin to enable mesh network management and communication using Nordic's SDKs. It also provides the ability to open BLE connection with mesh nodes using some other Flutter plugin. +library nordic_nrf_mesh; + export 'src/nrf_mesh.dart'; export 'src/mesh_network.dart' show IMeshNetwork; export 'src/mesh_manager_api.dart'; @@ -8,6 +11,6 @@ export 'src/ble/ble_manager_callbacks.dart'; export 'src/ble/ble_mesh_manager.dart'; export 'src/ble/ble_mesh_manager_callbacks.dart'; export 'src/utils/provisioning.dart' show ProvisioningEvent; -export 'src/contants.dart' show ProvisioningFailureCode, BleManagerFailureCode; +export 'src/constants.dart' show ProvisioningFailureCode, BleManagerFailureCode; export 'src/exceptions/exceptions.dart'; export 'src/extensions/extensions.dart'; diff --git a/lib/src/ble/ble_manager.dart b/lib/src/ble/ble_manager.dart index 186acb1e9..2303d7bc4 100644 --- a/lib/src/ble/ble_manager.dart +++ b/lib/src/ble/ble_manager.dart @@ -5,9 +5,9 @@ import 'package:flutter/foundation.dart'; import 'package:flutter_reactive_ble/flutter_reactive_ble.dart'; import 'package:meta/meta.dart'; import 'package:nordic_nrf_mesh/nordic_nrf_mesh.dart'; +import 'package:nordic_nrf_mesh/src/constants.dart'; -const mtuSizeMax = 517; -const maxPacketSize = 20; +// runtime constants final doozCustomServiceUuid = Platform.isAndroid ? Uuid.parse('00001400-0000-1000-8000-00805F9B34FB') : Uuid.parse('1400'); final doozCustomCharacteristicUuid = @@ -23,12 +23,16 @@ final meshProvisioningDataOut = Platform.isAndroid ? Uuid.parse('00002ADC-0000-1000-8000-00805F9B34FB') : Uuid.parse('2ADC'); final clientCharacteristicConfigDescriptorUuid = Platform.isAndroid ? Uuid.parse('00002902-0000-1000-8000-00805f9b34fb') : Uuid.parse('2902'); -const Duration _kConnectionTimeout = Duration(seconds: 30); +/// {@template ble_manager} +/// An abstract class that should be extended to handle BLE device interactions. +/// +/// It already implements most parts of the connection process and also triggers event on the given [BleManagerCallbacks]. +/// {@endtemplate} abstract class BleManager { /// The current BLE device being managed if any - DiscoveredDevice? _device; DiscoveredDevice? get device => _device; + DiscoveredDevice? _device; /// A [bool] used to adapt the connection process bool isProvisioningCompleted = false; @@ -46,34 +50,37 @@ abstract class BleManager { late final StreamSubscription _globalStatusListener; /// A [Completer] used to handle the async behavior of [connect] method - late Completer _connectCompleter; - @protected Completer get connectCompleter => _connectCompleter; + late Completer _connectCompleter; /// The entry point for BLE library - final FlutterReactiveBle _bleInstance; FlutterReactiveBle get bleInstance => _bleInstance; + final FlutterReactiveBle _bleInstance; + /// {@macro ble_manager} BleManager(this._bleInstance) { _globalStatusListener = _bleInstance.connectedDeviceStream.listen(_onGlobalStateUpdate); } + /// Will clear used resources Future dispose() async { await callbacks?.dispose(); await _connectedDeviceStatusListener?.cancel(); await _globalStatusListener.cancel(); } + /// A method that should be overriden to define a validation on the BLE device advertised services @visibleForOverriding Future isRequiredServiceSupported(bool shouldCheckDoozCustomService); /// A method that should implement the GATT initialization. /// - /// In our case, it means requesting the highest possible MTU size and subcribing to notifications + /// It should for instance request the highest possible MTU size and subscribe to notifications @visibleForOverriding Future initGatt(); + /// Will disconnect from the current device if any Future disconnect() async { if (_device == null) { _log('calling disconnect without connected device..'); @@ -98,6 +105,9 @@ abstract class BleManager { /// - add event in [callbacks] sinks /// - return any error on the stream or any given reason for [DeviceConnectionState.disconnected] events (usually a [GenericFailure]) /// + /// _DooZ specific API :_ + /// TODO remove before 1.0.0 + /// /// The [whitelist] has been introduced along with [doozCustomCharacteristicUuid] and is intended to be used like so : /// - the whitelist is populated with MAC addresses and [shouldCheckDoozCustomService] is true /// - it will connect to the BLE device and check the MAC address that should be stored in the DooZ custom charac @@ -106,7 +116,7 @@ abstract class BleManager { /// (because this is a DooZ application specific flow, we made these parameters optional) Future connect( final DiscoveredDevice discoveredDevice, { - Duration connectionTimeout = _kConnectionTimeout, + Duration connectionTimeout = kDefaultConnectionTimeout, List? whitelist, bool shouldCheckDoozCustomService = false, }) async { @@ -138,7 +148,7 @@ abstract class BleManager { connectionTimeout: connectionTimeout, ) .listen( - (ConnectionStateUpdate connectionStateUpdate) async { + (ConnectionStateUpdate connectionStateUpdate) { switch (connectionStateUpdate.connectionState) { case DeviceConnectionState.connecting: _device = discoveredDevice; @@ -234,6 +244,7 @@ abstract class BleManager { /// This method will read the [doozCustomCharacteristicUuid] if a device is connected and return the MAC address stored Future getMacId() async { + String? macId; try { final macIdChar = await bleInstance.readCharacteristic(QualifiedCharacteristic( characteristicId: doozCustomCharacteristicUuid, @@ -241,12 +252,12 @@ abstract class BleManager { deviceId: _device!.id, )); final macIdList = macIdChar.reversed.toList(); - final macId = _toMacAddress(macIdList); + macId = _toMacAddress(macIdList); _log('got mac adr from custom service ! $macId'); - return macId; } catch (e) { _log('caught error during reading dooz custom charac $e'); } + return macId; } Future _negotiateAndInitGatt(bool shouldCheckDoozCustomService) async { diff --git a/lib/src/ble/ble_manager_callbacks.dart b/lib/src/ble/ble_manager_callbacks.dart index f7dc3253a..97a052a27 100644 --- a/lib/src/ble/ble_manager_callbacks.dart +++ b/lib/src/ble/ble_manager_callbacks.dart @@ -2,18 +2,26 @@ import 'dart:async'; import 'package:flutter_reactive_ble/flutter_reactive_ble.dart'; +/// {@template discovered_device_and_service} +/// A data class used to hold a [DiscoveredDevice] and the corresponding BLE Mesh service +/// {@endtemplate} class BleManagerCallbacksDiscoveredServices { final DiscoveredDevice device; final DiscoveredService service; + /// {@macro discovered_device_and_service} const BleManagerCallbacksDiscoveredServices(this.device, this.service); } +/// {@template ble_error} +/// An error class use for propagating BLE errors +/// {@endtemplate} class BleManagerCallbacksError { final DiscoveredDevice? device; final String message; final Object? error; + /// {@macro ble_error} const BleManagerCallbacksError(this.device, this.message, this.error); @override @@ -23,37 +31,57 @@ class BleManagerCallbacksError { 'device name: ${device?.name})'; } +/// An abstract class that should be extended to access callbacks during BLE device interactions abstract class BleManagerCallbacks { + /// The [StreamController] that is used to trigger an event when a device is connecting final onDeviceConnectingController = StreamController(); + + /// The [Stream] that will contain the `connecting` event Stream get onDeviceConnecting => onDeviceConnectingController.stream; + /// The [StreamController] that is used to trigger an event when a device is connected final onDeviceConnectedController = StreamController(); + + /// The [Stream] that will contain the `connected` event Stream get onDeviceConnected => onDeviceConnectedController.stream; + /// The [StreamController] that is used to trigger an event when a device is disconnecting final onDeviceDisconnectingController = StreamController(); + + /// The [Stream] that will contain the `disconnecting` event Stream get onDeviceDisconnecting => onDeviceDisconnectingController.stream; + /// The [StreamController] that is used to trigger an event when a device is disconnected final onDeviceDisconnectedController = StreamController(); - Stream get onDeviceDisconnected => onDeviceDisconnectedController.stream; - // Stream onLinkLossOccurred; + /// The [Stream] that will contain the `disconnected` event + Stream get onDeviceDisconnected => onDeviceDisconnectedController.stream; + /// The [StreamController] that is used to trigger an event when BLE services are validated upon connection final onServicesDiscoveredController = StreamController(); + + /// The [Stream] that will contain a [BleManagerCallbacksDiscoveredServices] object when BLE services are validated upon connection Stream get onServicesDiscovered => onServicesDiscoveredController.stream; + /// The [StreamController] that is used to trigger an event when the phone is ready to interact with target BLE device final onDeviceReadyController = StreamController(); + + /// The [Stream] that will contain an event when the phone is ready to interact with target BLE device Stream get onDeviceReady => onDeviceReadyController.stream; + /// The [StreamController] that is used to trigger an event when an error occur during connection lifetime final onErrorController = StreamController(); - Stream get onError => onErrorController.stream; - // Stream onDeviceNotSupported; + /// The [Stream] that will contain any [BleManagerCallbacksError] that could occur during connection lifetime + Stream get onError => onErrorController.stream; /// A method that should be used to update the stored MTU so the native code properly constructs the PDUs Future sendMtuToMeshManagerApi(int mtu); + /// RFU bool shouldEnableBatteryLevelNotifications(DiscoveredDevice device) => false; + /// Will clear the used resources Future dispose() => Future.wait([ onDeviceConnectingController.close(), onDeviceConnectedController.close(), diff --git a/lib/src/ble/ble_mesh_manager.dart b/lib/src/ble/ble_mesh_manager.dart index 464c49161..9af9bb6ba 100644 --- a/lib/src/ble/ble_mesh_manager.dart +++ b/lib/src/ble/ble_mesh_manager.dart @@ -6,23 +6,35 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter_reactive_ble/flutter_reactive_ble.dart'; import 'package:nordic_nrf_mesh/nordic_nrf_mesh.dart'; +import 'package:nordic_nrf_mesh/src/constants.dart'; import 'package:retry/retry.dart'; +/// {@template ble_mesh_manager} +/// A Singleton that should be used to handle **BLE Mesh** connectivity features. +/// +/// It implements the methods to init GATT layer, subscribe to notifications and send PDUs for **BLE Mesh** nodes. +/// {@endtemplate} class BleMeshManager extends BleManager { static late final BleMeshManager _instance = BleMeshManager._(FlutterReactiveBle()); + /// The list of [DiscoveredService] that were discovered during the last connection process late List _discoveredServices; + /// The subscription for data when the connected node is a mesh proxy (ie. already provisioned in a network) StreamSubscription>? _meshProxyDataOutSubscription; + + /// The subscription for data when the connected node is a free mesh node (ie. waiting to be provisioned in a network) StreamSubscription>? _meshProvisioningDataOutSubscription; BleMeshManager._(FlutterReactiveBle _bleInstance) : super(_bleInstance); + /// {@macro ble_mesh_manager} factory BleMeshManager() => _instance as BleMeshManager; void _log(String msg) => debugPrint('[NordicNrfMesh] $msg'); - void onDeviceDisconnected() async { + /// A method to clear some resources when the device should be disconnected + void _onDeviceDisconnected() async { isProvisioningCompleted = false; await _meshProxyDataOutSubscription?.cancel(); _meshProxyDataOutSubscription = null; @@ -32,36 +44,42 @@ class BleMeshManager extends BleManager { @override Future disconnect() { - onDeviceDisconnected(); + _onDeviceDisconnected(); return super.disconnect(); } + /// A method to read the connected device's MAC address via [doozCustomServiceUuid] + /// + /// _(DooZ specific API)_ Future getServiceMacId() async { - if (hasExpectedService(doozCustomServiceUuid)) { + String? macId; + if (_hasExpectedService(doozCustomServiceUuid)) { final service = _discoveredServices.firstWhere((service) => service.serviceId == doozCustomServiceUuid); - if (hasExpectedCharacteristicUuid(service, doozCustomCharacteristicUuid)) { - return getMacId(); + if (_hasExpectedCharacteristicUuid(service, doozCustomCharacteristicUuid)) { + macId = await getMacId(); } } + return macId; } @override Future isRequiredServiceSupported(bool shouldCheckDoozCustomService) async { + // In the case of a mesh node, the advertised service should be either 0x1827 or 0x1828 _discoveredServices = await bleInstance.discoverServices(device!.id); _log('services $_discoveredServices'); isProvisioningCompleted = false; - if (hasExpectedService(meshProxyUuid)) { + if (_hasExpectedService(meshProxyUuid)) { isProvisioningCompleted = true; // check for meshProxy characs final service = _discoveredServices.firstWhere((service) => service.serviceId == meshProxyUuid); - if (hasExpectedCharacteristicUuid(service, meshProxyDataIn) && - hasExpectedCharacteristicUuid(service, meshProxyDataOut)) { + if (_hasExpectedCharacteristicUuid(service, meshProxyDataIn) && + _hasExpectedCharacteristicUuid(service, meshProxyDataOut)) { // if shouldCheckDoozCustomService is true, will also check for the existence of doozCustomServiceUuid // that has been introduced in firmwares v1.1.0 so we can get the mac address even on iOS devices if (shouldCheckDoozCustomService) { - if (hasExpectedService(doozCustomServiceUuid)) { + if (_hasExpectedService(doozCustomServiceUuid)) { final service = _discoveredServices.firstWhere((service) => service.serviceId == doozCustomServiceUuid); - if (hasExpectedCharacteristicUuid(service, doozCustomCharacteristicUuid)) { + if (_hasExpectedCharacteristicUuid(service, doozCustomCharacteristicUuid)) { return service; } } @@ -75,10 +93,10 @@ class BleMeshManager extends BleManager { } return null; } else { - if (hasExpectedService(meshProvisioningUuid)) { + if (_hasExpectedService(meshProvisioningUuid)) { final service = _discoveredServices.firstWhere((service) => service.serviceId == meshProvisioningUuid); - if (hasExpectedCharacteristicUuid(service, meshProvisioningDataIn) && - hasExpectedCharacteristicUuid(service, meshProvisioningDataOut)) { + if (_hasExpectedCharacteristicUuid(service, meshProvisioningDataIn) && + _hasExpectedCharacteristicUuid(service, meshProvisioningDataOut)) { return service; } } @@ -88,44 +106,42 @@ class BleMeshManager extends BleManager { @override Future initGatt() async { + // request highest MTU (only useful on Android) final negotiatedMtu = await bleInstance.requestMtu(deviceId: device!.id, mtu: mtuSizeMax); if (Platform.isAndroid) { mtuSize = negotiatedMtu - 3; } else if (Platform.isIOS) { mtuSize = negotiatedMtu; } + // notify about negociated MTU size await callbacks!.sendMtuToMeshManagerApi(mtuSize); + // subscribe to notifications from the proper BLE service (proxy/provisioning) DiscoveredService? discoveredService; if (isProvisioningCompleted) { discoveredService = _discoveredServices.firstWhere((service) => service.serviceId == meshProxyUuid); await _meshProxyDataOutSubscription?.cancel(); _meshProxyDataOutSubscription = - getDataOutSubscription(getQualifiedCharacteristic(meshProxyDataOut, discoveredService.serviceId)); + _getDataOutSubscription(_getQualifiedCharacteristic(meshProxyDataOut, discoveredService.serviceId)); } else { discoveredService = _discoveredServices.firstWhere((service) => service.serviceId == meshProvisioningUuid); await _meshProvisioningDataOutSubscription?.cancel(); _meshProvisioningDataOutSubscription = - getDataOutSubscription(getQualifiedCharacteristic(meshProvisioningDataOut, discoveredService.serviceId)); + _getDataOutSubscription(_getQualifiedCharacteristic(meshProvisioningDataOut, discoveredService.serviceId)); } } - bool hasExpectedService(Uuid serviceUuid) { - return _discoveredServices.any((service) => service.serviceId == serviceUuid); - } + bool _hasExpectedService(Uuid serviceUuid) => _discoveredServices.any((service) => service.serviceId == serviceUuid); - bool hasExpectedCharacteristicUuid(DiscoveredService discoveredService, Uuid expectedCharacteristicId) { - return discoveredService.characteristicIds.any((uuid) => uuid == expectedCharacteristicId); - } + bool _hasExpectedCharacteristicUuid(DiscoveredService discoveredService, Uuid expectedCharacteristicId) => + discoveredService.characteristicIds.any((uuid) => uuid == expectedCharacteristicId); - QualifiedCharacteristic getQualifiedCharacteristic(Uuid characteristicId, Uuid serviceId) { - return QualifiedCharacteristic( - characteristicId: characteristicId, - serviceId: serviceId, - deviceId: device!.id, - ); - } + QualifiedCharacteristic _getQualifiedCharacteristic(Uuid characteristicId, Uuid serviceId) => QualifiedCharacteristic( + characteristicId: characteristicId, + serviceId: serviceId, + deviceId: device!.id, + ); - StreamSubscription> getDataOutSubscription(QualifiedCharacteristic qCharacteristic) => + StreamSubscription> _getDataOutSubscription(QualifiedCharacteristic qCharacteristic) => bleInstance.subscribeToCharacteristic(qCharacteristic).where((data) => data.isNotEmpty == true).listen((data) { if (!(callbacks?.onDataReceivedController.isClosed == true) && callbacks!.onDataReceivedController.hasListener) { @@ -149,6 +165,9 @@ class BleMeshManager extends BleManager { } }); + /// This method will send the given PDU. + /// + /// It may split the data in chunks based on the current [mtuSize]. Future sendPdu(final List pdu) async { final chunks = ((pdu.length / (mtuSize - 1)) + 1).floor(); var srcOffset = 0; @@ -157,15 +176,15 @@ class BleMeshManager extends BleManager { final length = math.min(pdu.length - srcOffset, mtuSize); final sublist = pdu.sublist(srcOffset, srcOffset + length); final segmentedBuffer = sublist; - await send(segmentedBuffer); + await _send(segmentedBuffer); srcOffset += length; } } else { - await send(pdu); + await _send(pdu); } } - Future send(final List data) async { + Future _send(final List data) async { if (data.isEmpty) { return; } @@ -175,7 +194,7 @@ class BleMeshManager extends BleManager { () async { final service = _discoveredServices.firstWhere((service) => service.serviceId == meshProxyUuid); await bleInstance.writeCharacteristicWithoutResponse( - getQualifiedCharacteristic(meshProxyDataIn, service.serviceId), + _getQualifiedCharacteristic(meshProxyDataIn, service.serviceId), value: data); }, retryIf: (e) => e is PlatformException, @@ -188,7 +207,7 @@ class BleMeshManager extends BleManager { () async { final service = _discoveredServices.firstWhere((service) => service.serviceId == meshProvisioningUuid); await bleInstance.writeCharacteristicWithoutResponse( - getQualifiedCharacteristic(meshProvisioningDataIn, service.serviceId), + _getQualifiedCharacteristic(meshProvisioningDataIn, service.serviceId), value: data); }, retryIf: (e) => e is PlatformException, @@ -198,6 +217,7 @@ class BleMeshManager extends BleManager { } } + /// A method to clear GATT cache (only useful in some cases in **Android**) Future refreshDeviceCache() async { if (Platform.isAndroid) { if (device != null) { diff --git a/lib/src/ble/ble_mesh_manager_callbacks.dart b/lib/src/ble/ble_mesh_manager_callbacks.dart index 0ef33c164..d7766eb89 100644 --- a/lib/src/ble/ble_mesh_manager_callbacks.dart +++ b/lib/src/ble/ble_mesh_manager_callbacks.dart @@ -3,36 +3,60 @@ import 'dart:async'; import 'package:flutter_reactive_ble/flutter_reactive_ble.dart'; import 'package:nordic_nrf_mesh/src/ble/ble_manager_callbacks.dart'; +/// {@template ble_data_event} +/// An abstract data class that should be extended to hold the in/out data of the current BLE connection +/// {@endtemplate} abstract class _BleMeshManagerCallbacksEvent { + /// The currently connected device final DiscoveredDevice device; + + /// The currently used MTU size final int mtu; + + /// The PDU of this in/out event final List pdu; + /// {@macro ble_data_event} const _BleMeshManagerCallbacksEvent(this.device, this.mtu, this.pdu); @override String toString() => '$device, $mtu, $pdu'; } +/// {@template ble_data_received} +/// A data class used when some PDU is received +/// {@endtemplate} class BleMeshManagerCallbacksDataReceived extends _BleMeshManagerCallbacksEvent { + /// {@macro ble_data_received} const BleMeshManagerCallbacksDataReceived(DiscoveredDevice device, int mtu, List pdu) : super(device, mtu, pdu); @override String toString() => 'BleMeshManagerCallbacksDataReceived{ ${super.toString()} }'; } +/// {@template ble_data_sent} +/// A data class used when some PDU is sent +/// {@endtemplate} class BleMeshManagerCallbacksDataSent extends _BleMeshManagerCallbacksEvent { + /// {@macro ble_data_sent} const BleMeshManagerCallbacksDataSent(DiscoveredDevice device, int mtu, List pdu) : super(device, mtu, pdu); @override String toString() => 'BleMeshManagerCallbacksDataSent{ ${super.toString()} }'; } +/// An abstract class that should be extended to access callbacks during BLE device interactions abstract class BleMeshManagerCallbacks extends BleManagerCallbacks { + /// The [StreamController] that is used to trigger an event when some data is received while connected to a BLE device final onDataReceivedController = StreamController(); + + /// The [Stream] that will contain any data received while connected to a BLE device Stream get onDataReceived => onDataReceivedController.stream; + /// The [StreamController] that is used to trigger an event when some data is sent to the connected BLE device final onDataSentController = StreamController(); + + /// The [Stream] that will contain any data sent to the connected BLE device Stream get onDataSent => onDataSentController.stream; @override diff --git a/lib/src/ble/ble_scanner.dart b/lib/src/ble/ble_scanner.dart index 5177195ca..04f3cc24b 100644 --- a/lib/src/ble/ble_scanner.dart +++ b/lib/src/ble/ble_scanner.dart @@ -4,35 +4,46 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter_reactive_ble/flutter_reactive_ble.dart'; import 'package:nordic_nrf_mesh/nordic_nrf_mesh.dart'; +import 'package:nordic_nrf_mesh/src/constants.dart'; import 'ble_manager.dart'; // for mesh guid constants -const Duration defaultScanDuration = Duration(seconds: 5); - +/// {@template ble_scanner_error} +/// A data class used to hold some data when an ble scan error occur +/// {@endtemplate} class BleScannerError { final String message; final Object error; + /// {@macro ble_scanner_error} const BleScannerError(this.message, this.error); } +/// {@template ble_scanner} +/// This singleton is used to wrap the Bluetooth scanning features of [FlutterReactiveBle]. +/// {@endtemplate} class BleScanner { static late final BleScanner _instance = BleScanner._(); BleScanner._(); + /// {@macro ble_scanner} factory BleScanner() => _instance; + /// The entry point for BLE library final FlutterReactiveBle _flutterReactiveBle = FlutterReactiveBle(); + /// {@macro ble_status_stream} Stream get bleStatusStream => _flutterReactiveBle.statusStream; + /// {@macro ble_status} BleStatus get bleStatus => _flutterReactiveBle.status; - late final StreamController _onScanErrorController = StreamController.broadcast(); - + /// A [Stream] providing any [BleScannerError] that could occur when using (un)provisionedNodesInRange methods Stream get onScanErrorStream => _onScanErrorController.stream; + late final StreamController _onScanErrorController = StreamController.broadcast(); + /// Used to close the [onScanErrorStream] void dispose() { _onScanErrorController.close(); } @@ -45,7 +56,7 @@ class BleScanner { Future> _scanWithParamsAsFuture({ ScanMode scanMode = ScanMode.lowLatency, List withServices = const [], - Duration timeoutDuration = defaultScanDuration, + Duration timeoutDuration = kDefaultScanDuration, }) async { if (Platform.isIOS || Platform.isAndroid) { final scanResults = []; @@ -87,6 +98,7 @@ class BleScanner { } } + /// {@macro get_specific_node} Future searchForSpecificNode( String deviceNameOrId, Duration scanTimeout, @@ -105,25 +117,30 @@ class BleScanner { return result; } + /// {@macro get_unprovisioned} Future> unprovisionedNodesInRange({ - Duration timeoutDuration = defaultScanDuration, + Duration timeoutDuration = kDefaultScanDuration, }) => _scanWithParamsAsFuture( withServices: [meshProvisioningUuid], timeoutDuration: timeoutDuration, ); + /// {@macro scan_unprovisioned} Stream scanForUnprovisionedNodes() => _scanWithParamsAsStream(withServices: [meshProvisioningUuid]); + /// {@macro get_provisioned} Future> provisionedNodesInRange({ - Duration timeoutDuration = defaultScanDuration, + Duration timeoutDuration = kDefaultScanDuration, }) => _scanWithParamsAsFuture( withServices: [meshProxyUuid], timeoutDuration: timeoutDuration, ); + /// {@macro scan_provisioned} Stream scanForProxy() => _scanWithParamsAsStream(withServices: [meshProxyUuid]); + /// {@macro custom_scan} Stream scanWithServices(List services) => _scanWithParamsAsStream(withServices: services); } diff --git a/lib/src/contants.dart b/lib/src/constants.dart similarity index 86% rename from lib/src/contants.dart rename to lib/src/constants.dart index 6cc8659f4..c33bdbec4 100644 --- a/lib/src/contants.dart +++ b/lib/src/constants.dart @@ -1,4 +1,9 @@ +/// The namespace (used to identify flutter method and event channels) const namespace = 'fr.dooz.nordic_nrf_mesh'; +const Duration kDefaultScanDuration = Duration(seconds: 5); +const Duration kDefaultConnectionTimeout = Duration(seconds: 30); +const mtuSizeMax = 517; +const maxPacketSize = 20; /// Used when an error occured during the provisioning process enum ProvisioningFailureCode { diff --git a/lib/src/events/mesh_manager_api_events.dart b/lib/src/events/mesh_manager_api_events.dart index 9c8f6da8b..589b4d322 100644 --- a/lib/src/events/mesh_manager_api_events.dart +++ b/lib/src/events/mesh_manager_api_events.dart @@ -1,3 +1,8 @@ +/// {@template mesh_manager_event} +/// A class used to enumerate the events received from native side. +/// +/// _(may be refactored to an enum in future releases of Dart)_ +/// {@endtemplate} class MeshManagerApiEvent { final String value; diff --git a/lib/src/exceptions/ble_manager_exception.dart b/lib/src/exceptions/ble_manager_exception.dart index 8e40475a2..ad8c05353 100644 --- a/lib/src/exceptions/ble_manager_exception.dart +++ b/lib/src/exceptions/ble_manager_exception.dart @@ -1,4 +1,4 @@ -import 'package:nordic_nrf_mesh/src/contants.dart'; +import 'package:nordic_nrf_mesh/src/constants.dart'; /// An [Exception] that can be thrown during the lifecycle of a BLE connection class BleManagerException implements Exception { diff --git a/lib/src/exceptions/nrf_mesh_provisioning_exception.dart b/lib/src/exceptions/nrf_mesh_provisioning_exception.dart index 0496e2ee7..8beac8fb8 100644 --- a/lib/src/exceptions/nrf_mesh_provisioning_exception.dart +++ b/lib/src/exceptions/nrf_mesh_provisioning_exception.dart @@ -1,4 +1,4 @@ -import 'package:nordic_nrf_mesh/src/contants.dart'; +import 'package:nordic_nrf_mesh/src/constants.dart'; /// An [Exception] that can be thrown during the provisioning process class NrfMeshProvisioningException implements Exception { diff --git a/lib/src/extensions/extensions.dart b/lib/src/extensions/extensions.dart index 4c4c858be..a8c3b52c2 100644 --- a/lib/src/extensions/extensions.dart +++ b/lib/src/extensions/extensions.dart @@ -1,3 +1,4 @@ extension BitFieldString on int { + /// A method to "pretty-print" a given integer as a series of [width] bit(s) String bitField({int width = 8}) => toRadixString(2).padLeft(width, '0'); } diff --git a/lib/src/mesh_manager_api.dart b/lib/src/mesh_manager_api.dart index 9c80d46a8..63c5e758b 100644 --- a/lib/src/mesh_manager_api.dart +++ b/lib/src/mesh_manager_api.dart @@ -4,28 +4,32 @@ import 'dart:math'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_reactive_ble/flutter_reactive_ble.dart'; import 'package:nordic_nrf_mesh/nordic_nrf_mesh.dart'; -import 'package:nordic_nrf_mesh/src/contants.dart'; +import 'package:nordic_nrf_mesh/src/constants.dart'; import 'package:nordic_nrf_mesh/src/events/mesh_manager_api_events.dart'; import 'package:nordic_nrf_mesh/src/mesh_network.dart'; import 'package:rxdart/rxdart.dart'; +/// {@template mesh_manager_api} +/// This class is used to expose Nordic's APIs and handle a mesh network. +/// +/// It listens to all native events and distribute it to broadcast [Stream]s that should in turn be listened by plugin consumer. +/// {@endtemplate} class MeshManagerApi { + // native channels late final _methodChannel = const MethodChannel('$namespace/mesh_manager_api/methods'); late final _eventChannel = const EventChannel('$namespace/mesh_manager_api/events'); + late Stream> _eventChannelStream; + // event controllers late final _onNetworkLoadedStreamController = StreamController.broadcast(); late final _onNetworkImportedController = StreamController.broadcast(); late final _onNetworkUpdatedController = StreamController.broadcast(); - late final _onMeshPduCreatedController = StreamController>.broadcast(); late final _sendProvisioningPduController = StreamController.broadcast(); - late final _onProvisioningStateChangedController = StreamController.broadcast(); late final _onProvisioningFailedController = StreamController.broadcast(); late final _onProvisioningCompletedController = StreamController.broadcast(); - late final _onConfigCompositionDataStatusController = StreamController.broadcast(); late final _onConfigAppKeyStatusController = StreamController.broadcast(); late final _onGenericLevelStatusController = StreamController.broadcast(); @@ -33,7 +37,6 @@ class MeshManagerApi { late final _onDoozEpochStatusController = StreamController.broadcast(); late final _onV2MagicLevelSetStatusController = StreamController.broadcast(); late final _onV2MagicLevelGetStatusController = StreamController.broadcast(); - late final _onGenericOnOffStatusController = StreamController.broadcast(); late final _onConfigModelAppStatusController = StreamController.broadcast(); late final _onConfigModelSubscriptionStatusController = StreamController.broadcast(); @@ -42,12 +45,11 @@ class MeshManagerApi { late final _onConfigNetworkTransmitStatusController = StreamController.broadcast(); late final _onConfigDefaultTtlStatusController = StreamController.broadcast(); late final _onConfigBeaconStatusController = StreamController.broadcast(); - late final _onLightLightnessStatusController = StreamController.broadcast(); late final _onLightCtlStatusController = StreamController.broadcast(); late final _onLightHslStatusController = StreamController.broadcast(); late final _onConfigKeyRefreshPhaseStatusController = StreamController.broadcast(); - + // stream subs late StreamSubscription _onNetworkLoadedSubscription; late StreamSubscription _onNetworkImportedSubscription; late StreamSubscription _onNetworkUpdatedSubscription; @@ -73,63 +75,55 @@ class MeshManagerApi { late StreamSubscription _onConfigNetworkTransmitStatusSubscription; late StreamSubscription _onConfigDefaultTtlStatusSubscription; late StreamSubscription _onConfigBeaconStatusSubscription; - late StreamSubscription _onLightLightnessStatusSubscription; late StreamSubscription _onLightCtlStatusSubscription; late StreamSubscription _onLightHslStatusSubscription; - late StreamSubscription _onConfigKeyRefreshPhaseStatusSubscription; - late Stream> _eventChannelStream; MeshNetwork? _lastMeshNetwork; - void _log(String msg) => debugPrint('[NordicNrfMesh] $msg'); - MeshManagerApi() { + // initialize main event stream listener _eventChannelStream = _eventChannel.receiveBroadcastStream().cast().map((event) => event.cast()); if (kDebugMode) { _eventChannelStream.doOnData((data) => debugPrint('$data')); } - + // network events _onNetworkLoadedSubscription = _onMeshNetworkEventSucceed(MeshManagerApiEvent.loaded).listen(_onNetworkLoadedStreamController.add); _onNetworkImportedSubscription = _onMeshNetworkEventSucceed(MeshManagerApiEvent.imported).listen(_onNetworkImportedController.add); _onNetworkUpdatedSubscription = _onMeshNetworkEventSucceed(MeshManagerApiEvent.updated).listen(_onNetworkUpdatedController.add); - _onNetworkLoadFailedSubscription = _onMeshNetworkEventFailed(MeshManagerApiEvent.loadFailed).listen(_onNetworkLoadedStreamController.addError); _onNetworkImportFailedSubscription = _onMeshNetworkEventFailed(MeshManagerApiEvent.importFailed).listen(_onNetworkImportedController.addError); - + // pdu events _onMeshPduCreatedSubscription = _eventChannelStream .where((event) => event['eventName'] == MeshManagerApiEvent.meshPduCreated.value) .map((event) => event['pdu'] as List) .map((event) => event.cast()) .listen(_onMeshPduCreatedController.add); - _sendProvisioningPduSubscription = _eventChannelStream .where((event) => event['eventName'] == MeshManagerApiEvent.sendProvisioningPdu.value) .map((event) => SendProvisioningPduData.fromJson(event)) .listen(_sendProvisioningPduController.add); - + // provisioning events _onProvisioningStateChangedSubscription = _eventChannelStream .where((event) => event['eventName'] == MeshManagerApiEvent.provisioningStateChanged.value) .map((event) => MeshProvisioningStatusData.fromJson(event)) .listen(_onProvisioningStateChangedController.add); - _onProvisioningCompletedSubscription = _eventChannelStream .where((event) => event['eventName'] == MeshManagerApiEvent.provisioningCompleted.value) .map((event) => MeshProvisioningCompletedData.fromJson(event)) .listen(_onProvisioningCompletedController.add); - _onProvisioningFailedSubscription = _eventChannelStream .where((event) => event['eventName'] == MeshManagerApiEvent.provisioningFailed.value) .map((event) => MeshProvisioningStatusData.fromJson(event)) .listen(_onProvisioningFailedController.add); - + // mesh status events _onConfigCompositionDataStatusSubscription = _eventChannelStream .where((event) => event['eventName'] == MeshManagerApiEvent.configCompositionDataStatus.value) .map((event) => ConfigCompositionDataStatusData.fromJson(event)) @@ -162,7 +156,6 @@ class MeshManagerApi { .where((event) => event['eventName'] == MeshManagerApiEvent.v2MagicLevelGetStatus.value) .map((event) => MagicLevelGetStatusData.fromJson(event)) .listen(_onV2MagicLevelGetStatusController.add); - _onLightLightnessStatusSubscription = _eventChannelStream .where((event) => event['eventName'] == MeshManagerApiEvent.lightLightnessStatus.value) .map((event) => LightLightnessStatusData.fromJson(event)) @@ -175,7 +168,6 @@ class MeshManagerApi { .where((event) => event['eventName'] == MeshManagerApiEvent.lightHslStatus.value) .map((event) => LightHslStatusData.fromJson(event)) .listen(_onLightHslStatusController.add); - _onConfigModelAppStatusSubscription = _eventChannelStream .where((event) => event['eventName'] == MeshManagerApiEvent.configModelAppStatus.value) .map((event) => ConfigModelAppStatusData.fromJson(event)) @@ -259,6 +251,7 @@ class MeshManagerApi { Stream get onV2MagicLevelGetStatus => _onV2MagicLevelGetStatusController.stream; + /// The currently loaded [IMeshNetwork] or null IMeshNetwork? get meshNetwork => _lastMeshNetwork; Stream get onLightLightnessStatus => _onLightLightnessStatusController.stream; @@ -270,8 +263,7 @@ class MeshManagerApi { Stream get onConfigKeyRefreshPhaseStatus => _onConfigKeyRefreshPhaseStatusController.stream; - Uuid get meshProvisioningUuidServiceKey => meshProvisioningUuid; - + /// Checks if the node is advertising with Node Identity Future isAdvertisedWithNodeIdentity(final List serviceData) async { final result = await _methodChannel.invokeMethod( 'isAdvertisedWithNodeIdentity', @@ -280,6 +272,7 @@ class MeshManagerApi { return result!; } + /// Checks if the node identity matches Future nodeIdentityMatches(List serviceData) async { final result = await _methodChannel.invokeMethod( 'nodeIdentityMatches', @@ -288,6 +281,7 @@ class MeshManagerApi { return result!; } + /// Checks if the node is advertising with Network Identity Future isAdvertisingWithNetworkIdentity(final List serviceData) async { final result = await _methodChannel.invokeMethod( 'isAdvertisingWithNetworkIdentity', @@ -296,6 +290,7 @@ class MeshManagerApi { return result!; } + /// Checks if the generated network ids match. The network ID contained in the service data would be checked against a network id of each network key. Future networkIdMatches(List serviceData) async { final result = await _methodChannel.invokeMethod( 'networkIdMatches', @@ -304,6 +299,7 @@ class MeshManagerApi { return result!; } + /// Should be called to clear all resources used by this class void dispose() => Future.wait([ _onNetworkLoadedSubscription.cancel(), _onNetworkImportedSubscription.cancel(), @@ -363,44 +359,60 @@ class MeshManagerApi { _onConfigBeaconStatusController.close() ]); + /// Loads the mesh network from the local database. Future loadMeshNetwork() async { final future = _onNetworkLoadedStreamController.stream.first; await _methodChannel.invokeMethod('loadMeshNetwork'); return future; } + /// Starts an asynchronous task that imports a network from the mesh configuration db json Future importMeshNetworkJson(final String json) async { final future = _onNetworkImportedController.stream.first; await _methodChannel.invokeMethod('importMeshNetworkJson', {'json': json}); return future; } + /// Notify native side about the current mtu size Future setMtu(final int mtuSize) => _methodChannel.invokeMethod('setMtuSize', {'mtuSize': mtuSize}); + /// Exports full mesh network to a JSON String. Future exportMeshNetwork() async { final json = await _methodChannel.invokeMethod('exportMeshNetwork'); return json; } + /// This method will clear the provisioned nodes, reset the sequence number and generate new network with new provisioning data. Future resetMeshNetwork() => _methodChannel.invokeMethod('resetMeshNetwork'); + /// Handles notifications received by the client. + /// + /// **Should be called whenever data is received from a mesh node, so the Nordic library do the parsing job** Future handleNotifications(int mtu, List pdu) => _methodChannel.invokeMethod( 'handleNotifications', {'mtu': mtu, 'pdu': pdu}, ); + /// Must be called to handle sent data. Future handleWriteCallbacks(int mtu, List pdu) => _methodChannel.invokeMethod( 'handleWriteCallbacks', {'mtu': mtu, 'pdu': pdu}, ); + /// Identifies the node that is to be provisioned. + /// + /// _WARNING: This method is not intended to be used by external user of nrf_mesh_plugin. It is used by the provisioning method._ Future identifyNode(String serviceUuid) => _methodChannel.invokeMethod( 'identifyNode', {'serviceUuid': serviceUuid}, ); + /// This method reset the unprovisioned nodes cache. + /// + /// _WARNING: This method is not intended to be used by external use of nrf_mesh_plugin. It is used for the provisioning process._ Future cleanProvisioningData() => _methodChannel.invokeMethod('cleanProvisioningData'); + /// Will send a GenericLevelSet message to the given [address]. Future sendGenericLevelSet( int address, int level, { @@ -424,6 +436,7 @@ class MeshManagerApi { return status; } + /// Will send a GenericLevelGet message to the given [address]. Future sendGenericLevelGet( int address, { int keyIndex = 0, @@ -439,6 +452,7 @@ class MeshManagerApi { return status; } + /// Will send a GenericLevelOnOff message to the given [address]. Future sendGenericOnOffSet( int address, bool value, @@ -464,6 +478,9 @@ class MeshManagerApi { return status; } + /// Will send a MagicLevelSet message to the given [address]. + /// + /// _(DooZ specific API)_ Future sendV2MagicLevelSet( int address, int io, @@ -487,6 +504,9 @@ class MeshManagerApi { return status; } + /// Will send a MagicLevelGet message to the given [address]. + /// + /// _(DooZ specific API)_ Future sendV2MagicLevelGet( int address, int io, @@ -508,11 +528,14 @@ class MeshManagerApi { return status; } + /// Will send a ConfigCompositionDataGet message to the given [dest]. Future sendConfigCompositionDataGet(int dest) => _methodChannel.invokeMethod('sendConfigCompositionDataGet', {'dest': dest}); + /// Will send a ConfigAppKeyAdd message to the given [dest]. Future sendConfigAppKeyAdd(int dest) => _methodChannel.invokeMethod('sendConfigAppKeyAdd', {'dest': dest}); + /// Will send a ConfigModelAppBind message to the given [nodeId]. Future sendConfigModelAppBind(int nodeId, int elementId, int modelId, {int appKeyIndex = 0}) async { final status = _onConfigModelAppStatusController.stream.firstWhere( @@ -529,6 +552,7 @@ class MeshManagerApi { return status; } + /// Will send a ConfigModelSubscriptionAdd message to the given [elementAddress]. Future sendConfigModelSubscriptionAdd( int elementAddress, int subscriptionAddress, int modelIdentifier) async { final status = _onConfigModelSubscriptionStatusController.stream.firstWhere( @@ -546,6 +570,7 @@ class MeshManagerApi { return status; } + /// Will send a ConfigModelSubscriptionDelete message to the given [elementAddress]. Future sendConfigModelSubscriptionDelete( int elementAddress, int subscriptionAddress, int modelIdentifier) async { final status = _onConfigModelSubscriptionStatusController.stream.firstWhere( @@ -563,6 +588,7 @@ class MeshManagerApi { return status; } + /// Will send a ConfigModelSubscriptionDeleteAll message to the given [elementAddress]. Future sendConfigModelSubscriptionDeleteAll(int elementAddress, int modelIdentifier) => _methodChannel.invokeMethod( 'sendConfigModelSubscriptionDeleteAll', @@ -572,6 +598,7 @@ class MeshManagerApi { }, ); + /// Will send a ConfigModelPublicationSet message to the given [elementAddress]. Future sendConfigModelPublicationSet( int elementAddress, int publishAddress, @@ -613,6 +640,7 @@ class MeshManagerApi { return status; } + /// Will send a ConfigModelPublicationGet message to the given [elementAddress]. Future getPublicationSettings( int elementAddress, int modelIdentifier, @@ -632,6 +660,7 @@ class MeshManagerApi { } } + /// Will send a LightLightnessSet message to the given [address]. Future sendLightLightness( int address, int lightness, @@ -651,6 +680,7 @@ class MeshManagerApi { return status; } + /// Will send a LightCtlSet message to the given [address]. Future sendLightCtl( int address, int lightness, @@ -674,6 +704,7 @@ class MeshManagerApi { return status; } + /// Will send a LightHslSet message to the given [address]. Future sendLightHsl( int address, int lightness, @@ -697,6 +728,7 @@ class MeshManagerApi { return status; } + /// Will send a ConfigDefaultTtlGet message to the given [address]. Future getDefaultTtl(int address) async { final status = _onConfigDefaultTtlStatusController.stream.firstWhere( (element) => element.source == address, @@ -706,6 +738,7 @@ class MeshManagerApi { return status; } + /// Will send a ConfigDefaultTtlSet message to the given [address]. Future setDefaultTtl(int address, int ttl) async { final status = _onConfigDefaultTtlStatusController.stream.firstWhere( (element) => element.source == address, @@ -718,6 +751,7 @@ class MeshManagerApi { return status; } + /// Will send a ConfigNetworkTransmitSet message to the given [address]. Future setNetworkTransmitSettings( int address, int transmitCount, @@ -735,6 +769,7 @@ class MeshManagerApi { return status; } + /// Will send a ConfigNetworkTransmitGet message to the given [address]. Future getNetworkTransmitSettings(int address) async { final status = _onConfigNetworkTransmitStatusController.stream.firstWhere( (element) => element.source == address, @@ -744,6 +779,7 @@ class MeshManagerApi { return status; } + /// Will send a ConfigKeyRefreshPhaseGet message to the given [address]. Future keyRefreshPhaseGet({ int address = 0xFFFF, int netKeyIndex = 0, @@ -770,6 +806,7 @@ class MeshManagerApi { static const int useNewKeys = 2; // Normal operation static const int revokeOldKeys = 3; // Key Distribution + /// Will send a ConfigKeyRefreshPhaseSet message to the given [address]. Future keyRefreshPhaseSet({ int address = 0xFFFF, int netKeyIndex = 0, @@ -829,6 +866,8 @@ class MeshManagerApi { /// Will send a DoozScenarioSet message (0x8219). /// Defaults to a scenario that apply a level 0 (OFF cmd with lights). + /// + /// _(DooZ specific API)_ Future doozScenarioSet( int address, int scenarioId, @@ -873,6 +912,8 @@ class MeshManagerApi { } /// Will send a DoozEpochSet message (0x8220). + /// + /// _(DooZ specific API)_ Future doozScenarioEpochSet( int address, int tzData, @@ -942,26 +983,34 @@ class MeshManagerApi { return '${_digits(msb >> 32, 8)}-${_digits(msb >> 16, 4)}-${_digits(msb, 4)}-${_digits(lsb >> 48, 4)}-${_digits(lsb, 12)}'; } - Future provisioningIos(String uuid) => _methodChannel.invokeMethod('provisioning', {'uuid': uuid}); - + /// Provision the given [meshNode]. + /// + /// _WARNING: This method is not intended to be used by external user of nrf_mesh_plugin. It is used by the provisioning method._ Future provisioning(UnprovisionedMeshNode meshNode) => _methodChannel.invokeMethod('provisioning', meshNode.toJson()); + /// {@macro deprovision} Future deprovision(ProvisionedMeshNode meshNode) async { - final unicastAddress = await meshNode.unicastAddress; - final status = _onConfigNodeResetStatusController.stream - .where((element) => element.source == unicastAddress) - .timeout(const Duration(seconds: 3), - onTimeout: (sink) => sink.add( - const ConfigNodeResetStatus(-1, -1, false), - )) - .first; - await _methodChannel.invokeMethod('deprovision', {'unicastAddress': unicastAddress}); - return status; + if (Platform.isIOS || Platform.isAndroid) { + final unicastAddress = await meshNode.unicastAddress; + final status = _onConfigNodeResetStatusController.stream + .where((element) => element.source == unicastAddress) + .timeout(const Duration(seconds: 3), + onTimeout: (sink) => sink.add( + const ConfigNodeResetStatus(-1, -1, false), + )) + .first; + await _methodChannel.invokeMethod('deprovision', {'unicastAddress': unicastAddress}); + return status; + } else { + throw UnsupportedError('Platform ${Platform.operatingSystem} is not supported'); + } } + /// A method that will return a mesh node uuid during provisioning process or null Future cachedProvisionedMeshNodeUuid() => _methodChannel.invokeMethod('cachedProvisionedMeshNodeUuid'); + /// A method to get the sequence number of a given mesh [node] Future getSequenceNumber(ProvisionedMeshNode node) async { if (Platform.isIOS || Platform.isAndroid) { final result = await _methodChannel.invokeMethod( @@ -973,6 +1022,7 @@ class MeshManagerApi { throw Exception('Platform not supported'); } + /// A method to set the sequence number of a given mesh [node] Future setSequenceNumber(ProvisionedMeshNode node, int seqNum) async => _methodChannel.invokeMethod( 'setSequenceNumberForAddress', {'address': await node.unicastAddress, 'sequenceNumber': seqNum}, @@ -996,4 +1046,6 @@ class MeshManagerApi { Stream _onMeshNetworkEventFailed(final MeshManagerApiEvent eventType) => _filterEventChannel(eventType).map((event) => MeshNetworkEventError.fromJson(event)); + + void _log(String msg) => debugPrint('[NordicNrfMesh] $msg'); } diff --git a/lib/src/mesh_network.dart b/lib/src/mesh_network.dart index 9b5c5be97..20d4989db 100644 --- a/lib/src/mesh_network.dart +++ b/lib/src/mesh_network.dart @@ -3,33 +3,58 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; -import 'package:nordic_nrf_mesh/src/contants.dart'; -import 'package:nordic_nrf_mesh/src/models/models.dart'; +import 'package:nordic_nrf_mesh/nordic_nrf_mesh.dart'; +import 'package:nordic_nrf_mesh/src/constants.dart'; +/// {@template mesh_network} +/// The class defining the API to manage a bluetooth mesh network. +/// {@endtemplate} abstract class IMeshNetwork { - Future> get groups; - Future get highestAllocatableAddress; + /// The id of the network + String get id; + + /// The name of the network Future get name; + + /// The current list of provisioners + Future> get provisioners; + + /// The current list of nodes (provisioners + provisioned mesh devices) Future> get nodes; - String get id; - Future> get provisionerList; + /// The currently defined group(s) + Future> get groups; + + /// The max address that the current selected provisioner can allocate + Future get highestAllocatableAddress; + + /// Will try to add a new group in the network with the given [name] Future addGroupWithName(String name); + /// Will check if the given [unicastAddress] is free of use Future assignUnicastAddress(int unicastAddress); - Future> elementsForGroup(int id); + /// Will return the data of elements subscribed to the given [groupAddress] + Future> elementsForGroup(int groupAddress); + /// Will return the next free unicast address based on the number of elements of a node Future nextAvailableUnicastAddress(int elementSize); + /// Will return the next free unicast address based on the number of elements of a node. The address shall be greater than the given [minAddress] Future nextAvailableUnicastAddressWithMin(int minAddress, int elementSize); - Future removeGroup(int id); + /// Will remove the group with the given [groupAddress] + Future removeGroup(int groupAddress); + /// Will return the UUID of the current selected provisioner Future selectedProvisionerUuid(); + /// Will select the provisioner at the given index of the [provisioners] list Future selectProvisioner(int provisionerIndex); + /// Will add a provisioner in the current network using the given ranges and ttl. + /// + /// Will return false if the creation is a failure (e.g the unicast address range is incompatible with the current configuration) Future addProvisioner( int unicastAddressRange, int groupAddressRange, @@ -38,34 +63,53 @@ abstract class IMeshNetwork { String name, }); + /// Will update the given [provisioner] Future updateProvisioner(Provisioner provisioner); + /// Will remove the provisioner with the given uuid Future removeProvisioner(String provisionerUUID); + /// Will manually remove a node from the network. + /// + /// If you want to deprovision a node, please use the `deprovision` method of [NordicNrfMesh] Future deleteNode(String uid); + /// Will return the subscribed addresses of the given element and model Future getMeshModelSubscriptions(int elementAddress, int modelIdentifier); + /// Will return the elements that have subscribed either **Generic Level** models or **Generic ON/OFF** models to the given [groupAddress] Future getGroupElementIds(int groupAddress); + /// Will return the node corresponding to the given [address] Future getNode(int address); + /// Will return the node corresponding to the given [uuid] Future getNodeUsingUUID(String uuid); + /// Will return a newly generated Network Key Future generateNetKey(); + /// Will return the Network Key at the given index Future getNetKey(int netKeyIndex); + /// Will remove the Network Key at the given index Future removeNetKey(int netKeyIndex); + /// Will distribute the Network Key at the given index Future distributeNetKey(int netKeyIndex); } +/// {@template mesh_network_impl} +/// The implementation of [IMeshNetwork] used at runtime. +/// {@endtemplate} class MeshNetwork implements IMeshNetwork { + /// The [MethodChannel] used to interact with Nordic API final MethodChannel _methodChannel; + /// The unique id of the currently loaded [MeshNetwork] final String _id; + /// {@macro mesh_network_impl} MeshNetwork(this._id) : _methodChannel = MethodChannel('$namespace/mesh_network/$_id/methods'); @override @@ -134,7 +178,7 @@ class MeshNetwork implements IMeshNetwork { _methodChannel.invokeMethod('selectProvisioner', {'provisionerIndex': provisionerIndex}); @override - Future> get provisionerList async { + Future> get provisioners async { var provisioners = []; final result = await _methodChannel.invokeMethod('getProvisionersAsJson'); var prov = json.decode(result) as List; diff --git a/lib/src/models/group/group.dart b/lib/src/models/group/group.dart index ad9504aaa..b40e9547d 100644 --- a/lib/src/models/group/group.dart +++ b/lib/src/models/group/group.dart @@ -3,10 +3,16 @@ import 'package:freezed_annotation/freezed_annotation.dart'; part 'group.freezed.dart'; part 'group.g.dart'; +/// {@template group_data} +/// A freezed data class used to hold a group data +/// {@endtemplate} @freezed class GroupData with _$GroupData { + /// {@macro group_data} const factory GroupData(String name, int address, String? addressLabel, String meshUuid, int parentAddress, String? parentAddressLabel) = _GroupData; + /// Provide a constructor to get [GroupData] from JSON [Map]. + /// {@macro group_data} factory GroupData.fromJson(Map json) => _$GroupDataFromJson(json); } diff --git a/lib/src/models/mesh_node/provisioned_mesh_node.dart b/lib/src/models/mesh_node/provisioned_mesh_node.dart index 7fd0ee9f5..ad9d2244d 100644 --- a/lib/src/models/mesh_node/provisioned_mesh_node.dart +++ b/lib/src/models/mesh_node/provisioned_mesh_node.dart @@ -1,9 +1,12 @@ import 'package:flutter/services.dart'; import 'package:json_annotation/json_annotation.dart'; -import 'package:nordic_nrf_mesh/src/contants.dart'; +import 'package:nordic_nrf_mesh/src/constants.dart'; part 'provisioned_mesh_node.g.dart'; +/// {@template model_data} +/// A serializable data class used to hold data about a specific model of a mesh node +/// {@endtemplate} @JsonSerializable(anyMap: true) class ModelData { final int key; @@ -11,16 +14,24 @@ class ModelData { final List subscribedAddresses; final List boundAppKey; + /// {@macro model_data} ModelData(this.key, this.modelId, this.subscribedAddresses, this.boundAppKey); + /// Provide a constructor to get [ModelData] from JSON [Map]. + /// {@macro model_data} factory ModelData.fromJson(Map json) => _$ModelDataFromJson(json); + /// Provide a constructor to get [Map] from [ModelData]. + /// {@macro model_data} Map toJson() => _$ModelDataToJson(this); @override String toString() => 'ModelData ${toJson()}'; } +/// {@template element_data} +/// A serializable data class used to hold data about a specific element of a mesh node +/// {@endtemplate} @JsonSerializable(anyMap: true) class ElementData { final int key; @@ -29,28 +40,41 @@ class ElementData { final int locationDescriptor; final List models; + /// {@macro element_data} ElementData(this.key, this.name, this.address, this.locationDescriptor, this.models); + /// Provide a constructor to get [ElementData] from JSON [Map]. + /// {@macro element_data} factory ElementData.fromJson(Map json) => _$ElementDataFromJson(json.cast()); + /// Provide a constructor to get [Map] from [ElementData]. + /// {@macro element_data} Map toJson() => _$ElementDataToJson(this); @override String toString() => 'ElementData ${toJson()}'; } +/// {@template provisioned_node} +/// A class used to expose some data of a given **provisioned** mesh node +/// {@endtemplate} class ProvisionedMeshNode { final MethodChannel _methodChannel; final String uuid; + /// {@macro provisioned_node} ProvisionedMeshNode(this.uuid) : _methodChannel = MethodChannel('$namespace/provisioned_mesh_node/$uuid/methods'); + /// Will return the unicast address of this node as stored in the local database Future get unicastAddress async => (await _methodChannel.invokeMethod('unicastAddress'))!; + /// Will set the name of this node to be stored in the local database set nodeName(String name) => _methodChannel.invokeMethod('nodeName', {'name': name}); + /// Will return the name of this node as stored in the local database Future get name async => (await _methodChannel.invokeMethod('name'))!; + /// Will return the list of elements of this node as stored in the local database Future> get elements async { final _elements = await _methodChannel.invokeMethod('elements'); return _elements!.map((e) => ElementData.fromJson(e)).toList(); diff --git a/lib/src/models/mesh_node/unprovisioned_mesh_node.dart b/lib/src/models/mesh_node/unprovisioned_mesh_node.dart index 9dafb74ce..c79b5c2ff 100644 --- a/lib/src/models/mesh_node/unprovisioned_mesh_node.dart +++ b/lib/src/models/mesh_node/unprovisioned_mesh_node.dart @@ -1,15 +1,21 @@ import 'package:flutter/services.dart'; import 'package:json_annotation/json_annotation.dart'; -import 'package:nordic_nrf_mesh/src/contants.dart'; +import 'package:nordic_nrf_mesh/src/constants.dart'; part 'unprovisioned_mesh_node.g.dart'; +/// {@template unprovisioned_node} +/// A class used to expose some data of a given **unprovisioned** mesh node. +/// +/// _Used by the plugin to handle the provisioning process._ +/// {@endtemplate} @JsonSerializable() class UnprovisionedMeshNode { final MethodChannel _methodChannel; final String uuid; final List? provisionerPublicKeyXY; + /// {@macro unprovisioned_node} UnprovisionedMeshNode(this.uuid, this.provisionerPublicKeyXY) : _methodChannel = MethodChannel('$namespace/unprovisioned_mesh_node/$uuid/methods'); diff --git a/lib/src/models/network_key/network_key.dart b/lib/src/models/network_key/network_key.dart index e6625c7f0..87bdfc883 100644 --- a/lib/src/models/network_key/network_key.dart +++ b/lib/src/models/network_key/network_key.dart @@ -3,8 +3,12 @@ import 'package:freezed_annotation/freezed_annotation.dart'; part 'network_key.freezed.dart'; part 'network_key.g.dart'; +/// {@template network_key} +/// A freezed data class used to hold a given Network Key data +/// {@endtemplate} @freezed class NetworkKey with _$NetworkKey { + /// {@macro network_key} const factory NetworkKey( String name, int netKeyIndex, @@ -20,5 +24,7 @@ class NetworkKey with _$NetworkKey { int timestamp, ) = _NetworkKey; + /// Provide a constructor to get [NetworkKey] from JSON [Map]. + /// {@macro network_key} factory NetworkKey.fromJson(Map json) => _$NetworkKeyFromJson(json); } diff --git a/lib/src/models/provisioner/provisioner.dart b/lib/src/models/provisioner/provisioner.dart index 968594ec5..b70fff1d8 100644 --- a/lib/src/models/provisioner/provisioner.dart +++ b/lib/src/models/provisioner/provisioner.dart @@ -6,8 +6,12 @@ import 'package:nordic_nrf_mesh/src/models/provisioner/range_addresses/allocated part 'provisioner.freezed.dart'; part 'provisioner.g.dart'; +/// {@template provisioner} +/// A freezed data class used to hold data about a provisioner +/// {@endtemplate} @freezed class Provisioner with _$Provisioner { + /// {@macro provisioner} const factory Provisioner( String provisionerName, String provisionerUuid, @@ -18,5 +22,7 @@ class Provisioner with _$Provisioner { List allocatedSceneRanges, bool lastSelected) = _Provisioner; + /// Provide a constructor to get [Provisioner] from JSON [Map]. + /// {@macro provisioner} factory Provisioner.fromJson(Map json) => _$ProvisionerFromJson(json); } diff --git a/lib/src/models/provisioner/range_addresses/allocated_group_range.dart b/lib/src/models/provisioner/range_addresses/allocated_group_range.dart index 70a4ffea0..395d69f57 100644 --- a/lib/src/models/provisioner/range_addresses/allocated_group_range.dart +++ b/lib/src/models/provisioner/range_addresses/allocated_group_range.dart @@ -3,9 +3,15 @@ import 'package:freezed_annotation/freezed_annotation.dart'; part 'allocated_group_range.freezed.dart'; part 'allocated_group_range.g.dart'; +/// {@template group_range} +/// A freezed data class used to hold a group address range +/// {@endtemplate} @freezed class AllocatedGroupRange with _$AllocatedGroupRange { + /// {@macro group_range} const factory AllocatedGroupRange(int lowAddress, int highAddress) = _AllocatedGroupRange; + /// Provide a constructor to get [AllocatedGroupRange] from JSON [Map]. + /// {@macro group_range} factory AllocatedGroupRange.fromJson(Map json) => _$AllocatedGroupRangeFromJson(json); } diff --git a/lib/src/models/provisioner/range_addresses/allocated_scene_range.dart b/lib/src/models/provisioner/range_addresses/allocated_scene_range.dart index 9f0e1cb11..472f4dae0 100644 --- a/lib/src/models/provisioner/range_addresses/allocated_scene_range.dart +++ b/lib/src/models/provisioner/range_addresses/allocated_scene_range.dart @@ -3,9 +3,15 @@ import 'package:freezed_annotation/freezed_annotation.dart'; part 'allocated_scene_range.freezed.dart'; part 'allocated_scene_range.g.dart'; +/// {@template scene_range} +/// A freezed data class used to hold a scene range +/// {@endtemplate} @freezed class AllocatedSceneRange with _$AllocatedSceneRange { + /// {@macro scene_range} const factory AllocatedSceneRange(int firstScene, int lastScene) = _AllocatedSceneRange; + /// Provide a constructor to get [AllocatedSceneRange] from JSON [Map]. + /// {@macro scene_range} factory AllocatedSceneRange.fromJson(Map json) => _$AllocatedSceneRangeFromJson(json); } diff --git a/lib/src/models/provisioner/range_addresses/allocated_unicast_range.dart b/lib/src/models/provisioner/range_addresses/allocated_unicast_range.dart index 4730e9a71..cf57c0a95 100644 --- a/lib/src/models/provisioner/range_addresses/allocated_unicast_range.dart +++ b/lib/src/models/provisioner/range_addresses/allocated_unicast_range.dart @@ -3,9 +3,15 @@ import 'package:freezed_annotation/freezed_annotation.dart'; part 'allocated_unicast_range.freezed.dart'; part 'allocated_unicast_range.g.dart'; +/// {@template unicast_range} +/// A freezed data class used to hold a unicast address range +/// {@endtemplate} @freezed class AllocatedUnicastRange with _$AllocatedUnicastRange { + /// {@macro unicast_range} const factory AllocatedUnicastRange(int lowAddress, int highAddress) = _AllocatedUnicastRange; + /// Provide a constructor to get [AllocatedUnicastRange] from JSON [Map]. + /// {@macro unicast_range} factory AllocatedUnicastRange.fromJson(Map json) => _$AllocatedUnicastRangeFromJson(json); } diff --git a/lib/src/nrf_mesh.dart b/lib/src/nrf_mesh.dart index 61b2fa45e..dff7ca305 100644 --- a/lib/src/nrf_mesh.dart +++ b/lib/src/nrf_mesh.dart @@ -4,30 +4,36 @@ import 'package:flutter/services.dart'; import 'package:flutter_reactive_ble/flutter_reactive_ble.dart'; import 'package:nordic_nrf_mesh/nordic_nrf_mesh.dart'; import 'package:nordic_nrf_mesh/src/ble/ble_scanner.dart'; -import 'package:nordic_nrf_mesh/src/contants.dart'; +import 'package:nordic_nrf_mesh/src/constants.dart'; import 'package:nordic_nrf_mesh/src/utils/provisioning.dart' as utils_provisioning; +/// {@template nordic_nrf_mesh} +/// The entry point for the plugin. +/// It exposes some important methods such as Bluetooth scanning and mesh (de)provisioning. +/// +/// To leverage all Bluetooth capabilities, one shall instantiate [BleMeshManager]. +/// +/// To use the Nordic APIs, one should use the [MeshManagerApi] available via the [meshManagerApi] getter of the [NordicNrfMesh] instance. +/// {@endtemplate} +/// +/// {@macro mesh_manager_api} class NordicNrfMesh { final _methodChannel = const MethodChannel('$namespace/methods'); - final BleScanner _bleScanner = BleScanner(); - - NordicNrfMesh(); Future get platformVersion async { final version = await _methodChannel.invokeMethod('getPlatformVersion'); return version; } - late final MeshManagerApi _meshManagerApi = _createMeshManagerApi(); - MeshManagerApi get meshManagerApi => _meshManagerApi; + /// {@macro ble_scanner} + late final BleScanner _bleScanner = BleScanner(); - MeshManagerApi _createMeshManagerApi() { - _methodChannel.invokeMethod('createMeshManagerApi'); - final meshManagerApi = MeshManagerApi(); - return meshManagerApi; - } + /// {@macro mesh_manager_api} + MeshManagerApi get meshManagerApi => _meshManagerApi; + late final MeshManagerApi _meshManagerApi = MeshManagerApi(); - /// Will try to provision the specified [BluetoothDevice]. + /// {@template provisioning} + /// Will try to provision the specified [DiscoveredDevice]. /// /// After the process or if any error occurs, the [BleManager] will be disconnected from device. /// @@ -35,6 +41,7 @@ class NordicNrfMesh { /// /// Throws an [NrfMeshProvisioningException] if provisioning failed /// or an [UnsupportedError] if the current OS is not supported. + /// {@endtemplate} Future provisioning( final MeshManagerApi meshManagerApi, final BleMeshManager bleMeshManager, @@ -45,39 +52,46 @@ class NordicNrfMesh { utils_provisioning.provisioning(meshManagerApi, bleMeshManager, _bleScanner, device, serviceDataUuid, events: events); - /// Will try to deprovision the specified [ProvisionedMeshNode] by sending a unicast [ConfigNodeReset] message. + /// {@template deprovision} + /// Will try to deprovision the specified [ProvisionedMeshNode] by sending [ConfigNodeReset] message via the unicast address. /// /// Returns a [ConfigNodeResetStatus] or null if timeout after 5sec. /// /// Throws a method channel error "NOT FOUND" if not found in the currently loaded mesh n/w /// or an [UnsupportedError] if the current OS is not supported. + /// {@endtemplate} Future deprovision( final MeshManagerApi meshManagerApi, final ProvisionedMeshNode meshNode, ) => - utils_provisioning.deprovision(meshManagerApi, meshNode); + meshManagerApi.deprovision(meshNode); + /// {@template cancel_provisioning} /// Will try to cancel the provisioning. /// /// Returns `true` if the call has been successful, `false` otherwise. /// /// Throws an [UnsupportedError] if the current OS is not supported. + /// {@endtemplate} Future cancelProvisioning( final MeshManagerApi meshManagerApi, final BleMeshManager bleMeshManager, ) => utils_provisioning.cancelProvisioning(meshManagerApi, _bleScanner, bleMeshManager); + /// {@template get_unprovisioned} /// Will scan for **unprovisioned** nodes. /// /// Returns a [List] of [DiscoveredDevice] that may be empty if no device is in range. /// /// Throws an [UnsupportedError] if the current OS is not supported. + /// {@endtemplate} Future> unprovisionedNodesInRange({ - Duration timeoutDuration = defaultScanDuration, + Duration timeoutDuration = kDefaultScanDuration, }) => _bleScanner.unprovisionedNodesInRange(timeoutDuration: timeoutDuration); + /// {@template scan_unprovisioned} /// Will scan for **unprovisioned** nodes. /// /// Returns a [Stream] of [DiscoveredDevice]. @@ -85,18 +99,22 @@ class NordicNrfMesh { /// To stop the scan, make sure to cancel any subscription to this [Stream]. /// /// Throws an [UnsupportedError] if the current OS is not supported. + /// {@endtemplate} Stream scanForUnprovisionedNodes() => _bleScanner.scanForUnprovisionedNodes(); + /// {@template get_provisioned} /// Will scan for **provisioned** nodes. /// /// Returns a [List] of [DiscoveredDevice] that may be empty if no device is in range. /// /// Throws an [UnsupportedError] if the current OS is not supported. + /// {@endtemplate} Future> provisionedNodesInRange({ - Duration timeoutDuration = defaultScanDuration, + Duration timeoutDuration = kDefaultScanDuration, }) => _bleScanner.provisionedNodesInRange(timeoutDuration: timeoutDuration); + /// {@template scan_provisioned} /// Will scan for **provisioned** nodes. /// /// Returns a [Stream] of [DiscoveredDevice] for the user to listen to. @@ -104,8 +122,10 @@ class NordicNrfMesh { /// To stop the scan, user has to make sure to cancel any subscription to this [Stream]. /// /// Throws an [UnsupportedError] if the current OS is not supported. + /// {@endtemplate} Stream scanForProxy() => _bleScanner.scanForProxy(); + /// {@template custom_scan} /// Will scan for devices that broadcast given services. /// /// Returns a [Stream] of [DiscoveredDevice] for the user to listen to. @@ -113,8 +133,10 @@ class NordicNrfMesh { /// To stop the scan, user has to make sure to cancel any subscription to this [Stream]. /// /// Throws an [UnsupportedError] if the current OS is not supported. + /// {@endtemplate} Stream scanWithServices(List services) => _bleScanner.scanWithServices(services); + /// {@template get_specific_node} /// Will scan for the given device using name or id (MAC on Android or UUID on iOS). /// /// It will scan by default for **unprovisioned** nodes, but one can switch to proxy candidates using the [isProxy] bool flag. @@ -122,16 +144,21 @@ class NordicNrfMesh { /// Returns a [DiscoveredDevice] or null if not found after [timeoutDuration] (defaults to 5sec). /// /// Throws an [UnsupportedError] if the current OS is not supported. + /// {@endtemplate} Future searchForSpecificNode( String deviceNameOrId, { bool isProxy = false, - Duration timeoutDuration = defaultScanDuration, + Duration timeoutDuration = kDefaultScanDuration, }) => _bleScanner.searchForSpecificNode(deviceNameOrId, timeoutDuration, isProxy); + /// {@template ble_status_stream} /// Provide a [Stream] of the current [BleStatus] of the host device. + /// {@endtemplate} Stream get bleStatusStream => _bleScanner.bleStatusStream; + /// {@template ble_status} /// Will return the last known [BleStatus] (tracked via stream by BLE library, so it should always be up-to-date) + /// {@endtemplate} BleStatus get bleStatus => _bleScanner.bleStatus; } diff --git a/lib/src/utils/provisioning.dart b/lib/src/utils/provisioning.dart index 7c4de111e..0d0eeba0a 100644 --- a/lib/src/utils/provisioning.dart +++ b/lib/src/utils/provisioning.dart @@ -3,40 +3,44 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter_reactive_ble/flutter_reactive_ble.dart'; -import 'package:nordic_nrf_mesh/src/ble/ble_manager_callbacks.dart'; -import 'package:nordic_nrf_mesh/src/ble/ble_mesh_manager.dart'; -import 'package:nordic_nrf_mesh/src/ble/ble_mesh_manager_callbacks.dart'; +import 'package:nordic_nrf_mesh/nordic_nrf_mesh.dart'; import 'package:nordic_nrf_mesh/src/ble/ble_scanner.dart'; -import 'package:nordic_nrf_mesh/src/contants.dart'; -import 'package:nordic_nrf_mesh/src/events/data/config_node_reset_status/config_node_reset_status.dart'; -import 'package:nordic_nrf_mesh/src/exceptions/exceptions.dart'; -import 'package:nordic_nrf_mesh/src/mesh_manager_api.dart'; -import 'package:nordic_nrf_mesh/src/models/models.dart'; -class _ProvisioningEvent { - final _provisioningController = StreamController(); - final _provisioningCapabilitiesController = StreamController(); - final _provisioningInvitationController = StreamController(); - final _provisioningReconnectController = StreamController(); - final _onConfigCompositionDataStatusController = StreamController(); - final _onConfigAppKeyStatusController = StreamController(); - final _provisioningGattErrorController = StreamController(); -} - -class ProvisioningEvent extends _ProvisioningEvent { +/// {@template provisioning_events} +/// A class that may be used to listen to provisioning progress. +/// +/// When the provisioning method is running, if the caller passed a [ProvisioningEvent] instance, events will be streamed to notify about the current state. +/// {@endtemplate} +class ProvisioningEvent { + /// A [Stream] that will contain the [DiscoveredDevice] when the main part of the provisioning is about to begin Stream get onProvisioning => _provisioningController.stream; + final _provisioningController = StreamController(); + /// A [Stream] that will contain the [DiscoveredDevice] when the provisioning capabilities have been received Stream get onProvisioningCapabilities => _provisioningCapabilitiesController.stream; + final _provisioningCapabilitiesController = StreamController(); + /// A [Stream] that will contain the [DiscoveredDevice] when the provisioning invite has been sent Stream get onProvisioningInvitation => _provisioningInvitationController.stream; + final _provisioningInvitationController = StreamController(); + /// A [Stream] that will contain the [DiscoveredDevice] when provisioning is completed and we try to reconnect to the new node Stream get onProvisioningReconnect => _provisioningReconnectController.stream; + final _provisioningReconnectController = StreamController(); + /// A [Stream] that will contain the [DiscoveredDevice] when the mesh composition data has been received Stream get onConfigCompositionDataStatus => _onConfigCompositionDataStatusController.stream; + final _onConfigCompositionDataStatusController = StreamController(); + /// A [Stream] that will contain the [DiscoveredDevice] when the app key has been received by the new node Stream get onConfigAppKeyStatus => _onConfigAppKeyStatusController.stream; + final _onConfigAppKeyStatusController = StreamController(); + + /// A [Stream] that will contain a [BleManagerCallbacksError] (unexpected error that should be handled by plugin user) Stream get onProvisioningGattError => _provisioningGattErrorController.stream; + final _provisioningGattErrorController = StreamController(); + /// Will clear used resources Future dispose() => Future.wait([ _provisioningController.close(), _provisioningCapabilitiesController.close(), @@ -48,19 +52,21 @@ class ProvisioningEvent extends _ProvisioningEvent { ]); } -StreamSubscription? onBleScannerError; -StreamSubscription? onProvisioningCompletedSubscription; -StreamSubscription? onProvisioningStateChangedSubscription; -StreamSubscription? onProvisioningFailedSubscription; -StreamSubscription? sendProvisioningPduSubscription; -StreamSubscription? onConfigCompositionDataStatusSubscription; -StreamSubscription? onConfigAppKeyStatusSubscription; -StreamSubscription? onDeviceReadySubscription; -StreamSubscription? onDataReceivedSubscription; -StreamSubscription? onMeshPduCreatedSubscription; -StreamSubscription? onGattErrorSubscription; -StreamSubscription? onDataSentSubscription; +// necessary subcriptions to handle the whole provisioning process +StreamSubscription? _onBleScannerError; +StreamSubscription? _onProvisioningCompletedSubscription; +StreamSubscription? _onProvisioningStateChangedSubscription; +StreamSubscription? _onProvisioningFailedSubscription; +StreamSubscription? _sendProvisioningPduSubscription; +StreamSubscription? _onConfigCompositionDataStatusSubscription; +StreamSubscription? _onConfigAppKeyStatusSubscription; +StreamSubscription? _onDeviceReadySubscription; +StreamSubscription? _onDataReceivedSubscription; +StreamSubscription? _onMeshPduCreatedSubscription; +StreamSubscription? _onGattErrorSubscription; +StreamSubscription? _onDataSentSubscription; +/// {@macro provisioning} Future provisioning(MeshManagerApi meshManagerApi, BleMeshManager bleMeshManager, BleScanner bleScanner, DiscoveredDevice device, String serviceDataUuid, {ProvisioningEvent? events}) async { @@ -71,6 +77,7 @@ Future provisioning(MeshManagerApi meshManagerApi, BleMeshM } } +/// {@macro provisioning} Future _provisioning( MeshManagerApi meshManagerApi, BleMeshManager bleMeshManager, @@ -82,18 +89,24 @@ Future _provisioning( throw NrfMeshProvisioningException(ProvisioningFailureCode.meshConfiguration, 'You need to load a meshNetwork before being able to provision a device'); } + // this completer will help providing a Future that corresponds to the process ending final completer = Completer(); + // when this boll is false, it will notify errors via _onGattErrorSubscription late bool isHandlingConnectErrors; + // the node that will be returned after the provisioning process ends late final ProvisionedMeshNode provisionedMeshNode; - //'Undocumented scan throttle' error caught here - onBleScannerError = bleScanner.onScanErrorStream.listen((event) { + _onBleScannerError = bleScanner.onScanErrorStream.listen((event) { _log('Scanner Error : ${event.error}'); }); - + // override callbacks so we can react to BLE events final provisioningCallbacks = BleMeshManagerProvisioningCallbacks(meshManagerApi); bleMeshManager.callbacks = provisioningCallbacks; - onProvisioningCompletedSubscription = meshManagerApi.onProvisioningCompleted.listen((event) async { + // define listeners to handle the provisioning process asynchronously + _onProvisioningCompletedSubscription = meshManagerApi.onProvisioningCompleted.listen((event) async { + // upon provisioning completion, we disconnect from the device, + // scan to check whether it advertises the proper services, + // and then reconnect to it try { await bleMeshManager.refreshDeviceCache(); await bleMeshManager.disconnect(); @@ -135,11 +148,13 @@ Future _provisioning( completer.completeError(NrfMeshProvisioningException(ProvisioningFailureCode.provisioningCompleted, _msg)); } }); - onProvisioningFailedSubscription = meshManagerApi.onProvisioningFailed.listen((event) async { + // If provisioning failed, stop process and notify caller by throwing an error + _onProvisioningFailedSubscription = meshManagerApi.onProvisioningFailed.listen((event) async { completer.completeError(NrfMeshProvisioningException( ProvisioningFailureCode.provisioningFailed, 'Failed to provision device ${deviceToProvision.id}')); }); - onProvisioningStateChangedSubscription = meshManagerApi.onProvisioningStateChanged.listen((event) async { + // define what should be done when receiving some events from the native side + _onProvisioningStateChangedSubscription = meshManagerApi.onProvisioningStateChanged.listen((event) async { if (event.state == 'PROVISIONING_CAPABILITIES') { events?._provisioningCapabilitiesController.add(deviceToProvision); final unprovisionedMeshNode = @@ -178,29 +193,35 @@ Future _provisioning( } } }); - onDeviceReadySubscription = bleMeshManager.callbacks!.onDeviceReady.listen((event) async { + // when connected to device, need to identify it in order to start the provisioning process + _onDeviceReadySubscription = bleMeshManager.callbacks!.onDeviceReady.listen((event) async { if (Platform.isIOS && bleMeshManager.isProvisioningCompleted) { + // this case is here because the 'PROVISIONING_INVITE' is not on iOS native code (for now?) final unicast = await provisionedMeshNode.unicastAddress; await meshManagerApi.sendConfigCompositionDataGet(unicast); } else { await meshManagerApi.identifyNode(serviceDataUuid); } }); - sendProvisioningPduSubscription = meshManagerApi.sendProvisioningPdu.listen((event) async { + // handle sending PDUs + _sendProvisioningPduSubscription = meshManagerApi.sendProvisioningPdu.listen((event) async { await bleMeshManager.sendPdu(event.pdu); }); - onMeshPduCreatedSubscription = meshManagerApi.onMeshPduCreated.listen((event) async { + _onMeshPduCreatedSubscription = meshManagerApi.onMeshPduCreated.listen((event) async { await bleMeshManager.sendPdu(event); }); if (Platform.isAndroid) { - onDataSentSubscription = bleMeshManager.callbacks!.onDataSent.listen((event) async { + // on Android need to call Nordic library to handle sent data parsing + _onDataSentSubscription = bleMeshManager.callbacks!.onDataSent.listen((event) async { await meshManagerApi.handleWriteCallbacks(event.mtu, event.pdu); }); } - onDataReceivedSubscription = bleMeshManager.callbacks!.onDataReceived.listen((event) async { + // handle received data parsing + _onDataReceivedSubscription = bleMeshManager.callbacks!.onDataReceived.listen((event) async { await meshManagerApi.handleNotifications(event.mtu, event.pdu); }); - onGattErrorSubscription = bleMeshManager.callbacks!.onError.listen((event) { + // will notify call and stop process in case of unexpected GATT error + _onGattErrorSubscription = bleMeshManager.callbacks!.onError.listen((event) { _log('received error event : $event'); if (!isHandlingConnectErrors) { // if not in a connection phase where auto retry are implemented, we should notify gatt errors @@ -215,31 +236,39 @@ Future _provisioning( } } }); - onConfigCompositionDataStatusSubscription = meshManagerApi.onConfigCompositionDataStatus.listen((event) async { + // when we received the mesh composition data from the newly provisioned node, we should bind to the network using ConfigAppKey msg + _onConfigCompositionDataStatusSubscription = meshManagerApi.onConfigCompositionDataStatus.listen((event) async { events?._onConfigCompositionDataStatusController.add(deviceToProvision); await meshManagerApi.sendConfigAppKeyAdd(await provisionedMeshNode.unicastAddress); }); - onConfigAppKeyStatusSubscription = meshManagerApi.onConfigAppKeyStatus.listen((event) async { + // when ConfigAppKey has been received, the provisioning is successful ! + _onConfigAppKeyStatusSubscription = meshManagerApi.onConfigAppKeyStatus.listen((event) async { events?._onConfigAppKeyStatusController.add(deviceToProvision); completer.complete(provisionedMeshNode); }); try { + // disconnect from device if any await bleMeshManager.refreshDeviceCache(); await bleMeshManager.disconnect(); + // auto retry connect to the target device _connectRetryCount = 0; isHandlingConnectErrors = true; await _connect(bleMeshManager, deviceToProvision); isHandlingConnectErrors = false; + // wait for listeners to do their job await completer.future; + // cleanup resources await meshManagerApi.cleanProvisioningData(); await bleMeshManager.refreshDeviceCache(); await bleMeshManager.disconnect(); - cancelProvisioningCallbackSubscription(bleMeshManager); + _cancelProvisioningCallbackSubscription(bleMeshManager); _log('provisioning success !'); return provisionedMeshNode; } catch (e) { _log('caught error during provisioning... $e'); + // need to clean up data/resources and properly cancel the provisioning process await cancelProvisioning(meshManagerApi, bleScanner, bleMeshManager); + // depending on the error, always try to throw a NrfMeshProvisioningException to ease downstream error handling if (e is NrfMeshProvisioningException) { rethrow; } else if (e is GenericFailure || e is BleManagerException || e is TimeoutException) { @@ -253,13 +282,16 @@ Future _provisioning( } throw NrfMeshProvisioningException(ProvisioningFailureCode.initialConnection, message); } else { - // unknown error that should be diagnosed + // unknown error that should be diagnosed (please file an issue) throw NrfMeshProvisioningException(ProvisioningFailureCode.unknown, '$e'); } } } late int _connectRetryCount; + +/// A method to handle the connections. +/// It may auto retry depending on the failures that could occur. Future _connect(BleMeshManager bleMeshManager, DiscoveredDevice deviceToConnect) async { _connectRetryCount++; await bleMeshManager @@ -267,6 +299,10 @@ Future _connect(BleMeshManager bleMeshManager, DiscoveredDevice deviceToCo .catchError((e) async => await _onConnectError(e, bleMeshManager, deviceToConnect)); } +/// The method that implements the error handling for BLE connection. +/// +/// Some errors can be overcome by a simple retry, +/// others are considered unhandled and this method will rethrow them. Future _onConnectError(Object e, BleMeshManager bleMeshManager, DiscoveredDevice deviceToConnect) async { _log('caught error during connect $e'); if (e is GenericFailure) { @@ -320,31 +356,36 @@ Future _onConnectError(Object e, BleMeshManager bleMeshManager, Discovered } } -void cancelProvisioningCallbackSubscription(BleMeshManager bleMeshManager) { - onProvisioningCompletedSubscription?.cancel(); - onProvisioningStateChangedSubscription?.cancel(); - onProvisioningFailedSubscription?.cancel(); - sendProvisioningPduSubscription?.cancel(); - onConfigCompositionDataStatusSubscription?.cancel(); - onConfigAppKeyStatusSubscription?.cancel(); - onDeviceReadySubscription?.cancel(); - onDataReceivedSubscription?.cancel(); - onMeshPduCreatedSubscription?.cancel(); - onGattErrorSubscription?.cancel(); - onBleScannerError?.cancel(); - if (Platform.isAndroid) onDataSentSubscription?.cancel(); +/// Will clear stream subscriptions used for the provisioning process +void _cancelProvisioningCallbackSubscription(BleMeshManager bleMeshManager) { + _onProvisioningCompletedSubscription?.cancel(); + _onProvisioningStateChangedSubscription?.cancel(); + _onProvisioningFailedSubscription?.cancel(); + _sendProvisioningPduSubscription?.cancel(); + _onConfigCompositionDataStatusSubscription?.cancel(); + _onConfigAppKeyStatusSubscription?.cancel(); + _onDeviceReadySubscription?.cancel(); + _onDataReceivedSubscription?.cancel(); + _onMeshPduCreatedSubscription?.cancel(); + _onGattErrorSubscription?.cancel(); + _onBleScannerError?.cancel(); + if (Platform.isAndroid) _onDataSentSubscription?.cancel(); if (bleMeshManager.callbacks != null) bleMeshManager.callbacks!.dispose(); } +/// {@macro cancel_provisioning} Future cancelProvisioning( MeshManagerApi meshManagerApi, BleScanner bleScanner, BleMeshManager bleMeshManager) async { if (Platform.isIOS || Platform.isAndroid) { _log('should cancel provisioning'); + // try to dispose any resources used by provisioning process try { bleScanner.dispose(); - final cachedProvisionedMeshNodeUuid = await meshManagerApi.cachedProvisionedMeshNodeUuid(); if (bleMeshManager.isProvisioningCompleted && cachedProvisionedMeshNodeUuid != null) { + // a node has been added to the network, but we want to cancel + + // get the unwanted node final nodes = await meshManagerApi.meshNetwork!.nodes; ProvisionedMeshNode? nodeToDelete; try { @@ -352,18 +393,21 @@ Future cancelProvisioning( } on StateError catch (e) { _log('node not found in network\n$e'); } - + // if found, try first to send a ConfigNodeReset if (nodeToDelete != null) { final status = await meshManagerApi.deprovision(nodeToDelete); if (status.success == false) { + // manually delete node from network (WARNING: the device may still be in provisioned state) await meshManagerApi.meshNetwork!.deleteNode(cachedProvisionedMeshNodeUuid); } } } + // remove any data in native side await meshManagerApi.cleanProvisioningData(); + // disconnect await bleMeshManager.refreshDeviceCache(); await bleMeshManager.disconnect(); - cancelProvisioningCallbackSubscription(bleMeshManager); + _cancelProvisioningCallbackSubscription(bleMeshManager); return true; } catch (e) { _log('ERROR - $e'); @@ -374,17 +418,13 @@ Future cancelProvisioning( } } -Future deprovision(MeshManagerApi meshManagerApi, ProvisionedMeshNode meshNode) { - if (Platform.isIOS || Platform.isAndroid) { - return meshManagerApi.deprovision(meshNode); - } else { - throw UnsupportedError('Platform ${Platform.operatingSystem} is not supported'); - } -} - +/// {@template prov_ble_manager} +/// A minimal implementation of [BleMeshManagerCallbacks] +/// {@endtemplate} class BleMeshManagerProvisioningCallbacks extends BleMeshManagerCallbacks { final MeshManagerApi meshManagerApi; + /// {@macro prov_ble_manager} BleMeshManagerProvisioningCallbacks(this.meshManagerApi); @override diff --git a/plugin_channels.md b/plugin_channels.md index 5c817c09e..3bb5685d4 100644 --- a/plugin_channels.md +++ b/plugin_channels.md @@ -22,18 +22,6 @@ await widget.nordicNrfMesh.platformVersion; This method returns a string e.g. `"iOS 11.0"` - - -### createMeshManagerApi - -``` -await widget.nordicNrfMesh.meshManagerApi; -``` - -This method returns nothing, no errors is thrown - - - ## DoozMeshManagerApiChannel ### loadMeshNetwork diff --git a/pubspec.lock b/pubspec.lock index b6728aad7..e9f8e6d98 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -28,7 +28,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.8.1" + version: "2.8.2" boolean_selector: dependency: transitive description: @@ -98,7 +98,7 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" charcode: dependency: transitive description: @@ -311,9 +311,16 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10" + version: "0.12.11" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.3" meta: - dependency: "direct main" + dependency: transitive description: name: meta url: "https://pub.dartlang.org" @@ -484,7 +491,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.2" + version: "0.4.8" timing: dependency: transitive description: @@ -499,20 +506,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.0" - uuid: - dependency: "direct dev" - description: - name: uuid - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.4" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" + version: "2.1.1" watcher: dependency: transitive description: @@ -535,5 +535,5 @@ packages: source: hosted version: "3.1.0" sdks: - dart: ">=2.12.0 <3.0.0" + dart: ">=2.14.0 <3.0.0" flutter: ">=2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 9548fbaa0..7b53f791f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,21 +1,26 @@ name: nordic_nrf_mesh -description: A Flutter plugin to enable mesh network management and communication using Nordic's SDKs. It also provides the ability to open BLE connection with mesh nodes using some other flutter package. -version: 0.9.1 +description: A Flutter plugin to enable mesh network management and communication using Nordic's SDKs. It also provides the ability to open BLE connection with mesh nodes using some other Flutter plugin. +version: 0.10.0 +homepage: https://www.dooz-domotique.com/ +repository: https://github.com/OZEO-DOOZ/nrf_mesh_plugin +issue_tracker: https://github.com/OZEO-DOOZ/nrf_mesh_plugin/issues environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.14.0 <3.0.0" flutter: ">=2.0.0 <3.0.0" dependencies: flutter: sdk: flutter + # BLE handling flutter_reactive_ble: ^4.0.1 + rxdart: ^0.27.1 + retry: ^3.1.0 + # Models json_annotation: ^4.0.0 freezed_annotation: ^0.14.0 - meta: ^1.3.0 - rxdart: ^0.27.1 + # static analysis flutter_lints: ^1.0.4 - retry: ^3.1.0 dev_dependencies: flutter_test: @@ -23,17 +28,8 @@ dev_dependencies: build_runner: ^2.1.0 json_serializable: ^4.1.0 freezed: ^0.14.0 - uuid: ^3.0.0-nullsafety.0 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec -# The following section is specific to Flutter. flutter: - # This section identifies this Flutter project as a plugin project. - # The 'pluginClass' and Android 'package' identifiers should not ordinarily - # be modified. They are used by the tooling to maintain consistency when - # adding or updating assets for this project. plugin: platforms: android: @@ -41,32 +37,3 @@ flutter: pluginClass: NordicNrfMeshPlugin ios: pluginClass: NordicNrfMeshPlugin - # To add assets to your plugin package, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - # - # For details regarding assets in packages, see - # https://flutter.dev/assets-and-images/#from-packages - # - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - # To add custom fonts to your plugin package, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts in packages, see - # https://flutter.dev/custom-fonts/#from-packages diff --git a/test/nordic_nrf_mesh_test.dart b/test/nordic_nrf_mesh_test.dart index 4c28dd400..78b193b53 100644 --- a/test/nordic_nrf_mesh_test.dart +++ b/test/nordic_nrf_mesh_test.dart @@ -3,21 +3,28 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:nordic_nrf_mesh/nordic_nrf_mesh.dart'; void main() { + // variables const channel = MethodChannel('fr.dooz.nordic_nrf_mesh/methods'); - + late final NordicNrfMesh nordicNrfMesh; + late final MeshManagerApi meshManagerApi; TestWidgetsFlutterBinding.ensureInitialized(); - - setUp(() { + // init/dispose + setUpAll(() { channel.setMockMethodCallHandler((MethodCall methodCall) async { return '42'; }); + nordicNrfMesh = NordicNrfMesh(); + meshManagerApi = nordicNrfMesh.meshManagerApi; }); - - tearDown(() { + tearDownAll(() { channel.setMockMethodCallHandler(null); + meshManagerApi.dispose(); }); - + // tests test('getPlatformVersion', () async { expect(await NordicNrfMesh().platformVersion, '42'); }); + test('mesh network is null by default', () { + expect(meshManagerApi.meshNetwork, isNull); + }); }