react-native-web-serial-api 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +147 -0
- package/android/build.gradle +62 -0
- package/android/gradle.properties +6 -0
- package/android/src/main/AndroidManifest.xml +21 -0
- package/android/src/main/java/dev/webserialapi/NativeUsbSerialModule.java +704 -0
- package/android/src/main/java/dev/webserialapi/NativeUsbSerialPackage.java +46 -0
- package/android/src/main/java/dev/webserialapi/PortPickerActivity.java +235 -0
- package/android/src/main/java/dev/webserialapi/UsbDetachReceiver.java +37 -0
- package/android/src/main/res/xml/device_filter.xml +13 -0
- package/babel.config.js +3 -0
- package/biome.json +35 -0
- package/example/.watchmanconfig +1 -0
- package/example/App.tsx +71 -0
- package/example/__tests__/App.test.tsx +16 -0
- package/example/android/app/build.gradle +120 -0
- package/example/android/app/debug.keystore +0 -0
- package/example/android/app/proguard-rules.pro +10 -0
- package/example/android/app/src/debug/AndroidManifest.xml +9 -0
- package/example/android/app/src/main/AndroidManifest.xml +38 -0
- package/example/android/app/src/main/java/dev/uzlopak/MainActivity.kt +22 -0
- package/example/android/app/src/main/java/dev/uzlopak/MainApplication.kt +41 -0
- package/example/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
- package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/example/android/app/src/main/res/values/strings.xml +3 -0
- package/example/android/app/src/main/res/values/styles.xml +9 -0
- package/example/android/build.gradle +22 -0
- package/example/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/example/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/example/android/gradle.properties +47 -0
- package/example/android/gradlew +252 -0
- package/example/android/gradlew.bat +94 -0
- package/example/android/settings.gradle +6 -0
- package/example/app.json +4 -0
- package/example/babel.config.js +21 -0
- package/example/biome.json +47 -0
- package/example/deploy.sh +11 -0
- package/example/index.html +26 -0
- package/example/index.js +9 -0
- package/example/index.web.js +8 -0
- package/example/jest.config.js +12 -0
- package/example/metro.config.js +58 -0
- package/example/package-lock.json +14510 -0
- package/example/package.json +48 -0
- package/example/react-native.config.js +17 -0
- package/example/src/components/AppBar.tsx +73 -0
- package/example/src/components/Menu.tsx +90 -0
- package/example/src/components/SingleChoiceDialog.tsx +120 -0
- package/example/src/screens/ConnectScreen.tsx +195 -0
- package/example/src/screens/DevicesScreen.tsx +141 -0
- package/example/src/screens/TerminalScreen.tsx +564 -0
- package/example/src/settings.ts +43 -0
- package/example/src/theme.ts +19 -0
- package/example/src/util/TextUtil.ts +129 -0
- package/example/tsconfig.json +10 -0
- package/example/vite.config.mjs +55 -0
- package/lib/commonjs/NativeUsbSerial.js +11 -0
- package/lib/commonjs/NativeUsbSerial.js.map +1 -0
- package/lib/commonjs/NativeUsbSerial.web.js +12 -0
- package/lib/commonjs/NativeUsbSerial.web.js.map +1 -0
- package/lib/commonjs/UsbSerial.js +149 -0
- package/lib/commonjs/UsbSerial.js.map +1 -0
- package/lib/commonjs/WebSerial.js +852 -0
- package/lib/commonjs/WebSerial.js.map +1 -0
- package/lib/commonjs/index.js +44 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/serial.android.js +13 -0
- package/lib/commonjs/serial.android.js.map +1 -0
- package/lib/commonjs/serial.js +13 -0
- package/lib/commonjs/serial.js.map +1 -0
- package/lib/commonjs/serial.web.js +11 -0
- package/lib/commonjs/serial.web.js.map +1 -0
- package/lib/typescript/src/NativeUsbSerial.d.ts +51 -0
- package/lib/typescript/src/NativeUsbSerial.d.ts.map +1 -0
- package/lib/typescript/src/NativeUsbSerial.web.d.ts +3 -0
- package/lib/typescript/src/NativeUsbSerial.web.d.ts.map +1 -0
- package/lib/typescript/src/UsbSerial.d.ts +97 -0
- package/lib/typescript/src/UsbSerial.d.ts.map +1 -0
- package/lib/typescript/src/WebSerial.d.ts +236 -0
- package/lib/typescript/src/WebSerial.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +7 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/serial.android.d.ts +2 -0
- package/lib/typescript/src/serial.android.d.ts.map +1 -0
- package/lib/typescript/src/serial.d.ts +2 -0
- package/lib/typescript/src/serial.d.ts.map +1 -0
- package/lib/typescript/src/serial.web.d.ts +4 -0
- package/lib/typescript/src/serial.web.d.ts.map +1 -0
- package/package.json +78 -0
- package/react-native.config.js +9 -0
- package/scripts/deploy-release.sh +127 -0
- package/src/NativeUsbSerial.ts +124 -0
- package/src/NativeUsbSerial.web.ts +5 -0
- package/src/UsbSerial.ts +305 -0
- package/src/WebSerial.ts +1084 -0
- package/src/index.ts +23 -0
- package/src/lib/dom-exception.ts +161 -0
- package/src/lib/event-target.ts +170 -0
- package/src/lib/promise.ts +19 -0
- package/src/serial.android.ts +1 -0
- package/src/serial.ts +1 -0
- package/src/serial.web.ts +6 -0
- package/tsconfig.build.json +7 -0
- package/tsconfig.json +20 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aras Abbasi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# react-native-web-serial-api
|
|
2
|
+
|
|
3
|
+
> The [W3C Web Serial API](https://wicg.github.io/serial/) (`navigator.serial`) for **React Native on Android**, backed by a USB-serial TurboModule built on top of [`mik3y/usb-serial-for-android`](https://github.com/mik3y/usb-serial-for-android).
|
|
4
|
+
|
|
5
|
+
Talk to USB serial devices (FTDI, CP210x, CH340/CH341, PL2303, CDC-ACM …) from React Native using the **exact same API you already know from the browser** — `serial.requestPort()`, `port.open()`, `port.readable`, `port.writable`, and so on.
|
|
6
|
+
|
|
7
|
+
- Spec-compliant `Serial` / `SerialPort` implementation (`getPorts`, `requestPort`, `open`, `close`, `readable`, `writable`, `setSignals`, `getSignals`, `forget`, `connect`/`disconnect` events)
|
|
8
|
+
- New Architecture **TurboModule**
|
|
9
|
+
- Native port-picker dialog + USB permission handling
|
|
10
|
+
- Backed by Web Streams (`ReadableStream` / `WritableStream`)
|
|
11
|
+
- Drop-in for code written against the browser Web Serial API (on web it transparently uses the native `navigator.serial`)
|
|
12
|
+
|
|
13
|
+
> **Platform support:** Android only. On web (`react-native-web`) the package delegates to the browser's native Web Serial API. There is no iOS implementation (iOS does not allow generic USB-serial access), so iOS autolinking is disabled.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npm install react-native-web-serial-api
|
|
19
|
+
# or
|
|
20
|
+
yarn add react-native-web-serial-api
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This is a New Architecture library, so make sure your app has the New Architecture enabled (default in recent React Native). No manual linking is required — the module is autolinked.
|
|
24
|
+
|
|
25
|
+
### Android setup
|
|
26
|
+
|
|
27
|
+
The library ships its own `AndroidManifest.xml` that declares the port-picker activity, the detach receiver, and the `android.hardware.usb.host` feature, so usually **no extra configuration is needed**.
|
|
28
|
+
|
|
29
|
+
If you want your app to be **launched automatically when a matching device is plugged in**, add an intent filter to your launcher activity in `android/app/src/main/AndroidManifest.xml`:
|
|
30
|
+
|
|
31
|
+
```xml
|
|
32
|
+
<activity android:name=".MainActivity" ...>
|
|
33
|
+
<intent-filter>
|
|
34
|
+
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
|
|
35
|
+
</intent-filter>
|
|
36
|
+
<!-- The device_filter resource is provided by the library -->
|
|
37
|
+
<meta-data
|
|
38
|
+
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
|
|
39
|
+
android:resource="@xml/device_filter" />
|
|
40
|
+
</activity>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The bundled `@xml/device_filter` matches the common USB-serial chips (CDC-ACM, FTDI `0x0403`, CP210x `0x10C4`, CH34x `0x1A86`, PL2303 `0x067B`). Provide your own `res/xml/device_filter.xml` to override it.
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import {serial} from 'react-native-web-serial-api';
|
|
49
|
+
|
|
50
|
+
async function run() {
|
|
51
|
+
// Shows a native dialog to pick a port, then requests USB permission.
|
|
52
|
+
const port = await serial.requestPort({
|
|
53
|
+
filters: [{usbVendorId: 0x0403}], // optional — e.g. only FTDI devices
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
await port.open({baudRate: 115200, dataBits: 8, stopBits: 1, parity: 'none'});
|
|
57
|
+
|
|
58
|
+
// Write
|
|
59
|
+
const writer = port.writable.getWriter();
|
|
60
|
+
await writer.write(new TextEncoder().encode('Hello\n'));
|
|
61
|
+
writer.releaseLock();
|
|
62
|
+
|
|
63
|
+
// Read
|
|
64
|
+
const reader = port.readable.getReader();
|
|
65
|
+
const {value} = await reader.read(); // value is a Uint8Array
|
|
66
|
+
console.log(value);
|
|
67
|
+
reader.releaseLock();
|
|
68
|
+
|
|
69
|
+
await port.close();
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Control & status signals
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
await port.setSignals({dataTerminalReady: true, requestToSend: false});
|
|
77
|
+
const {clearToSend, dataCarrierDetect, ringIndicator, dataSetReady} =
|
|
78
|
+
await port.getSignals();
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Connect / disconnect events
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
serial.addEventListener('connect', () => console.log('device attached'));
|
|
85
|
+
serial.addEventListener('disconnect', () => console.log('device detached'));
|
|
86
|
+
|
|
87
|
+
port.addEventListener('disconnect', () => console.log('this port went away'));
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Listing already-permitted ports
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
const ports = await serial.getPorts();
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## API
|
|
97
|
+
|
|
98
|
+
The package exposes:
|
|
99
|
+
|
|
100
|
+
| Export | Description |
|
|
101
|
+
| --- | --- |
|
|
102
|
+
| `serial` | A ready-to-use `Serial` instance (`navigator.serial` equivalent). |
|
|
103
|
+
| `Serial`, `SerialPort` | The Web Serial API classes. |
|
|
104
|
+
| `UsbSerial` | Lower-level access to the raw USB-serial TurboModule (Android only). |
|
|
105
|
+
| `Event`, `EventTarget` | The event primitives used by the polyfill. |
|
|
106
|
+
| Types | `SerialOptions`, `SerialOutputSignals`, `SerialInputSignals`, `SerialPortInfo`, `SerialPortFilter`, `SerialPortRequestOptions`. |
|
|
107
|
+
|
|
108
|
+
## Example app
|
|
109
|
+
|
|
110
|
+
The [`example/`](./example) app is a React Native port of [SimpleUsbTerminal](https://github.com/kai-morich/SimpleUsbTerminal) built entirely on this package's Web Serial API — a **Devices** list (with baud-rate selection) and a **Terminal** (colored receive/send log, HEX mode, newline selection, clear, control-lines row with RTS/DTR toggles, flow control, and Send BREAK).
|
|
111
|
+
|
|
112
|
+
```sh
|
|
113
|
+
# install the library's build tooling
|
|
114
|
+
npm install
|
|
115
|
+
|
|
116
|
+
# install and run the example on Android
|
|
117
|
+
cd example
|
|
118
|
+
npm install
|
|
119
|
+
npm run android
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Because it uses the Web Serial API, a few SimpleUsbTerminal details map differently:
|
|
123
|
+
|
|
124
|
+
- **Driver/chip name** isn't exposed by the Web Serial API (`getInfo()` returns only VID/PID), so rows show `Vendor/Product` plus a best-effort chip label from known vendor IDs.
|
|
125
|
+
- **Flow control** is limited to *None* / *Hardware (RTS-CTS)* — XON/XOFF and DTR/DSR aren't in the Web Serial spec. Changing it reconnects the port.
|
|
126
|
+
- The Android background **foreground-service notification** is omitted (it's service plumbing unrelated to serial I/O).
|
|
127
|
+
|
|
128
|
+
### Run the example in the browser
|
|
129
|
+
|
|
130
|
+
The example also runs as a web app via [`react-native-web`](https://necolas.github.io/react-native-web/) + [Vite](https://vite.dev/). On web, the package delegates to the browser's native `navigator.serial`, so the exact same `App.tsx` talks to real serial hardware over WebUSB-style permissions.
|
|
131
|
+
|
|
132
|
+
```sh
|
|
133
|
+
cd example
|
|
134
|
+
npm install
|
|
135
|
+
npm run web # dev server at http://localhost:5173
|
|
136
|
+
# npm run web:build # production build into example/dist
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
> Web Serial only works in **Chromium-based browsers** (Chrome / Edge / Opera) over a **secure context** (`http://localhost` counts), and `requestPort()` must be called from a user gesture — the demo's "Request port" button handles that.
|
|
140
|
+
|
|
141
|
+
## How it works
|
|
142
|
+
|
|
143
|
+
The JavaScript layer (`src/`) implements the Web Serial API on top of a thin TurboModule (`NativeUsbSerial`) whose native Android implementation (`android/src/main/java/dev/webserialapi/`) wraps `usb-serial-for-android`. Reads/writes are bridged to `ReadableStream`/`WritableStream` via [`web-streams-polyfill`](https://github.com/MattiasBuelens/web-streams-polyfill).
|
|
144
|
+
|
|
145
|
+
## License
|
|
146
|
+
|
|
147
|
+
MIT © Aras Abbasi
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
// Allow the library to be built either standalone (using the *_ defaults from
|
|
3
|
+
// gradle.properties) or as part of a host app (using the host's rootProject.ext).
|
|
4
|
+
ext.getExtOrDefault = { name ->
|
|
5
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["WebSerialApi_" + name]
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// When autolinked into a host app, the Android Gradle Plugin is already on the
|
|
9
|
+
// root project's buildscript classpath, so we must not redeclare it here. Only
|
|
10
|
+
// pull it in (with an explicit version) when building this library standalone.
|
|
11
|
+
def agpVersion = rootProject.ext.has("agpVersion") ? rootProject.ext.get("agpVersion") : "8.7.2"
|
|
12
|
+
|
|
13
|
+
repositories {
|
|
14
|
+
google()
|
|
15
|
+
mavenCentral()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
dependencies {
|
|
19
|
+
if (project == rootProject) {
|
|
20
|
+
classpath "com.android.tools.build:gradle:$agpVersion"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
apply plugin: "com.android.library"
|
|
26
|
+
// Drives React Native Codegen for the TurboModule spec declared in package.json -> codegenConfig
|
|
27
|
+
apply plugin: "com.facebook.react"
|
|
28
|
+
|
|
29
|
+
def getExtOrIntegerDefault(name) {
|
|
30
|
+
return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["WebSerialApi_" + name]).toInteger()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
android {
|
|
34
|
+
namespace "dev.webserialapi"
|
|
35
|
+
|
|
36
|
+
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
|
|
37
|
+
|
|
38
|
+
defaultConfig {
|
|
39
|
+
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
|
40
|
+
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
compileOptions {
|
|
44
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
45
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
repositories {
|
|
50
|
+
mavenCentral()
|
|
51
|
+
google()
|
|
52
|
+
// usb-serial-for-android is published on JitPack
|
|
53
|
+
maven { url "https://jitpack.io" }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
dependencies {
|
|
57
|
+
// The version of react-native is set by the host app's React Native Gradle Plugin
|
|
58
|
+
implementation "com.facebook.react:react-android"
|
|
59
|
+
implementation "com.github.mik3y:usb-serial-for-android:3.10.0"
|
|
60
|
+
// PortPickerActivity extends AppCompatActivity and uses the AppCompat dialog theme
|
|
61
|
+
implementation "androidx.appcompat:appcompat:1.7.0"
|
|
62
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Fallback values used when the library is built standalone (i.e. when the host
|
|
2
|
+
# app's rootProject.ext does not provide these). Inside a host app the app's
|
|
3
|
+
# values take precedence (see getExtOrDefault in build.gradle).
|
|
4
|
+
WebSerialApi_compileSdkVersion=35
|
|
5
|
+
WebSerialApi_targetSdkVersion=34
|
|
6
|
+
WebSerialApi_minSdkVersion=24
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
2
|
+
|
|
3
|
+
<uses-feature android:name="android.hardware.usb.host" />
|
|
4
|
+
|
|
5
|
+
<application>
|
|
6
|
+
<!-- Native dialog shown by serial.requestPort() to let the user pick a port -->
|
|
7
|
+
<activity
|
|
8
|
+
android:name="dev.webserialapi.PortPickerActivity"
|
|
9
|
+
android:exported="false"
|
|
10
|
+
android:theme="@style/Theme.AppCompat.Dialog" />
|
|
11
|
+
|
|
12
|
+
<!-- Emits a "disconnect" JS event when a USB serial device is unplugged -->
|
|
13
|
+
<receiver
|
|
14
|
+
android:name="dev.webserialapi.UsbDetachReceiver"
|
|
15
|
+
android:exported="true">
|
|
16
|
+
<intent-filter>
|
|
17
|
+
<action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
|
|
18
|
+
</intent-filter>
|
|
19
|
+
</receiver>
|
|
20
|
+
</application>
|
|
21
|
+
</manifest>
|