├── .editorconfig ├── .eslintrc.js ├── .gitattributes ├── .github ├── actions │ └── setup │ │ └── action.yml └── workflows │ └── ci.yml ├── .gitignore ├── .nvmrc ├── .prettierrc.js ├── .watchmanconfig ├── .yarnrc.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── android ├── build.gradle ├── gradle.properties ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ └── com │ │ └── espidfprovisioning │ │ ├── EspIdfProvisioningModule.kt │ │ └── EspIdfProvisioningPackage.kt │ ├── newarch │ └── EspIdfProvisioningSpec.kt │ └── oldarch │ └── EspIdfProvisioningSpec.kt ├── babel.config.js ├── example ├── .bundle │ └── config ├── .watchmanconfig ├── Gemfile ├── README.md ├── android │ ├── app │ │ ├── build.gradle │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── espidfprovisioningexample │ │ │ │ ├── MainActivity.kt │ │ │ │ └── MainApplication.kt │ │ │ └── res │ │ │ ├── drawable │ │ │ └── rn_edit_text_material.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── app.json ├── babel.config.js ├── index.js ├── ios │ ├── .xcode.env │ ├── AppDelegate.swift │ ├── EspIdfProvisioningExample-Bridging-Header.h │ ├── EspIdfProvisioningExample.swift │ ├── EspIdfProvisioningExample.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── EspIdfProvisioningExample.xcscheme │ ├── EspIdfProvisioningExample.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── EspIdfProvisioningExample │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── LaunchScreen.storyboard │ │ └── PrivacyInfo.xcprivacy │ ├── EspIdfProvisioningExampleTests │ │ ├── EspIdfProvisioningExampleTests.m │ │ └── Info.plist │ ├── Podfile │ └── Podfile.lock ├── jest.config.js ├── metro.config.js ├── package.json ├── react-native.config.js ├── src │ ├── App.tsx │ ├── DeviceScreen.tsx │ ├── HomeScreen.tsx │ ├── ProvisionScreen.tsx │ ├── SendDataScreen.tsx │ ├── SettingsScreen.tsx │ ├── WifiListScreen.tsx │ ├── WifiPasswordScreen.tsx │ ├── theme.tsx │ └── types.ts └── yarn.lock ├── ios ├── EspIdfProvisioning-Bridging-Header.h ├── EspIdfProvisioning.mm ├── EspIdfProvisioning.swift └── EspIdfProvisioning.xcodeproj │ └── project.pbxproj ├── lefthook.yml ├── package.json ├── provisioning.gif ├── react-native-esp-idf-provisioning.podspec ├── scripts └── bootstrap.js ├── src ├── NativeEspIdfProvisioning.ts ├── __tests__ │ └── index.test.tsx ├── index.tsx └── types.ts ├── tsconfig.build.json ├── tsconfig.json ├── turbo.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | indent_style = space 10 | indent_size = 2 11 | 12 | end_of_line = lf 13 | charset = utf-8 14 | trim_trailing_whitespace = true 15 | insert_final_newline = true 16 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: '@react-native', 4 | }; 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | # specific for windows script files 3 | *.bat text eol=crlf -------------------------------------------------------------------------------- /.github/actions/setup/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup 2 | description: Setup Node.js and install dependencies 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | - name: Enable Corepack 8 | run: corepack enable 9 | shell: bash 10 | 11 | - name: Setup Node.js 12 | uses: actions/setup-node@v4 13 | with: 14 | node-version-file: .nvmrc 15 | 16 | - name: Cache dependencies 17 | id: yarn-cache 18 | uses: actions/cache@v4 19 | with: 20 | path: | 21 | **/node_modules 22 | .yarn/install-state.gz 23 | key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}-${{ hashFiles('**/package.json', '!node_modules/**') }} 24 | restore-keys: | 25 | ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 26 | ${{ runner.os }}-yarn- 27 | 28 | - name: Install dependencies 29 | if: steps.yarn-cache.outputs.cache-hit != 'true' 30 | run: | 31 | yarn --cwd example install --immutable 32 | yarn install --immutable 33 | shell: bash 34 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | lint: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | 17 | - name: Setup 18 | uses: ./.github/actions/setup 19 | 20 | - name: Lint files 21 | run: yarn lint 22 | 23 | - name: Typecheck files 24 | run: yarn typecheck 25 | 26 | test: 27 | runs-on: ubuntu-latest 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@v4 31 | 32 | - name: Setup 33 | uses: ./.github/actions/setup 34 | 35 | - name: Run unit tests 36 | run: yarn test --maxWorkers=2 --coverage 37 | 38 | build-library: 39 | runs-on: ubuntu-latest 40 | steps: 41 | - name: Checkout 42 | uses: actions/checkout@v4 43 | 44 | - name: Setup 45 | uses: ./.github/actions/setup 46 | 47 | - name: Build package 48 | run: yarn prepack 49 | 50 | build-android: 51 | runs-on: ubuntu-latest 52 | env: 53 | TURBO_CACHE_DIR: .turbo/android 54 | steps: 55 | - name: Checkout 56 | uses: actions/checkout@v4 57 | 58 | - name: Setup 59 | uses: ./.github/actions/setup 60 | 61 | - name: Cache turborepo for Android 62 | uses: actions/cache@v4 63 | with: 64 | path: ${{ env.TURBO_CACHE_DIR }} 65 | key: ${{ runner.os }}-turborepo-android-${{ hashFiles('**/yarn.lock') }} 66 | restore-keys: | 67 | ${{ runner.os }}-turborepo-android- 68 | 69 | - name: Check turborepo cache for Android 70 | run: | 71 | TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:android').cache.status") 72 | 73 | if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then 74 | echo "turbo_cache_hit=1" >> $GITHUB_ENV 75 | fi 76 | 77 | - name: Install JDK 78 | if: env.turbo_cache_hit != 1 79 | uses: actions/setup-java@v4 80 | with: 81 | distribution: 'zulu' 82 | java-version: '17' 83 | 84 | - name: Finalize Android SDK 85 | if: env.turbo_cache_hit != 1 86 | run: | 87 | /bin/bash -c "yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses > /dev/null" 88 | echo "ANDROID_HOME=$ANDROID_HOME" >> $GITHUB_ENV 89 | echo "sdk.dir=$ANDROID_HOME" > example/android/local.properties 90 | 91 | - name: Cache Gradle 92 | if: env.turbo_cache_hit != 1 93 | uses: actions/cache@v4 94 | with: 95 | path: | 96 | ~/.gradle/wrapper 97 | ~/.gradle/caches 98 | key: ${{ runner.os }}-gradle-${{ hashFiles('example/android/gradle/wrapper/gradle-wrapper.properties') }} 99 | restore-keys: | 100 | ${{ runner.os }}-gradle- 101 | 102 | - name: Build example for Android 103 | env: 104 | JAVA_OPTS: "-XX:MaxHeapSize=6g" 105 | run: | 106 | yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" 107 | 108 | build-ios: 109 | runs-on: macos-14 110 | env: 111 | TURBO_CACHE_DIR: .turbo/ios 112 | steps: 113 | - name: Checkout 114 | uses: actions/checkout@v4 115 | 116 | - name: Setup 117 | uses: ./.github/actions/setup 118 | 119 | - name: Cache turborepo for iOS 120 | uses: actions/cache@v4 121 | with: 122 | path: ${{ env.TURBO_CACHE_DIR }} 123 | key: ${{ runner.os }}-turborepo-ios-${{ hashFiles('**/yarn.lock') }} 124 | restore-keys: | 125 | ${{ runner.os }}-turborepo-ios- 126 | 127 | - name: Check turborepo cache for iOS 128 | run: | 129 | TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:ios').cache.status") 130 | 131 | if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then 132 | echo "turbo_cache_hit=1" >> $GITHUB_ENV 133 | fi 134 | 135 | - name: Cache cocoapods 136 | if: env.turbo_cache_hit != 1 137 | id: cocoapods-cache 138 | uses: actions/cache@v4 139 | with: 140 | path: | 141 | **/ios/Pods 142 | key: ${{ runner.os }}-cocoapods-${{ hashFiles('example/ios/Podfile.lock') }} 143 | restore-keys: | 144 | ${{ runner.os }}-cocoapods- 145 | 146 | - name: Install cocoapods 147 | if: env.turbo_cache_hit != 1 && steps.cocoapods-cache.outputs.cache-hit != 'true' 148 | run: | 149 | cd example/ios 150 | pod install 151 | env: 152 | NO_FLIPPER: 1 153 | 154 | - name: Build example for iOS 155 | run: | 156 | yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" 157 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # XDE 6 | .expo/ 7 | 8 | # VSCode 9 | .vscode/ 10 | jsconfig.json 11 | 12 | # Xcode 13 | # 14 | build/ 15 | *.pbxuser 16 | !default.pbxuser 17 | *.mode1v3 18 | !default.mode1v3 19 | *.mode2v3 20 | !default.mode2v3 21 | *.perspectivev3 22 | !default.perspectivev3 23 | xcuserdata 24 | *.xccheckout 25 | *.moved-aside 26 | DerivedData 27 | *.hmap 28 | *.ipa 29 | *.xcuserstate 30 | project.xcworkspace 31 | **/.xcode.env.local 32 | 33 | # Android/IJ 34 | # 35 | .classpath 36 | .cxx 37 | .gradle 38 | .idea 39 | .project 40 | .settings 41 | local.properties 42 | android.iml 43 | 44 | # Cocoapods 45 | # 46 | example/ios/Pods 47 | 48 | # Ruby 49 | example/vendor/ 50 | 51 | # node.js 52 | # 53 | node_modules/ 54 | npm-debug.log 55 | yarn-debug.log 56 | yarn-error.log 57 | 58 | # BUCK 59 | buck-out/ 60 | \.buckd/ 61 | android/app/libs 62 | android/keystores/debug.keystore 63 | 64 | # Expo 65 | .expo/ 66 | 67 | # Turborepo 68 | .turbo/ 69 | 70 | # generated by bob 71 | lib/ 72 | 73 | # Yarn 74 | .yarn/* 75 | !.yarn/patches 76 | !.yarn/plugins 77 | !.yarn/releases 78 | !.yarn/sdks 79 | !.yarn/versions 80 | example/.yarn/* 81 | !example/.yarn/patches 82 | !example/.yarn/plugins 83 | !example/.yarn/releases 84 | !example/.yarn/sdks 85 | !example/.yarn/versions 86 | 87 | .kotlin/ 88 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v20 2 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | arrowParens: 'avoid', 3 | bracketSameLine: true, 4 | bracketSpacing: false, 5 | singleQuote: true, 6 | trailingComma: 'all', 7 | }; 8 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, caste, color, religion, or sexual 11 | identity and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the overall 27 | community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or advances of 32 | any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email address, 36 | without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official e-mail address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | [INSERT CONTACT METHOD]. 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series of 87 | actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or permanent 94 | ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within the 114 | community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.1, available at 120 | [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. 121 | 122 | Community Impact Guidelines were inspired by 123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available at 127 | [https://www.contributor-covenant.org/translations][translations]. 128 | 129 | [homepage]: https://www.contributor-covenant.org 130 | [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html 131 | [Mozilla CoC]: https://github.com/mozilla/diversity 132 | [FAQ]: https://www.contributor-covenant.org/faq 133 | [translations]: https://www.contributor-covenant.org/translations 134 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are always welcome, no matter how large or small! 4 | 5 | We want this community to be friendly and respectful to each other. Please follow it in all your interactions with the project. Before contributing, please read the [code of conduct](./CODE_OF_CONDUCT.md). 6 | 7 | ## Development workflow 8 | 9 | To get started with the project, run `yarn` in the root directory to install the required dependencies for each package: 10 | 11 | ```sh 12 | yarn 13 | ``` 14 | 15 | > While it's possible to use [`npm`](https://github.com/npm/cli), the tooling is built around [`yarn`](https://classic.yarnpkg.com/), so you'll have an easier time if you use `yarn` for development. 16 | 17 | While developing, you can run the [example app](/example/) to test your changes. Any changes you make in your library's JavaScript code will be reflected in the example app without a rebuild. If you change any native code, then you'll need to rebuild the example app. 18 | 19 | To start the packager: 20 | 21 | ```sh 22 | yarn example start 23 | ``` 24 | 25 | To run the example app on Android: 26 | 27 | ```sh 28 | yarn example android 29 | ``` 30 | 31 | To run the example app on iOS: 32 | 33 | ```sh 34 | yarn example ios 35 | ``` 36 | 37 | By default, the example is configured to build with the old architecture. To run the example with the new architecture, you can do the following: 38 | 39 | 1. For Android, run: 40 | 41 | ```sh 42 | ORG_GRADLE_PROJECT_newArchEnabled=true yarn example android 43 | ``` 44 | 45 | 2. For iOS, run: 46 | 47 | ```sh 48 | RCT_NEW_ARCH_ENABLED=1 yarn example pods 49 | yarn example ios 50 | ``` 51 | 52 | If you are building for a different architecture than your previous build, make sure to remove the build folders first. You can run the following command to cleanup all build folders: 53 | 54 | ```sh 55 | yarn clean 56 | ``` 57 | 58 | To confirm that the app is running with the new architecture, you can check the Metro logs for a message like this: 59 | 60 | ```sh 61 | Running "EspIdfProvisioningExample" with {"fabric":true,"initialProps":{"concurrentRoot":true},"rootTag":1} 62 | ``` 63 | 64 | Note the `"fabric":true` and `"concurrentRoot":true` properties. 65 | 66 | Make sure your code passes TypeScript and ESLint. Run the following to verify: 67 | 68 | ```sh 69 | yarn typecheck 70 | yarn lint 71 | ``` 72 | 73 | To fix formatting errors, run the following: 74 | 75 | ```sh 76 | yarn lint --fix 77 | ``` 78 | 79 | Remember to add tests for your change if possible. Run the unit tests by: 80 | 81 | ```sh 82 | yarn test 83 | ``` 84 | 85 | To edit the Objective-C or Swift files, open `example/ios/EspIdfProvisioningExample.xcworkspace` in XCode and find the source files at `Pods > Development Pods > react-native-esp-idf-provisioning`. 86 | 87 | To edit the Java or Kotlin files, open `example/android` in Android studio and find the source files at `react-native-esp-idf-provisioning` under `Android`. 88 | 89 | 90 | ### Commit message convention 91 | 92 | We follow the [conventional commits specification](https://www.conventionalcommits.org/en) for our commit messages: 93 | 94 | - `fix`: bug fixes, e.g. fix crash due to deprecated method. 95 | - `feat`: new features, e.g. add new method to the module. 96 | - `refactor`: code refactor, e.g. migrate from class components to hooks. 97 | - `docs`: changes into documentation, e.g. add usage example for the module.. 98 | - `test`: adding or updating tests, e.g. add integration tests using detox. 99 | - `chore`: tooling changes, e.g. change CI config. 100 | 101 | Our pre-commit hooks verify that your commit message matches this format when committing. 102 | 103 | ### Linting and tests 104 | 105 | [ESLint](https://eslint.org/), [Prettier](https://prettier.io/), [TypeScript](https://www.typescriptlang.org/) 106 | 107 | We use [TypeScript](https://www.typescriptlang.org/) for type checking, [ESLint](https://eslint.org/) with [Prettier](https://prettier.io/) for linting and formatting the code, and [Jest](https://jestjs.io/) for testing. 108 | 109 | Our pre-commit hooks verify that the linter and tests pass when committing. 110 | 111 | ### Publishing to npm 112 | 113 | We use [release-it](https://github.com/release-it/release-it) to make it easier to publish new versions. It handles common tasks like bumping version based on semver, creating tags and releases etc. 114 | 115 | To publish new versions, run the following: 116 | 117 | ```sh 118 | yarn release 119 | ``` 120 | 121 | ### Scripts 122 | 123 | The `package.json` file contains various scripts for common tasks: 124 | 125 | - `yarn bootstrap`: setup project by installing all dependencies and pods. 126 | - `yarn typecheck`: type-check files with TypeScript. 127 | - `yarn lint`: lint files with ESLint. 128 | - `yarn test`: run unit tests with Jest. 129 | - `yarn example start`: start the Metro server for the example app. 130 | - `yarn example android`: run the example app on Android. 131 | - `yarn example ios`: run the example app on iOS. 132 | 133 | ### Sending a pull request 134 | 135 | > **Working on your first pull request?** You can learn how from this _free_ series: [How to Contribute to an Open Source Project on GitHub](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github). 136 | 137 | When you're sending a pull request: 138 | 139 | - Prefer small pull requests focused on one change. 140 | - Verify that linters and tests are passing. 141 | - Review the documentation to make sure it looks good. 142 | - Follow the pull request template when opening a pull request. 143 | - For pull requests that change the API or implementation, discuss with maintainers first by opening an issue. 144 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Mateo Gianolio 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-esp-idf-provisioning 2 | 3 | Android and iOS bridge for ESP IDF provisioning. Provides a unified interface for BLE and SoftAP provisioning using the libraries provided by Espressif: 4 | 5 | - https://github.com/espressif/esp-idf-provisioning-android 6 | - https://github.com/espressif/esp-idf-provisioning-ios 7 | 8 | SoftAP mode is not tested and probably does not work yet. Feel free to help with this. See [#6](https://github.com/orbital-systems/react-native-esp-idf-provisioning/issues/6). 9 | 10 | QR code scanning is deliberately not supported. This can be done using other react-native libraries. 11 | 12 | ![Provisioning](provisioning.gif) 13 | 14 | ## Installation 15 | 16 | ```sh 17 | npm install @orbital-systems/react-native-esp-idf-provisioning 18 | ``` 19 | 20 | ## Usage 21 | 22 | ```ts 23 | import { 24 | ESPProvisionManager, 25 | ESPDevice, 26 | ESPTransport, 27 | ESPSecurity, 28 | } from '@orbital-systems/react-native-esp-idf-provisioning'; 29 | 30 | // Method 1. 31 | // Get devices... 32 | let prefix = 'PROV_'; 33 | let transport = ESPTransport.ble; 34 | let security = ESPSecurity.secure2; 35 | const devices = await ESPProvisionManager.searchESPDevices( 36 | prefix, 37 | transport, 38 | security 39 | ); 40 | 41 | // ... and select device (using picklist, dropdown, w/e) 42 | const device: ESPDevice = devices[0]; 43 | 44 | // Method 2. 45 | // If you know device name and transport/security settings, create a device class instance 46 | const device = new ESPDevice({ 47 | name: 'name', 48 | transport: ESPTransport.ble, 49 | security: ESPSecurity.secure2, 50 | }); 51 | 52 | // Connect to device with proofOfPossession 53 | const proofOfPossession = 'pop'; 54 | await device.connect(proofOfPosession); 55 | 56 | // Connect to device with proofOfPossession + username 57 | const proofOfPossession = 'pop'; 58 | const username = 'username'; 59 | await device.connect(proofOfPosession, null, username); 60 | 61 | // Connect to device with softAP password 62 | const softAPPassword = 'password'; 63 | await device.connect(null, softAPPassword, null); 64 | 65 | // Get wifi list 66 | const wifiList = await device.scanWifiList(); 67 | 68 | // Provision device 69 | const ssid = 'ssid'; 70 | const passphrase = 'passphrase'; 71 | await device.provision(ssid, passphrase); 72 | 73 | // Disconnect 74 | device.disconnect(); 75 | ``` 76 | 77 | ## Why use this library instead of alternatives? 78 | 79 | There have been several attempts to create a react-native bridge of Espressif's native libraries. 80 | 81 | Below are a few examples: 82 | 83 | - https://github.com/manbomb/esp-idf-ble-provisioning-rn - Last commit 3 years ago 84 | - https://github.com/amoghpalnitkar/react-native-esp-idf-provisioning - Last commit 4 years ago 85 | - https://github.com/kafudev/react-native-esp-idf - Last commit 2 years ago 86 | - https://github.com/futuristiclabs/react-native-esp32-idf - Last commit 3 years ago 87 | 88 | We wanted something that is guaranteed to be compatible with the latest versions of Android and iOS. 89 | We also needed it to work with the latest (or almost latest) version of react-native. 90 | 91 | The examples above were great for inspiration, but they were all abandoned a while ago. 92 | 93 | Since this is something we as a company (Orbital Systems) rely on for our current and future 94 | IoT devices, we decided it was worth it to create and maintain a library with help from the community. 95 | 96 | ## Enums 97 | 98 | ```ts 99 | enum ESPTransport { 100 | ble = 'ble', 101 | softap = 'softap', 102 | } 103 | 104 | enum ESPSecurity { 105 | unsecure = 0, 106 | secure = 1, 107 | secure2 = 2, 108 | } 109 | 110 | enum ESPWifiAuthMode { 111 | open = 0, 112 | wep = 1, 113 | wpa2Enterprise = 2, 114 | wpa2Psk = 3, 115 | wpaPsk = 4, 116 | wpaWpa2Psk = 5, 117 | } 118 | ``` 119 | 120 | ## Permissions 121 | 122 | ### Android 123 | 124 | See AndroidManifest.xml in the example project. 125 | 126 | ### iOS 127 | 128 | - Since iOS 13, apps that want to access SSID (Wi-Fi network name) are required to have the location permission. Add key `NSLocationWhenInUseUsageDescription` in Info.plist with proper description. This permission is required to verify iOS device is currently connected with the SoftAP. 129 | 130 | - Since iOS 14, apps that communicate over local network are required to have the local network permission. Add key `NSLocalNetworkUsageDescription` in Info.plist with proper description. This permission is required to send/receive provisioning data with the SoftAP devices. 131 | 132 | - To use BLE, you must add an entry for NSBluetoothAlwaysUsageDescription to your app.json. 133 | 134 | ## Contributing 135 | 136 | See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow. 137 | 138 | ## License 139 | 140 | MIT 141 | 142 | --- 143 | 144 | Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob) 145 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.library" 2 | apply plugin: "kotlin-android" 3 | 4 | buildscript { 5 | repositories { 6 | google() 7 | mavenCentral() 8 | } 9 | 10 | dependencies { 11 | classpath "com.android.tools.build:gradle:7.2.1" 12 | } 13 | } 14 | 15 | def isNewArchitectureEnabled() { 16 | return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true" 17 | } 18 | 19 | def appProject = rootProject.allprojects.find { it.plugins.hasPlugin('com.android.application') } 20 | 21 | if (isNewArchitectureEnabled()) { 22 | apply plugin: "com.facebook.react" 23 | } 24 | 25 | def getExtOrDefault(name) { 26 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["EspIdfProvisioning_" + name] 27 | } 28 | 29 | def getExtOrIntegerDefault(name) { 30 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["EspIdfProvisioning_" + name]).toInteger() 31 | } 32 | 33 | def supportsNamespace() { 34 | def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.') 35 | def major = parsed[0].toInteger() 36 | def minor = parsed[1].toInteger() 37 | 38 | // Namespace support was added in 7.3.0 39 | if (major == 7 && minor >= 3) { 40 | return true 41 | } 42 | 43 | return major >= 8 44 | } 45 | 46 | android { 47 | if (supportsNamespace()) { 48 | namespace "com.espidfprovisioning" 49 | 50 | sourceSets { 51 | main { 52 | manifest.srcFile "src/main/AndroidManifestNew.xml" 53 | } 54 | } 55 | } 56 | 57 | compileSdkVersion getExtOrIntegerDefault("compileSdkVersion") 58 | 59 | defaultConfig { 60 | minSdkVersion getExtOrIntegerDefault("minSdkVersion") 61 | targetSdkVersion getExtOrIntegerDefault("targetSdkVersion") 62 | buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() 63 | consumerProguardFiles 'proguard-rules.pro' 64 | } 65 | 66 | buildTypes { 67 | release { 68 | minifyEnabled false 69 | } 70 | } 71 | 72 | lintOptions { 73 | disable "GradleCompatible" 74 | } 75 | 76 | compileOptions { 77 | sourceCompatibility JavaVersion.VERSION_1_8 78 | targetCompatibility JavaVersion.VERSION_1_8 79 | } 80 | 81 | sourceSets { 82 | main { 83 | if (isNewArchitectureEnabled()) { 84 | java.srcDirs += [ 85 | "src/newarch", 86 | // This is needed to build Kotlin project with NewArch enabled 87 | "${project.buildDir}/generated/source/codegen/java" 88 | ] 89 | } else { 90 | java.srcDirs += ["src/oldarch"] 91 | } 92 | } 93 | } 94 | } 95 | 96 | repositories { 97 | mavenCentral() 98 | google() 99 | maven { url 'https://jitpack.io' } 100 | } 101 | 102 | 103 | dependencies { 104 | // For < 0.71, this will be from the local maven repo 105 | // For > 0.71, this will be replaced by `com.facebook.react:react-android:$version` by react gradle plugin 106 | //noinspection GradleDynamicVersion 107 | implementation("com.facebook.react:react-native:+") 108 | implementation("com.github.espressif:esp-idf-provisioning-android:lib-2.2.3") 109 | implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") 110 | implementation("org.greenrobot:eventbus") 111 | } 112 | 113 | if (isNewArchitectureEnabled()) { 114 | react { 115 | jsRootDir = file("../src/") 116 | libraryName = "EspIdfProvisioning" 117 | codegenJavaPackageName = "com.espidfprovisioning" 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | EspIdfProvisioning_kotlinVersion=1.7.0 2 | EspIdfProvisioning_minSdkVersion=21 3 | EspIdfProvisioning_targetSdkVersion=31 4 | EspIdfProvisioning_compileSdkVersion=31 5 | EspIdfProvisioning_ndkversion=21.4.7075529 6 | -------------------------------------------------------------------------------- /android/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -dontwarn com.espressif.provisioning.** 2 | -dontwarn espressif.** 3 | -keep class com.espressif.provisioning.** {*;} 4 | -keepclassmembers class com.espressif.provisioning.** {*;} 5 | -keep class espressif.** {*;} 6 | -keepclassmembers class espressif.** {*;} 7 | -keep interface com.espressif.provisioning.** {*;} 8 | -keep interface espressif.** {*;} 9 | -keep public enum com.espressif.provisioning.** {*;} 10 | -keep public enum espressif.** {*;} 11 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /android/src/main/java/com/espidfprovisioning/EspIdfProvisioningModule.kt: -------------------------------------------------------------------------------- 1 | package com.espidfprovisioning 2 | 3 | import android.Manifest 4 | import android.annotation.SuppressLint 5 | import android.bluetooth.BluetoothDevice 6 | import android.bluetooth.BluetoothManager 7 | import android.bluetooth.le.ScanResult 8 | import android.content.Context 9 | import android.content.pm.PackageManager 10 | import android.os.Build 11 | import androidx.core.content.ContextCompat 12 | import com.espressif.provisioning.DeviceConnectionEvent 13 | import com.espressif.provisioning.ESPConstants 14 | import com.espressif.provisioning.ESPDevice 15 | import com.espressif.provisioning.ESPProvisionManager 16 | import com.espressif.provisioning.WiFiAccessPoint 17 | import com.espressif.provisioning.listeners.BleScanListener 18 | import com.espressif.provisioning.listeners.ProvisionListener 19 | import com.espressif.provisioning.listeners.ResponseListener 20 | import com.espressif.provisioning.listeners.WiFiScanListener 21 | import com.facebook.react.bridge.Arguments 22 | import com.facebook.react.bridge.Promise 23 | import com.facebook.react.bridge.ReactApplicationContext 24 | import com.facebook.react.bridge.ReactMethod 25 | import com.facebook.react.bridge.WritableMap 26 | import org.greenrobot.eventbus.EventBus 27 | import org.greenrobot.eventbus.Subscribe 28 | import org.greenrobot.eventbus.ThreadMode 29 | import org.json.JSONException 30 | import org.json.JSONObject 31 | import java.lang.Exception 32 | import java.util.ArrayList 33 | import java.util.Base64 34 | 35 | 36 | inline fun T?.guard(nullClause: () -> Nothing): T { 37 | return this ?: nullClause() 38 | } 39 | 40 | fun BluetoothDevice.isAlreadyConnected(): Boolean { 41 | return try { 42 | javaClass.getMethod("isConnected").invoke(this) as? Boolean? ?: false 43 | } catch (e: Throwable) { 44 | false 45 | } 46 | } 47 | 48 | @OptIn(kotlin.ExperimentalStdlibApi::class) 49 | class EspIdfProvisioningModule internal constructor(context: ReactApplicationContext?) : EspIdfProvisioningSpec(context) { 50 | override fun getName(): String { 51 | return NAME 52 | } 53 | 54 | companion object { 55 | const val NAME = "EspIdfProvisioning" 56 | } 57 | 58 | private val espProvisionManager = ESPProvisionManager.getInstance(context) 59 | private val espDevices = HashMap() 60 | private val bluetoothAdapter = (context?.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter 61 | 62 | private fun hasBluetoothPermissions(): Boolean { 63 | if (Build.VERSION.SDK_INT <= 30) { 64 | return ContextCompat.checkSelfPermission(reactApplicationContext, Manifest.permission.BLUETOOTH) == PackageManager.PERMISSION_GRANTED && 65 | ContextCompat.checkSelfPermission(reactApplicationContext, Manifest.permission.BLUETOOTH_ADMIN) == PackageManager.PERMISSION_GRANTED 66 | } 67 | 68 | return ContextCompat.checkSelfPermission(reactApplicationContext, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED && 69 | ContextCompat.checkSelfPermission(reactApplicationContext, Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED 70 | } 71 | 72 | private fun hasWifiPermission(): Boolean { 73 | return ContextCompat.checkSelfPermission(reactApplicationContext, Manifest.permission.CHANGE_WIFI_STATE) == PackageManager.PERMISSION_GRANTED && 74 | ContextCompat.checkSelfPermission(reactApplicationContext, Manifest.permission.ACCESS_WIFI_STATE) == PackageManager.PERMISSION_GRANTED && 75 | ContextCompat.checkSelfPermission(reactApplicationContext, Manifest.permission.ACCESS_NETWORK_STATE) == PackageManager.PERMISSION_GRANTED 76 | } 77 | 78 | private fun hasFineLocationPermission(): Boolean { 79 | return ContextCompat.checkSelfPermission(reactApplicationContext, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED 80 | } 81 | 82 | @SuppressLint("MissingPermission") 83 | @ReactMethod 84 | override fun searchESPDevices(devicePrefix: String, transport: String, security: Double, promise: Promise?) { 85 | val transportEnum = when (transport) { 86 | "softap" -> ESPConstants.TransportType.TRANSPORT_SOFTAP 87 | "ble" -> ESPConstants.TransportType.TRANSPORT_BLE 88 | else -> ESPConstants.TransportType.TRANSPORT_BLE 89 | } 90 | val securityEnum = when (security.toInt()) { 91 | 0 -> ESPConstants.SecurityType.SECURITY_0 92 | 1 -> ESPConstants.SecurityType.SECURITY_1 93 | 2 -> ESPConstants.SecurityType.SECURITY_2 94 | else -> ESPConstants.SecurityType.SECURITY_2 95 | } 96 | 97 | espDevices.clear() 98 | if (transportEnum == ESPConstants.TransportType.TRANSPORT_BLE) { 99 | // Permission checks 100 | if (!hasBluetoothPermissions() || !hasFineLocationPermission()) { 101 | promise?.reject(Error("Missing one of the following permissions: BLUETOOTH, BLUETOOTH_ADMIN, BLUETOOTH_CONNECT, BLUETOOTH_SCAN, ACCESS_FINE_LOCATION")) 102 | return 103 | } 104 | 105 | espProvisionManager.searchBleEspDevices(devicePrefix, object : BleScanListener { 106 | override fun scanStartFailed() { 107 | promise?.reject(Error("Scan could not be started.")) 108 | } 109 | 110 | override fun onPeripheralFound(device: BluetoothDevice?, scanResult: ScanResult?) { 111 | // Can this happen? 112 | if (device == null) { 113 | return 114 | } 115 | 116 | val deviceName = scanResult?.scanRecord?.deviceName 117 | 118 | // No device name 119 | if (deviceName.isNullOrEmpty()) { 120 | return 121 | } 122 | 123 | val serviceUuid = scanResult.scanRecord?.serviceUuids?.getOrNull(0)?.toString() 124 | if (serviceUuid != null && !espDevices.containsKey(deviceName)) { 125 | val espDevice = ESPDevice(reactApplicationContext, transportEnum, securityEnum) 126 | espDevice.bluetoothDevice = device 127 | espDevice.deviceName = deviceName 128 | espDevice.primaryServiceUuid = serviceUuid 129 | espDevices[deviceName] = espDevice 130 | } 131 | } 132 | 133 | override fun scanCompleted() { 134 | if (espDevices.size == 0) { 135 | promise?.reject(Error("No bluetooth device found with given prefix")) 136 | return 137 | } 138 | 139 | val resultArray = Arguments.createArray() 140 | 141 | espDevices.values.forEach { espDevice -> 142 | val resultMap = Arguments.createMap() 143 | resultMap.putString("name", espDevice.deviceName) 144 | resultMap.putString("transport", transport) 145 | resultMap.putInt("security", security.toInt()) 146 | 147 | resultArray.pushMap(resultMap) 148 | } 149 | 150 | promise?.resolve(resultArray) 151 | } 152 | 153 | override fun onFailure(e: Exception?) { 154 | if (e != null) { 155 | promise?.reject(e) 156 | } 157 | } 158 | }) 159 | } else { 160 | if (!hasWifiPermission() || !hasFineLocationPermission()) { 161 | promise?.reject(Error("Missing one of the following permissions: CHANGE_WIFI_STATE, ACCESS_WIFI_STATE, ACCESS_NETWORK_STATE, ACCESS_FINE_LOCATION")) 162 | } 163 | 164 | espProvisionManager.searchWiFiEspDevices(devicePrefix, object : WiFiScanListener { 165 | override fun onWifiListReceived(wifiList: ArrayList?) { 166 | if (wifiList?.size == 0) { 167 | promise?.reject(Error("No wifi device found with given prefix")) 168 | return 169 | } 170 | 171 | val resultArray = Arguments.createArray() 172 | 173 | wifiList?.forEach { wiFiAccessPoint -> 174 | val espDevice = ESPDevice(reactApplicationContext, transportEnum, securityEnum) 175 | espDevice.wifiDevice = wiFiAccessPoint 176 | espDevice.deviceName = wiFiAccessPoint.wifiName 177 | espDevices[wiFiAccessPoint.wifiName] = espDevice 178 | 179 | val resultMap = Arguments.createMap() 180 | 181 | resultMap.putString("name", wiFiAccessPoint.wifiName) 182 | resultMap.putString("transport", transport) 183 | resultMap.putInt("security", wiFiAccessPoint.security) 184 | 185 | resultArray.pushMap(resultMap) 186 | } 187 | 188 | promise?.resolve(resultArray) 189 | } 190 | 191 | override fun onWiFiScanFailed(e: Exception?) { 192 | if (e != null) { 193 | promise?.reject(e) 194 | } 195 | } 196 | }) 197 | } 198 | } 199 | 200 | @SuppressLint("MissingPermission") 201 | @ReactMethod 202 | override fun stopESPDevicesSearch() { 203 | // Permission checks 204 | if (!hasBluetoothPermissions() || !hasFineLocationPermission()) { 205 | // If we don't have permissions we are probably not scanning either, so just return 206 | return 207 | } 208 | 209 | espProvisionManager.stopBleScan() 210 | } 211 | 212 | @SuppressLint("MissingPermission") 213 | @ReactMethod 214 | override fun createESPDevice( 215 | deviceName: String, 216 | transport: String, 217 | security: Double, 218 | proofOfPossession: String?, 219 | softAPPassword: String?, 220 | username: String?, 221 | promise: Promise? 222 | ) { 223 | // Permission checks 224 | if (!hasBluetoothPermissions()) { 225 | promise?.reject(Error("Missing one of the following permissions: BLUETOOTH, BLUETOOTH_ADMIN, BLUETOOTH_CONNECT, BLUETOOTH_SCAN")) 226 | return 227 | } 228 | 229 | val transportEnum = when (transport) { 230 | "softap" -> ESPConstants.TransportType.TRANSPORT_SOFTAP 231 | "ble" -> ESPConstants.TransportType.TRANSPORT_BLE 232 | else -> ESPConstants.TransportType.TRANSPORT_BLE 233 | } 234 | val securityEnum = when (security.toInt()) { 235 | 0 -> ESPConstants.SecurityType.SECURITY_0 236 | 1 -> ESPConstants.SecurityType.SECURITY_1 237 | 2 -> ESPConstants.SecurityType.SECURITY_2 238 | else -> ESPConstants.SecurityType.SECURITY_2 239 | } 240 | 241 | // If no ESP device found in list (no scan has been performed), create a new one 242 | var espDevice = espDevices[deviceName]; 243 | if (espDevice == null) { 244 | espDevice = espProvisionManager.createESPDevice(transportEnum, securityEnum) 245 | espDevices[deviceName] = espDevice 246 | } else { 247 | // This looks weird on first glance, but it catches the edge case when 248 | // a user unpairs the device from Android bluetooth menu and then tries to reconnect 249 | // to it. 250 | // 251 | // I found out it connects just fine but fails to write characteristics and is 252 | // disconnected immediately after trying to write characteristic (by the device). 253 | // 254 | // Pre-emptively disconnecting the device upon creation if it's in bond state NONE seems 255 | // to fix this issue. Don't ask me why ;) 256 | if (espDevice.bluetoothDevice?.bondState == BluetoothDevice.BOND_NONE) { 257 | espDevice.disconnectDevice() 258 | } 259 | } 260 | 261 | if (transportEnum == ESPConstants.TransportType.TRANSPORT_BLE) { 262 | // If the bluetooth device does not exist, try using the bonded one (if it exists) 263 | if (espDevice?.bluetoothDevice == null) { 264 | espDevice?.bluetoothDevice = bluetoothAdapter.bondedDevices.find { bondedDevice -> 265 | bondedDevice.name == deviceName 266 | } 267 | } 268 | 269 | // If the bluetooth device exists and we have a primary service uuid, we will be able to connect to it 270 | if (espDevice?.bluetoothDevice != null && espDevice.primaryServiceUuid != null) { 271 | espDevice.proofOfPossession = proofOfPossession 272 | if (username != null) { 273 | espDevice.userName = username 274 | } 275 | 276 | val result = Arguments.createMap() 277 | result.putString("name", espDevice.deviceName) 278 | result.putString("transport", transport) 279 | result.putInt("security", security.toInt()) 280 | 281 | promise?.resolve(result) 282 | return 283 | } 284 | } else { 285 | if (espDevice?.wifiDevice == null) { 286 | val wifiDevice = WiFiAccessPoint() 287 | wifiDevice.wifiName = deviceName 288 | wifiDevice.password = softAPPassword 289 | 290 | espDevice?.wifiDevice = wifiDevice 291 | } 292 | 293 | val result = Arguments.createMap() 294 | result.putString("name", espDevice?.deviceName) 295 | result.putString("transport", transport) 296 | result.putInt("security", security.toInt()) 297 | 298 | promise?.resolve(result) 299 | return 300 | } 301 | 302 | // Exhausted our other options, perform search in hope of finding the device 303 | searchESPDevices(deviceName, transport, security, object : Promise { 304 | override fun resolve(p0: Any?) { 305 | // If search does not find the device, consider it not found 306 | val espDevice = espDevices[deviceName].guard { 307 | promise?.reject(Error("Device not found.")) 308 | return 309 | } 310 | 311 | // Configure proof of possession 312 | espDevice.proofOfPossession = proofOfPossession 313 | if (username != null) { 314 | espDevice.userName = username 315 | } 316 | 317 | val result = Arguments.createMap() 318 | result.putString("name", espDevice.deviceName) 319 | result.putString("transport", transport) 320 | result.putInt("security", security.toInt()) 321 | 322 | promise?.resolve(result) 323 | } 324 | 325 | override fun reject(message: String) { 326 | promise?.reject(message) 327 | } 328 | 329 | override fun reject(code: String, userInfo: WritableMap) { 330 | promise?.reject(code, userInfo) 331 | } 332 | 333 | override fun reject(code: String, message: String?) { 334 | promise?.reject(code, message) 335 | } 336 | 337 | override fun reject(code: String, message: String?, userInfo: WritableMap) { 338 | promise?.reject(code, message, userInfo) 339 | } 340 | 341 | override fun reject(code: String, message: String?, throwable: Throwable?) { 342 | promise?.reject(code, message, throwable) 343 | } 344 | 345 | override fun reject(code: String, throwable: Throwable?) { 346 | promise?.reject(code, throwable) 347 | } 348 | 349 | override fun reject(code: String, throwable: Throwable?, userInfo: WritableMap) { 350 | promise?.reject(code, throwable, userInfo) 351 | } 352 | 353 | override fun reject(code: String?, message: String?, throwable: Throwable?, userInfo: WritableMap?) { 354 | promise?.reject(code, message, throwable, userInfo) 355 | } 356 | 357 | override fun reject(throwable: Throwable) { 358 | promise?.reject(throwable) 359 | } 360 | 361 | override fun reject(throwable: Throwable, userInfo: WritableMap) { 362 | promise?.reject(throwable, userInfo) 363 | } 364 | }) 365 | } 366 | 367 | @SuppressLint("MissingPermission") 368 | @ReactMethod 369 | override fun connect(deviceName: String, promise: Promise?) { 370 | val espDevice = espDevices[deviceName].guard { 371 | promise?.reject(Error("No ESP device found. Call createESPDevice first.")) 372 | return 373 | } 374 | 375 | if (espDevice.transportType == ESPConstants.TransportType.TRANSPORT_SOFTAP) { 376 | // Permission checks 377 | if (!hasWifiPermission() || !hasFineLocationPermission()) { 378 | promise?.reject(Error("Missing one of the following permissions: CHANGE_WIFI_STATE, ACCESS_WIFI_STATE, ACCESS_NETWORK_STATE, ACCESS_FINE_LOCATION")) 379 | return 380 | } 381 | } 382 | 383 | // If device is already connected, exit early 384 | if (espDevice.transportType == ESPConstants.TransportType.TRANSPORT_BLE && 385 | espDevice.bluetoothDevice?.isAlreadyConnected() == true) { 386 | val result = Arguments.createMap() 387 | result.putString("status", "connected") 388 | promise?.resolve(result) 389 | return 390 | } 391 | 392 | espDevice.connectToDevice() 393 | 394 | EventBus.getDefault().register(object { 395 | @Subscribe(threadMode = ThreadMode.MAIN) 396 | fun onEvent(event: DeviceConnectionEvent) { 397 | when (event.eventType) { 398 | ESPConstants.EVENT_DEVICE_CONNECTED -> { 399 | val result = Arguments.createMap() 400 | result.putString("status", "connected") 401 | promise?.resolve(result) 402 | } 403 | ESPConstants.EVENT_DEVICE_CONNECTION_FAILED -> { 404 | promise?.reject(Error("Device connection failed.")) 405 | } 406 | else -> { 407 | // Do nothing 408 | } 409 | } 410 | 411 | // Unregister event listener after 1 event received 412 | EventBus.getDefault().unregister(this) 413 | } 414 | }) 415 | } 416 | 417 | @ReactMethod 418 | override fun sendData(deviceName: String, path: String, data: String, promise: Promise?) { 419 | val espDevice = espDevices[deviceName].guard { 420 | promise?.reject(Error("No ESP device found. Call createESPDevice first.")) 421 | return 422 | } 423 | 424 | val decodedData = Base64.getDecoder().decode(data) 425 | espDevice.sendDataToCustomEndPoint(path, decodedData, object : ResponseListener { 426 | override fun onSuccess(returnData: ByteArray?) { 427 | val encodedData = Base64.getEncoder().encode(returnData).toString(Charsets.UTF_8) 428 | promise?.resolve(encodedData) 429 | } 430 | 431 | override fun onFailure(e: Exception?) { 432 | if (e != null) { 433 | promise?.reject(e) 434 | } 435 | } 436 | }) 437 | } 438 | 439 | @ReactMethod 440 | override fun scanWifiList(deviceName: String, promise: Promise?) { 441 | val espDevice = espDevices[deviceName].guard { 442 | promise?.reject(Error("No ESP device found. Call createESPDevice first.")) 443 | return 444 | } 445 | 446 | espDevice.scanNetworks(object : WiFiScanListener { 447 | override fun onWifiListReceived(wifiList: ArrayList?) { 448 | val resultArray = Arguments.createArray() 449 | 450 | wifiList?.forEach { item -> 451 | val resultMap = Arguments.createMap() 452 | resultMap.putString("ssid", item.wifiName) 453 | resultMap.putInt("rssi", item.rssi) 454 | resultMap.putInt("auth", item.security) 455 | resultArray.pushMap(resultMap) 456 | } 457 | 458 | promise?.resolve(resultArray) 459 | } 460 | 461 | override fun onWiFiScanFailed(e: Exception?) { 462 | if (e != null) { 463 | promise?.reject(e) 464 | } 465 | } 466 | }) 467 | } 468 | 469 | @ReactMethod 470 | override fun disconnect(deviceName: String) { 471 | espDevices[deviceName]?.disconnectDevice() 472 | } 473 | 474 | @ReactMethod 475 | override fun provision(deviceName: String, ssid: String, passphrase: String, promise: Promise?) { 476 | val espDevice = espDevices[deviceName].guard { 477 | promise?.reject(Error("No ESP device found. Call createESPDevice first.")) 478 | return 479 | } 480 | 481 | espDevice.provision(ssid, passphrase, object : ProvisionListener { 482 | override fun createSessionFailed(e: Exception?) { 483 | if (e != null) { 484 | promise?.reject(e) 485 | } 486 | } 487 | 488 | override fun wifiConfigSent() { 489 | return 490 | } 491 | 492 | override fun wifiConfigFailed(e: Exception?) { 493 | if (e != null) { 494 | promise?.reject(e) 495 | } 496 | } 497 | 498 | override fun wifiConfigApplied() { 499 | return 500 | } 501 | 502 | override fun wifiConfigApplyFailed(e: Exception?) { 503 | if (e != null) { 504 | promise?.reject(e) 505 | } 506 | } 507 | 508 | override fun provisioningFailedFromDevice(failureReason: ESPConstants.ProvisionFailureReason?) { 509 | promise?.reject(Error(failureReason.toString())) 510 | } 511 | 512 | override fun deviceProvisioningSuccess() { 513 | val result = Arguments.createMap() 514 | result.putString("status", "success") 515 | promise?.resolve(result) 516 | } 517 | 518 | override fun onProvisioningFailed(e: Exception?) { 519 | if (e != null) { 520 | promise?.reject(e) 521 | } 522 | } 523 | }) 524 | } 525 | 526 | @ReactMethod 527 | override fun getProofOfPossession(deviceName: String, promise: Promise?) { 528 | val espDevice = espDevices[deviceName].guard { 529 | promise?.reject(Error("No ESP device found. Call createESPDevice first.")) 530 | return 531 | } 532 | 533 | promise?.resolve(espDevice.proofOfPossession) 534 | } 535 | 536 | @ReactMethod 537 | override fun setProofOfPossession(deviceName: String, proofOfPossession: String, promise: Promise?) { 538 | val espDevice = espDevices[deviceName].guard { 539 | promise?.reject(Error("No ESP device found. Call createESPDevice first.")) 540 | return 541 | } 542 | 543 | espDevice.proofOfPossession = proofOfPossession 544 | promise?.resolve(proofOfPossession) 545 | } 546 | 547 | @ReactMethod 548 | override fun getUsername(deviceName: String, promise: Promise?) { 549 | val espDevice = espDevices[deviceName].guard { 550 | promise?.reject(Error("No ESP device found. Call createESPDevice first.")) 551 | return 552 | } 553 | 554 | promise?.resolve(espDevice.userName) 555 | } 556 | 557 | @ReactMethod 558 | override fun setUsername(deviceName: String, username: String, promise: Promise?) { 559 | val espDevice = espDevices[deviceName].guard { 560 | promise?.reject(Error("No ESP device found. Call createESPDevice first.")) 561 | return 562 | } 563 | 564 | espDevice.userName = username 565 | promise?.resolve(username) 566 | } 567 | 568 | @ReactMethod 569 | override fun getDeviceName(deviceName: String, promise: Promise?) { 570 | val espDevice = espDevices[deviceName].guard { 571 | promise?.reject(Error("No ESP device found. Call createESPDevice first.")) 572 | return 573 | } 574 | 575 | promise?.resolve(espDevice.deviceName) 576 | } 577 | 578 | @ReactMethod 579 | override fun setDeviceName(deviceName: String, newDeviceName: String, promise: Promise?) { 580 | val espDevice = espDevices[deviceName].guard { 581 | promise?.reject(Error("No ESP device found. Call createESPDevice first.")) 582 | return 583 | } 584 | 585 | if (newDeviceName == "") { 586 | promise?.reject(Error("Cannot set empty device name.")) 587 | return 588 | } 589 | 590 | // Not sure what this does 591 | espDevice.deviceName = newDeviceName 592 | 593 | promise?.resolve(newDeviceName) 594 | } 595 | 596 | override fun getPrimaryServiceUuid(deviceName: String, promise: Promise?) { 597 | val espDevice = espDevices[deviceName].guard { 598 | promise?.reject(Error("No ESP device found. Call createESPDevice first.")) 599 | return 600 | } 601 | 602 | promise?.resolve(espDevice.primaryServiceUuid) 603 | } 604 | 605 | @ReactMethod 606 | override fun setPrimaryServiceUuid(deviceName: String, primaryServiceUuid: String, promise: Promise?) { 607 | val espDevice = espDevices[deviceName].guard { 608 | promise?.reject(Error("No ESP device found. Call createESPDevice first.")) 609 | return 610 | } 611 | 612 | espDevice.primaryServiceUuid = primaryServiceUuid 613 | promise?.resolve(primaryServiceUuid) 614 | } 615 | 616 | @ReactMethod 617 | override fun getSecurityType(deviceName: String, promise: Promise?) { 618 | val espDevice = espDevices[deviceName].guard { 619 | promise?.reject(Error("No ESP device found. Call createESPDevice first.")) 620 | return 621 | } 622 | 623 | promise?.resolve(espDevice.securityType.ordinal) 624 | } 625 | 626 | @ReactMethod 627 | override fun setSecurityType(deviceName: String, security: Double, promise: Promise?) { 628 | val espDevice = espDevices[deviceName].guard { 629 | promise?.reject(Error("No ESP device found. Call createESPDevice first.")) 630 | return 631 | } 632 | 633 | val securityEnum = when (security.toInt()) { 634 | 0 -> ESPConstants.SecurityType.SECURITY_0 635 | 1 -> ESPConstants.SecurityType.SECURITY_1 636 | 2 -> ESPConstants.SecurityType.SECURITY_2 637 | else -> ESPConstants.SecurityType.SECURITY_2 638 | } 639 | 640 | espDevice.securityType = securityEnum 641 | 642 | promise?.resolve(securityEnum.ordinal) 643 | } 644 | 645 | @ReactMethod 646 | override fun getTransportType(deviceName: String, promise: Promise?) { 647 | val espDevice = espDevices[deviceName].guard { 648 | promise?.reject(Error("No ESP device found. Call createESPDevice first.")) 649 | return 650 | } 651 | 652 | promise?.resolve(espDevice.transportType.toString()) 653 | } 654 | 655 | @ReactMethod 656 | override fun getVersionInfo(deviceName: String, promise: Promise?) { 657 | val espDevice = espDevices[deviceName].guard { 658 | promise?.reject(Error("No ESP device found. Call createESPDevice first.")) 659 | return 660 | } 661 | 662 | val result = Arguments.createMap() 663 | 664 | if (espDevice.versionInfo !== null) { 665 | try { 666 | val protoVersion = JSONObject(espDevice.versionInfo).getJSONObject("prov") 667 | 668 | val prov = Arguments.createMap() 669 | if (protoVersion.has("sec_ver")) { 670 | prov.putInt("sec_ver", protoVersion.optInt("sec_ver")) 671 | } 672 | if (protoVersion.has("ver")) { 673 | prov.putString("ver", protoVersion.optString("ver")) 674 | } 675 | if (protoVersion.has("cap")) { 676 | val capabilities = Arguments.createArray() 677 | val cap = protoVersion.getJSONArray("cap") 678 | for (i in 0.. = HashMap() 21 | val isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED 22 | moduleInfos[EspIdfProvisioningModule.NAME] = ReactModuleInfo( 23 | EspIdfProvisioningModule.NAME, 24 | EspIdfProvisioningModule.NAME, 25 | false, // canOverrideExistingModule 26 | false, // needsEagerInit 27 | true, // hasConstants 28 | false, // isCxxModule 29 | isTurboModule // isTurboModule 30 | ) 31 | moduleInfos 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /android/src/newarch/EspIdfProvisioningSpec.kt: -------------------------------------------------------------------------------- 1 | package com.espidfprovisioning 2 | 3 | import com.facebook.react.bridge.ReactApplicationContext 4 | 5 | abstract class EspIdfProvisioningSpec(context: ReactApplicationContext?) : 6 | NativeEspIdfProvisioningSpec(context) 7 | -------------------------------------------------------------------------------- /android/src/oldarch/EspIdfProvisioningSpec.kt: -------------------------------------------------------------------------------- 1 | package com.espidfprovisioning 2 | 3 | import com.facebook.react.bridge.Promise 4 | import com.facebook.react.bridge.ReactApplicationContext 5 | import com.facebook.react.bridge.ReactContextBaseJavaModule 6 | 7 | abstract class EspIdfProvisioningSpec(context: ReactApplicationContext?) : ReactContextBaseJavaModule(context) { 8 | abstract fun searchESPDevices(devicePrefix: String, transport: String, security: Double, promise: Promise?) 9 | abstract fun stopESPDevicesSearch() 10 | abstract fun createESPDevice(deviceName: String, transport: String, security: Double, proofOfPossession: String?, softAPPassword: String?, username: String?, promise: Promise?) 11 | abstract fun connect(deviceName: String, promise: Promise?) 12 | abstract fun sendData(deviceName: String, path: String, data: String, promise: Promise?) 13 | abstract fun scanWifiList(deviceName: String, promise: Promise?) 14 | abstract fun disconnect(deviceName: String) 15 | abstract fun provision(deviceName: String, ssid: String, passphrase: String, promise: Promise?) 16 | abstract fun getProofOfPossession(deviceName: String, promise: Promise?) 17 | abstract fun setProofOfPossession(deviceName: String, proofOfPossession: String, promise: Promise?) 18 | abstract fun getUsername(deviceName: String, promise: Promise?) 19 | abstract fun setUsername(deviceName: String, username: String, promise: Promise?) 20 | abstract fun getDeviceName(deviceName: String, promise: Promise?) 21 | abstract fun setDeviceName(deviceName: String, newDeviceName: String, promise: Promise?) 22 | abstract fun getPrimaryServiceUuid(deviceName: String, promise: Promise?) 23 | abstract fun setPrimaryServiceUuid(deviceName: String, primaryServiceUuid: String, promise: Promise?) 24 | abstract fun getSecurityType(deviceName: String, promise: Promise?) 25 | abstract fun setSecurityType(deviceName: String, security: Double, promise: Promise?) 26 | abstract fun getTransportType(deviceName: String, promise: Promise?) 27 | abstract fun getVersionInfo(deviceName: String, promise: Promise?) 28 | abstract fun getDeviceCapabilities(deviceName: String, promise: Promise?) 29 | } 30 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:@react-native/babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /example/.bundle/config: -------------------------------------------------------------------------------- 1 | BUNDLE_PATH: "vendor/bundle" 2 | BUNDLE_FORCE_RUBY_PLATFORM: 1 3 | -------------------------------------------------------------------------------- /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /example/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version 4 | ruby ">= 2.6.10" 5 | 6 | # Exclude problematic versions of cocoapods and activesupport that causes build failures. 7 | gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1' 8 | gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0' 9 | gem 'xcodeproj', '< 1.26.0' 10 | gem 'concurrent-ruby', '< 1.3.4' 11 | 12 | # Ruby 3.4.0 has removed some libraries from the standard library. 13 | gem 'bigdecimal' 14 | gem 'logger' 15 | gem 'benchmark' 16 | gem 'mutex_m' 17 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | This is a new [**React Native**](https://reactnative.dev) project, bootstrapped using [`@react-native-community/cli`](https://github.com/react-native-community/cli). 2 | 3 | # Getting Started 4 | 5 | >**Note**: Make sure you have completed the [React Native - Environment Setup](https://reactnative.dev/docs/environment-setup) instructions till "Creating a new application" step, before proceeding. 6 | 7 | ## Step 1: Start the Metro Server 8 | 9 | First, you will need to start **Metro**, the JavaScript _bundler_ that ships _with_ React Native. 10 | 11 | To start Metro, run the following command from the _root_ of your React Native project: 12 | 13 | ```bash 14 | # using npm 15 | npm start 16 | 17 | # OR using Yarn 18 | yarn start 19 | ``` 20 | 21 | ## Step 2: Start your Application 22 | 23 | Let Metro Bundler run in its _own_ terminal. Open a _new_ terminal from the _root_ of your React Native project. Run the following command to start your _Android_ or _iOS_ app: 24 | 25 | ### For Android 26 | 27 | ```bash 28 | # using npm 29 | npm run android 30 | 31 | # OR using Yarn 32 | yarn android 33 | ``` 34 | 35 | ### For iOS 36 | 37 | ```bash 38 | # using npm 39 | npm run ios 40 | 41 | # OR using Yarn 42 | yarn ios 43 | ``` 44 | 45 | If everything is set up _correctly_, you should see your new app running in your _Android Emulator_ or _iOS Simulator_ shortly provided you have set up your emulator/simulator correctly. 46 | 47 | This is one way to run your app — you can also run it directly from within Android Studio and Xcode respectively. 48 | 49 | ## Step 3: Modifying your App 50 | 51 | Now that you have successfully run the app, let's modify it. 52 | 53 | 1. Open `App.tsx` in your text editor of choice and edit some lines. 54 | 2. For **Android**: Press the R key twice or select **"Reload"** from the **Developer Menu** (Ctrl + M (on Window and Linux) or Cmd ⌘ + M (on macOS)) to see your changes! 55 | 56 | For **iOS**: Hit Cmd ⌘ + R in your iOS Simulator to reload the app and see your changes! 57 | 58 | ## Congratulations! :tada: 59 | 60 | You've successfully run and modified your React Native App. :partying_face: 61 | 62 | ### Now what? 63 | 64 | - If you want to add this new React Native code to an existing application, check out the [Integration guide](https://reactnative.dev/docs/integration-with-existing-apps). 65 | - If you're curious to learn more about React Native, check out the [Introduction to React Native](https://reactnative.dev/docs/getting-started). 66 | 67 | # Troubleshooting 68 | 69 | If you can't get this to work, see the [Troubleshooting](https://reactnative.dev/docs/troubleshooting) page. 70 | 71 | # Learn More 72 | 73 | To learn more about React Native, take a look at the following resources: 74 | 75 | - [React Native Website](https://reactnative.dev) - learn more about React Native. 76 | - [Getting Started](https://reactnative.dev/docs/environment-setup) - an **overview** of React Native and how setup your environment. 77 | - [Learn the Basics](https://reactnative.dev/docs/getting-started) - a **guided tour** of the React Native **basics**. 78 | - [Blog](https://reactnative.dev/blog) - read the latest official React Native **Blog** posts. 79 | - [`@facebook/react-native`](https://github.com/facebook/react-native) - the Open Source; GitHub **repository** for React Native. 80 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | apply plugin: "com.facebook.react" 3 | apply plugin: "kotlin-android" 4 | 5 | 6 | /** 7 | * This is the configuration block to customize your React Native Android app. 8 | * By default you don't need to apply any configuration, just uncomment the lines you need. 9 | */ 10 | react { 11 | /* Folders */ 12 | // The root of your project, i.e. where "package.json" lives. Default is '..' 13 | // root = file("../") 14 | // The folder where the react-native NPM package is. Default is ../node_modules/react-native 15 | // reactNativeDir = file("../node_modules/react-native") 16 | // The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen 17 | // codegenDir = file("../node_modules/@react-native/codegen") 18 | // The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js 19 | // cliFile = file("../node_modules/react-native/cli.js") 20 | 21 | /* Variants */ 22 | // The list of variants to that are debuggable. For those we're going to 23 | // skip the bundling of the JS bundle and the assets. By default is just 'debug'. 24 | // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. 25 | // debuggableVariants = ["liteDebug", "prodDebug"] 26 | 27 | /* Bundling */ 28 | // A list containing the node command and its flags. Default is just 'node'. 29 | // nodeExecutableAndArgs = ["node"] 30 | // 31 | // The command to run when bundling. By default is 'bundle' 32 | // bundleCommand = "ram-bundle" 33 | // 34 | // The path to the CLI configuration file. Default is empty. 35 | // bundleConfig = file(../rn-cli.config.js) 36 | // 37 | // The name of the generated asset file containing your JS bundle 38 | // bundleAssetName = "MyApplication.android.bundle" 39 | // 40 | // The entry file for bundle generation. Default is 'index.android.js' or 'index.js' 41 | // entryFile = file("../js/MyApplication.android.js") 42 | // 43 | // A list of extra flags to pass to the 'bundle' commands. 44 | // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle 45 | // extraPackagerArgs = [] 46 | 47 | /* Hermes Commands */ 48 | // The hermes compiler command to run. By default it is 'hermesc' 49 | // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc" 50 | // 51 | // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" 52 | // hermesFlags = ["-O", "-output-source-map"] 53 | 54 | /* Autolinking */ 55 | autolinkLibrariesWithApp() 56 | } 57 | 58 | /** 59 | * Set this to true to Run Proguard on Release builds to minify the Java bytecode. 60 | */ 61 | def enableProguardInReleaseBuilds = false 62 | 63 | /** 64 | * The preferred build flavor of JavaScriptCore (JSC) 65 | * 66 | * For example, to use the international variant, you can use: 67 | * `def jscFlavor = io.github.react-native-community:jsc-android-intl:2026004.+` 68 | * 69 | * The international variant includes ICU i18n library and necessary data 70 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that 71 | * give correct results when using with locales other than en-US. Note that 72 | * this variant is about 6MiB larger per architecture than default. 73 | */ 74 | def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+' 75 | 76 | android { 77 | ndkVersion rootProject.ext.ndkVersion 78 | compileSdkVersion rootProject.ext.compileSdkVersion 79 | buildToolsVersion rootProject.ext.buildToolsVersion 80 | 81 | namespace "com.espidfprovisioningexample" 82 | defaultConfig { 83 | applicationId "com.espidfprovisioningexample" 84 | minSdkVersion rootProject.ext.minSdkVersion 85 | targetSdkVersion rootProject.ext.targetSdkVersion 86 | versionCode 1 87 | versionName "1.0" 88 | } 89 | signingConfigs { 90 | debug { 91 | storeFile file('debug.keystore') 92 | storePassword 'android' 93 | keyAlias 'androiddebugkey' 94 | keyPassword 'android' 95 | } 96 | } 97 | buildTypes { 98 | debug { 99 | signingConfig signingConfigs.debug 100 | } 101 | release { 102 | // Caution! In production, you need to generate your own keystore file. 103 | // see https://reactnative.dev/docs/signed-apk-android. 104 | signingConfig signingConfigs.debug 105 | minifyEnabled enableProguardInReleaseBuilds 106 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 107 | } 108 | } 109 | } 110 | 111 | dependencies { 112 | // The version of react-native is set by the React Native Gradle Plugin 113 | implementation("com.facebook.react:react-android") 114 | implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") 115 | 116 | if (hermesEnabled.toBoolean()) { 117 | implementation("com.facebook.react:hermes-android") 118 | } else { 119 | implementation jscFlavor 120 | } 121 | } 122 | 123 | apply from: file("../../node_modules/react-native-vector-icons/fonts.gradle") 124 | -------------------------------------------------------------------------------- /example/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbital-systems/react-native-esp-idf-provisioning/ce4f914ac99780b944a01e45538e521590586e01/example/android/app/debug.keystore -------------------------------------------------------------------------------- /example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 26 | 27 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 23 | 24 | 32 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/espidfprovisioningexample/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.espidfprovisioningexample 2 | 3 | import android.os.Bundle; 4 | import com.facebook.react.ReactActivity 5 | import com.facebook.react.ReactActivityDelegate 6 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled 7 | import com.facebook.react.defaults.DefaultReactActivityDelegate 8 | 9 | class MainActivity : ReactActivity() { 10 | override fun onCreate(savedInstanceState: Bundle?) { 11 | super.onCreate(null) 12 | } 13 | 14 | /** 15 | * Returns the name of the main component registered from JavaScript. This is used to schedule 16 | * rendering of the component. 17 | */ 18 | override fun getMainComponentName(): String = "EspIdfProvisioningExample" 19 | 20 | /** 21 | * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] 22 | * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] 23 | */ 24 | override fun createReactActivityDelegate(): ReactActivityDelegate = 25 | DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) 26 | } 27 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/espidfprovisioningexample/MainApplication.kt: -------------------------------------------------------------------------------- 1 | package com.espidfprovisioningexample 2 | 3 | import android.app.Application 4 | import com.facebook.react.PackageList 5 | import com.facebook.react.ReactApplication 6 | import com.facebook.react.ReactHost 7 | import com.facebook.react.ReactNativeHost 8 | import com.facebook.react.ReactPackage 9 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load 10 | import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost 11 | import com.facebook.react.defaults.DefaultReactNativeHost 12 | import com.facebook.react.soloader.OpenSourceMergedSoMapping 13 | import com.facebook.soloader.SoLoader 14 | 15 | class MainApplication : Application(), ReactApplication { 16 | 17 | override val reactNativeHost: ReactNativeHost = 18 | object : DefaultReactNativeHost(this) { 19 | override fun getPackages(): List = 20 | PackageList(this).packages.apply { 21 | // Packages that cannot be autolinked yet can be added manually here, for example: 22 | // add(MyReactNativePackage()) 23 | } 24 | 25 | override fun getJSMainModuleName(): String = "index" 26 | 27 | override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG 28 | 29 | override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED 30 | override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED 31 | } 32 | 33 | override val reactHost: ReactHost 34 | get() = getDefaultReactHost(applicationContext, reactNativeHost) 35 | 36 | override fun onCreate() { 37 | super.onCreate() 38 | SoLoader.init(this, OpenSourceMergedSoMapping) 39 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { 40 | // If you opted-in for the New Architecture, we load the native entry point for this app. 41 | load() 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/rn_edit_text_material.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 21 | 22 | 23 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbital-systems/react-native-esp-idf-provisioning/ce4f914ac99780b944a01e45538e521590586e01/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbital-systems/react-native-esp-idf-provisioning/ce4f914ac99780b944a01e45538e521590586e01/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbital-systems/react-native-esp-idf-provisioning/ce4f914ac99780b944a01e45538e521590586e01/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbital-systems/react-native-esp-idf-provisioning/ce4f914ac99780b944a01e45538e521590586e01/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbital-systems/react-native-esp-idf-provisioning/ce4f914ac99780b944a01e45538e521590586e01/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbital-systems/react-native-esp-idf-provisioning/ce4f914ac99780b944a01e45538e521590586e01/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbital-systems/react-native-esp-idf-provisioning/ce4f914ac99780b944a01e45538e521590586e01/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbital-systems/react-native-esp-idf-provisioning/ce4f914ac99780b944a01e45538e521590586e01/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbital-systems/react-native-esp-idf-provisioning/ce4f914ac99780b944a01e45538e521590586e01/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbital-systems/react-native-esp-idf-provisioning/ce4f914ac99780b944a01e45538e521590586e01/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | EspIdfProvisioningExample 3 | 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "35.0.0" 6 | minSdkVersion = 26 7 | compileSdkVersion = 35 8 | targetSdkVersion = 35 9 | ndkVersion = "27.1.12297006" 10 | kotlinVersion = "2.0.21" 11 | } 12 | repositories { 13 | google() 14 | mavenCentral() 15 | } 16 | dependencies { 17 | classpath("com.android.tools.build:gradle:8.7.2") 18 | classpath("com.facebook.react:react-native-gradle-plugin") 19 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin") 20 | } 21 | } 22 | 23 | apply plugin: "com.facebook.react.rootproject" 24 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m 13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | # AndroidX package structure to make it clearer which packages are bundled with the 21 | # Android operating system, and which are packaged with your app's APK 22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 23 | android.useAndroidX=true 24 | 25 | # Use this property to specify which architecture you want to build. 26 | # You can also override it from the CLI using 27 | # ./gradlew -PreactNativeArchitectures=x86_64 28 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 29 | 30 | # Use this property to enable support to the new architecture. 31 | # This will allow you to use TurboModules and the Fabric render in 32 | # your application. You should enable this flag either if you want 33 | # to write custom TurboModules/Fabric components OR use libraries that 34 | # are providing them. 35 | newArchEnabled=true 36 | 37 | # Use this property to enable or disable the Hermes JS engine. 38 | # If set to false, you will be using JSC instead. 39 | hermesEnabled=true 40 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/orbital-systems/react-native-esp-idf-provisioning/ce4f914ac99780b944a01e45538e521590586e01/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | if ! command -v java >/dev/null 2>&1 137 | then 138 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 139 | 140 | Please set the JAVA_HOME variable in your environment to match the 141 | location of your Java installation." 142 | fi 143 | fi 144 | 145 | # Increase the maximum file descriptors if we can. 146 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 147 | case $MAX_FD in #( 148 | max*) 149 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 150 | # shellcheck disable=SC2039,SC3045 151 | MAX_FD=$( ulimit -H -n ) || 152 | warn "Could not query maximum file descriptor limit" 153 | esac 154 | case $MAX_FD in #( 155 | '' | soft) :;; #( 156 | *) 157 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 158 | # shellcheck disable=SC2039,SC3045 159 | ulimit -n "$MAX_FD" || 160 | warn "Could not set maximum file descriptor limit to $MAX_FD" 161 | esac 162 | fi 163 | 164 | # Collect all arguments for the java command, stacking in reverse order: 165 | # * args from the command line 166 | # * the main class name 167 | # * -classpath 168 | # * -D...appname settings 169 | # * --module-path (only if needed) 170 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 171 | 172 | # For Cygwin or MSYS, switch paths to Windows format before running java 173 | if "$cygwin" || "$msys" ; then 174 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 175 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 176 | 177 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 178 | 179 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 180 | for arg do 181 | if 182 | case $arg in #( 183 | -*) false ;; # don't mess with options #( 184 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 185 | [ -e "$t" ] ;; #( 186 | *) false ;; 187 | esac 188 | then 189 | arg=$( cygpath --path --ignore --mixed "$arg" ) 190 | fi 191 | # Roll the args list around exactly as many times as the number of 192 | # args, so each arg winds up back in the position where it started, but 193 | # possibly modified. 194 | # 195 | # NB: a `for` loop captures its iteration list before it begins, so 196 | # changing the positional parameters here affects neither the number of 197 | # iterations, nor the values presented in `arg`. 198 | shift # remove old arg 199 | set -- "$@" "$arg" # push replacement arg 200 | done 201 | fi 202 | 203 | 204 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 205 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 206 | 207 | # Collect all arguments for the java command: 208 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 209 | # and any embedded shellness will be escaped. 210 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 211 | # treated as '${Hostname}' itself on the command line. 212 | 213 | set -- \ 214 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 215 | -classpath "$CLASSPATH" \ 216 | org.gradle.wrapper.GradleWrapperMain \ 217 | "$@" 218 | 219 | # Stop when "xargs" is not available. 220 | if ! command -v xargs >/dev/null 2>&1 221 | then 222 | die "xargs is not available" 223 | fi 224 | 225 | # Use "xargs" to parse quoted args. 226 | # 227 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 228 | # 229 | # In Bash we could simply go: 230 | # 231 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 232 | # set -- "${ARGS[@]}" "$@" 233 | # 234 | # but POSIX shell has neither arrays nor command substitution, so instead we 235 | # post-process each arg (as a line of input to sed) to backslash-escape any 236 | # character that might be a shell metacharacter, then use eval to reverse 237 | # that process (while maintaining the separation between arguments), and wrap 238 | # the whole thing up as a single "set" statement. 239 | # 240 | # This will of course break if any of these variables contains a newline or 241 | # an unmatched quote. 242 | # 243 | 244 | eval "set -- $( 245 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 246 | xargs -n1 | 247 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 248 | tr '\n' ' ' 249 | )" '"$@"' 250 | 251 | exec "$JAVACMD" "$@" 252 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") } 2 | plugins { id("com.facebook.react.settings") } 3 | extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() } 4 | rootProject.name = 'EspIdfProvisioningExample' 5 | include ':app' 6 | includeBuild('../node_modules/@react-native/gradle-plugin') 7 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "EspIdfProvisioningExample", 3 | "displayName": "EspIdfProvisioningExample" 4 | } 5 | -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const pak = require('../package.json'); 3 | 4 | module.exports = { 5 | presets: ['module:@react-native/babel-preset'], 6 | plugins: [ 7 | [ 8 | 'module-resolver', 9 | { 10 | extensions: ['.tsx', '.ts', '.js', '.json'], 11 | alias: { 12 | [pak.name]: path.join(__dirname, '..', pak.source), 13 | }, 14 | }, 15 | ], 16 | ], 17 | overrides: [ 18 | { 19 | plugins: [ 20 | [ 21 | '@babel/plugin-transform-private-methods', 22 | { 23 | loose: true, 24 | }, 25 | ], 26 | ], 27 | }, 28 | ], 29 | }; 30 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | import { AppRegistry } from 'react-native'; 2 | import App from './src/App'; 3 | import { name as appName } from './app.json'; 4 | 5 | AppRegistry.registerComponent(appName, () => App); 6 | -------------------------------------------------------------------------------- /example/ios/.xcode.env: -------------------------------------------------------------------------------- 1 | # This `.xcode.env` file is versioned and is used to source the environment 2 | # used when running script phases inside Xcode. 3 | # To customize your local environment, you can create an `.xcode.env.local` 4 | # file that is not versioned. 5 | 6 | # NODE_BINARY variable contains the PATH to the node executable. 7 | # 8 | # Customize the NODE_BINARY variable here. 9 | # For example, to use nvm with brew, add the following line 10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use 11 | export NODE_BINARY=$(command -v node) 12 | -------------------------------------------------------------------------------- /example/ios/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import React 3 | import React_RCTAppDelegate 4 | import ReactAppDependencyProvider 5 | 6 | @main 7 | class AppDelegate: UIResponder, UIApplicationDelegate { 8 | var window: UIWindow? 9 | 10 | var reactNativeDelegate: ReactNativeDelegate? 11 | var reactNativeFactory: RCTReactNativeFactory? 12 | 13 | func application( 14 | _ application: UIApplication, 15 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil 16 | ) -> Bool { 17 | let delegate = ReactNativeDelegate() 18 | let factory = RCTReactNativeFactory(delegate: delegate) 19 | delegate.dependencyProvider = RCTAppDependencyProvider() 20 | 21 | reactNativeDelegate = delegate 22 | reactNativeFactory = factory 23 | 24 | window = UIWindow(frame: UIScreen.main.bounds) 25 | 26 | factory.startReactNative( 27 | withModuleName: "EspIdfProvisioningExample", 28 | in: window, 29 | launchOptions: launchOptions 30 | ) 31 | 32 | return true 33 | } 34 | } 35 | 36 | class ReactNativeDelegate: RCTDefaultReactNativeFactoryDelegate { 37 | override func sourceURL(for bridge: RCTBridge) -> URL? { 38 | self.bundleURL() 39 | } 40 | 41 | override func bundleURL() -> URL? { 42 | #if DEBUG 43 | RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") 44 | #else 45 | Bundle.main.url(forResource: "main", withExtension: "jsbundle") 46 | #endif 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /example/ios/EspIdfProvisioningExample-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // EspIdfProvisioning-Bridging-Header.h 2 | -------------------------------------------------------------------------------- /example/ios/EspIdfProvisioningExample.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EspIdfProvisioningExample.swift 3 | // EspIdfProvisioningExample 4 | // 5 | // Created by Mateo Gianolio on 2023-09-18. 6 | // 7 | 8 | import Foundation 9 | -------------------------------------------------------------------------------- /example/ios/EspIdfProvisioningExample.xcodeproj/xcshareddata/xcschemes/EspIdfProvisioningExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /example/ios/EspIdfProvisioningExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/EspIdfProvisioningExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/EspIdfProvisioningExample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "scale" : "1x", 46 | "size" : "1024x1024" 47 | } 48 | ], 49 | "info" : { 50 | "author" : "xcode", 51 | "version" : 1 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /example/ios/EspIdfProvisioningExample/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/ios/EspIdfProvisioningExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | EspIdfProvisioningExample 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(CURRENT_PROJECT_VERSION) 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | 30 | NSAllowsArbitraryLoads 31 | 32 | NSAllowsLocalNetworking 33 | 34 | 35 | NSBluetoothAlwaysUsageDescription 36 | We need Bluetooth to discover, connect and share information with other nearby devices. 37 | NSBluetoothPeripheralUsageDescription 38 | We need Bluetooth to discover, connect and share information with other nearby devices. 39 | NSLocationWhenInUseUsageDescription 40 | 41 | UILaunchStoryboardName 42 | LaunchScreen 43 | UIRequiredDeviceCapabilities 44 | 45 | arm64 46 | 47 | UISupportedInterfaceOrientations 48 | 49 | UIInterfaceOrientationPortrait 50 | UIInterfaceOrientationLandscapeLeft 51 | UIInterfaceOrientationLandscapeRight 52 | 53 | UIViewControllerBasedStatusBarAppearance 54 | 55 | UIAppFonts 56 | 57 | AntDesign.ttf 58 | Entypo.ttf 59 | EvilIcons.ttf 60 | Feather.ttf 61 | FontAwesome.ttf 62 | FontAwesome5_Brands.ttf 63 | FontAwesome5_Regular.ttf 64 | FontAwesome5_Solid.ttf 65 | FontAwesome6_Brands.ttf 66 | FontAwesome6_Regular.ttf 67 | FontAwesome6_Solid.ttf 68 | Foundation.ttf 69 | Ionicons.ttf 70 | MaterialIcons.ttf 71 | MaterialCommunityIcons.ttf 72 | SimpleLineIcons.ttf 73 | Octicons.ttf 74 | Zocial.ttf 75 | Fontisto.ttf 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /example/ios/EspIdfProvisioningExample/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 24 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /example/ios/EspIdfProvisioningExample/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyAccessedAPITypes 6 | 7 | 8 | NSPrivacyAccessedAPIType 9 | NSPrivacyAccessedAPICategoryFileTimestamp 10 | NSPrivacyAccessedAPITypeReasons 11 | 12 | C617.1 13 | 14 | 15 | 16 | NSPrivacyAccessedAPIType 17 | NSPrivacyAccessedAPICategoryUserDefaults 18 | NSPrivacyAccessedAPITypeReasons 19 | 20 | CA92.1 21 | 22 | 23 | 24 | NSPrivacyAccessedAPIType 25 | NSPrivacyAccessedAPICategorySystemBootTime 26 | NSPrivacyAccessedAPITypeReasons 27 | 28 | 35F9.1 29 | 30 | 31 | 32 | NSPrivacyCollectedDataTypes 33 | 34 | NSPrivacyTracking 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/EspIdfProvisioningExampleTests/EspIdfProvisioningExampleTests.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import 5 | #import 6 | 7 | #define TIMEOUT_SECONDS 600 8 | #define TEXT_TO_LOOK_FOR @"Welcome to React" 9 | 10 | @interface EspIdfProvisioningExampleTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation EspIdfProvisioningExampleTests 15 | 16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test 17 | { 18 | if (test(view)) { 19 | return YES; 20 | } 21 | for (UIView *subview in [view subviews]) { 22 | if ([self findSubviewInView:subview matching:test]) { 23 | return YES; 24 | } 25 | } 26 | return NO; 27 | } 28 | 29 | - (void)testRendersWelcomeScreen 30 | { 31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 33 | BOOL foundElement = NO; 34 | 35 | __block NSString *redboxError = nil; 36 | #ifdef DEBUG 37 | RCTSetLogFunction( 38 | ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 39 | if (level >= RCTLogLevelError) { 40 | redboxError = message; 41 | } 42 | }); 43 | #endif 44 | 45 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 46 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 47 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 48 | 49 | foundElement = [self findSubviewInView:vc.view 50 | matching:^BOOL(UIView *view) { 51 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 52 | return YES; 53 | } 54 | return NO; 55 | }]; 56 | } 57 | 58 | #ifdef DEBUG 59 | RCTSetLogFunction(RCTDefaultLogFunction); 60 | #endif 61 | 62 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 63 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /example/ios/EspIdfProvisioningExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Resolve react_native_pods.rb with node to allow for hoisting 2 | require Pod::Executable.execute_command('node', ['-p', 3 | 'require.resolve( 4 | "react-native/scripts/react_native_pods.rb", 5 | {paths: [process.argv[1]]}, 6 | )', __dir__]).strip 7 | 8 | platform :ios, min_ios_version_supported 9 | prepare_react_native_project! 10 | 11 | linkage = ENV['USE_FRAMEWORKS'] 12 | if linkage != nil 13 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green 14 | use_frameworks! :linkage => linkage.to_sym 15 | end 16 | 17 | target 'EspIdfProvisioningExample' do 18 | config = use_native_modules! 19 | 20 | use_react_native!( 21 | :path => config[:reactNativePath], 22 | # An absolute path to your application root. 23 | :app_path => "#{Pod::Config.instance.installation_root}/.." 24 | ) 25 | 26 | post_install do |installer| 27 | # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 28 | react_native_post_install( 29 | installer, 30 | config[:reactNativePath], 31 | :mac_catalyst_enabled => false, 32 | # :ccache_enabled => true 33 | ) 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /example/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'react-native', 3 | }; 4 | -------------------------------------------------------------------------------- /example/metro.config.js: -------------------------------------------------------------------------------- 1 | const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config'); 2 | const path = require('path'); 3 | const escape = require('escape-string-regexp'); 4 | const exclusionList = require('metro-config/src/defaults/exclusionList'); 5 | const pak = require('../package.json'); 6 | 7 | const root = path.resolve(__dirname, '..'); 8 | const modules = Object.keys({ ...pak.peerDependencies }); 9 | 10 | /** 11 | * Metro configuration 12 | * https://facebook.github.io/metro/docs/configuration 13 | * 14 | * @type {import('@react-native/metro-config').MetroConfig} 15 | */ 16 | const config = { 17 | watchFolders: [root], 18 | 19 | // We need to make sure that only one version is loaded for peerDependencies 20 | // So we block them at the root, and alias them to the versions in example's node_modules 21 | resolver: { 22 | blacklistRE: exclusionList( 23 | modules.map( 24 | (m) => 25 | new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`) 26 | ) 27 | ), 28 | 29 | extraNodeModules: modules.reduce((acc, name) => { 30 | acc[name] = path.join(__dirname, 'node_modules', name); 31 | return acc; 32 | }, {}), 33 | }, 34 | 35 | transformer: { 36 | getTransformOptions: async () => ({ 37 | transform: { 38 | experimentalImportSupport: false, 39 | inlineRequires: true, 40 | }, 41 | }), 42 | }, 43 | }; 44 | 45 | module.exports = mergeConfig(getDefaultConfig(__dirname), config); 46 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "EspIdfProvisioningExample", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "android": "react-native run-android", 7 | "ios": "react-native run-ios", 8 | "start": "react-native start", 9 | "pods": "pod-install --quiet" 10 | }, 11 | "dependencies": { 12 | "@react-navigation/native": "^7.1.9", 13 | "@react-navigation/native-stack": "^7.3.13", 14 | "@rneui/base": "^4.0.0-rc.8", 15 | "@rneui/themed": "^4.0.0-rc.8", 16 | "@types/react": "^19.1.5", 17 | "deepmerge": "4.3.1", 18 | "react": "19.0.0", 19 | "react-native": "0.79.2", 20 | "react-native-default-preference": "^1.4.4", 21 | "react-native-safe-area-context": "^5.4.1", 22 | "react-native-screens": "^4.11.0-beta.2", 23 | "react-native-vector-icons": "^10.2.0" 24 | }, 25 | "devDependencies": { 26 | "@babel/core": "^7.25.2", 27 | "@babel/preset-env": "^7.25.3", 28 | "@babel/runtime": "^7.25.0", 29 | "@react-native-community/cli": "18.0.0", 30 | "@react-native-community/cli-platform-android": "18.0.0", 31 | "@react-native-community/cli-platform-ios": "18.0.0", 32 | "babel-plugin-module-resolver": "^5.0.2" 33 | }, 34 | "resolutions": { 35 | "deepmerge": "4.3.1" 36 | }, 37 | "engines": { 38 | "node": ">=18" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /example/react-native.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const pak = require('../package.json'); 3 | 4 | module.exports = { 5 | dependencies: { 6 | [pak.name]: { 7 | root: path.join(__dirname, '..'), 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /example/src/App.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { NavigationContainer } from '@react-navigation/native'; 3 | import { createNativeStackNavigator } from '@react-navigation/native-stack'; 4 | import { SafeAreaProvider } from 'react-native-safe-area-context'; 5 | import { ThemeProvider } from '@rneui/themed'; 6 | 7 | import { theme } from './theme'; 8 | import { HomeScreen } from './HomeScreen'; 9 | import { SettingsScreen } from './SettingsScreen'; 10 | import { ProvisionScreen } from './ProvisionScreen'; 11 | import { DeviceScreen } from './DeviceScreen'; 12 | import { WifiListScreen } from './WifiListScreen'; 13 | import { WifiPasswordScreen } from './WifiPasswordScreen'; 14 | import { SendDataScreen } from './SendDataScreen'; 15 | import type { StackParamList } from './types'; 16 | 17 | const Stack = createNativeStackNavigator(); 18 | 19 | export default function App() { 20 | return ( 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /example/src/DeviceScreen.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { View, ScrollView, ActivityIndicator } from 'react-native'; 3 | import { useFocusEffect } from '@react-navigation/native'; 4 | import { type NativeStackScreenProps } from '@react-navigation/native-stack'; 5 | import { useSafeAreaInsets } from 'react-native-safe-area-context'; 6 | import { Text, Button } from '@rneui/themed'; 7 | import { ESPDevice } from '@orbital-systems/react-native-esp-idf-provisioning'; 8 | import type { StackParamList } from './types'; 9 | import { styles } from './theme'; 10 | 11 | export function DeviceScreen( 12 | props: NativeStackScreenProps 13 | ) { 14 | const insets = useSafeAreaInsets(); 15 | const [device, setDevice] = React.useState(); 16 | const [versionInfo, setVersionInfo] = React.useState>(); 17 | const [deviceCapabilities, setDeviceCapabilities] = 18 | React.useState(); 19 | 20 | React.useEffect(() => { 21 | async function getVersionInfo() { 22 | setVersionInfo(await device?.getVersionInfo()); 23 | } 24 | 25 | async function getDeviceCapabilities() { 26 | setDeviceCapabilities(await device?.getDeviceCapabilities()); 27 | } 28 | 29 | if (device) { 30 | getVersionInfo(); 31 | getDeviceCapabilities(); 32 | } 33 | }, [device]); 34 | 35 | useFocusEffect( 36 | React.useCallback(() => { 37 | (async () => { 38 | if (device) { 39 | return () => device?.disconnect(); 40 | } 41 | 42 | const espDevice = new ESPDevice({ 43 | name: props.route.params.name, 44 | transport: props.route.params.transport, 45 | security: props.route.params.security, 46 | }); 47 | 48 | try { 49 | console.info('Connecting to device...'); 50 | await espDevice.connect( 51 | props.route.params.proofOfPossession, 52 | props.route.params.softAPPassword, 53 | props.route.params.username 54 | ); 55 | 56 | setDevice(espDevice); 57 | } catch (error) { 58 | console.error(error); 59 | props.navigation.goBack(); 60 | } 61 | 62 | return () => { 63 | console.info('Disconnecting...'); 64 | espDevice.disconnect(); 65 | }; 66 | })(); 67 | }, [ 68 | device, 69 | props.navigation, 70 | props.route.params.name, 71 | props.route.params.proofOfPossession, 72 | props.route.params.security, 73 | props.route.params.softAPPassword, 74 | props.route.params.transport, 75 | props.route.params.username, 76 | ]) 77 | ); 78 | 79 | return ( 80 | 81 | {device ? ( 82 | 83 | 84 | Device name 85 | 86 | {device?.name} 87 | 88 | Capabilities 89 | 90 | 91 | {JSON.stringify(deviceCapabilities ?? [])} 92 | 93 | 94 | Version info 95 | 96 | {JSON.stringify(versionInfo ?? {})} 97 | 98 |