rnww-plugin-wifi 1.0.0 → 1.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/README.md +221 -693
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
# RNWW Plugin
|
|
1
|
+
# RNWW Plugin WiFi
|
|
2
2
|
|
|
3
|
-
React Native WebView
|
|
3
|
+
React Native WebView WiFi 정보 조회 및 연결 관리 플러그인
|
|
4
4
|
|
|
5
5
|
## 설치
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install rnww-plugin-
|
|
8
|
+
npm install rnww-plugin-wifi
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
## 빠른 시작
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
|
-
import {
|
|
14
|
+
import { registerWifiHandlers } from 'rnww-plugin-wifi';
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
registerWifiHandlers({
|
|
17
17
|
bridge: yourBridgeImplementation,
|
|
18
18
|
platform: { OS: Platform.OS },
|
|
19
19
|
});
|
|
@@ -23,815 +23,343 @@ registerBackgroundHandlers({
|
|
|
23
23
|
|
|
24
24
|
## Bridge Handlers
|
|
25
25
|
|
|
26
|
-
###
|
|
26
|
+
### getCurrentWifiInfo
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
현재 연결된 WiFi 정보를 조회합니다.
|
|
29
29
|
|
|
30
30
|
```typescript
|
|
31
|
-
bridge.call('
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
actions: [
|
|
48
|
-
{
|
|
49
|
-
id: 'pause',
|
|
50
|
-
title: '일시정지',
|
|
51
|
-
onPress: (actionId, taskId) => {
|
|
52
|
-
console.log(`Action ${actionId} clicked for task ${taskId}`);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
]
|
|
56
|
-
}
|
|
57
|
-
});
|
|
31
|
+
const result = await bridge.call('getCurrentWifiInfo');
|
|
32
|
+
// {
|
|
33
|
+
// success: true,
|
|
34
|
+
// data: {
|
|
35
|
+
// ssid: 'MyWiFi',
|
|
36
|
+
// bssid: 'AA:BB:CC:DD:EE:FF',
|
|
37
|
+
// rssi: -45,
|
|
38
|
+
// signalLevel: 80,
|
|
39
|
+
// frequency: 5180,
|
|
40
|
+
// linkSpeed: 433,
|
|
41
|
+
// ipAddress: '192.168.1.100',
|
|
42
|
+
// isConnected: true,
|
|
43
|
+
// networkId: 1,
|
|
44
|
+
// security: 'WPA2'
|
|
45
|
+
// }
|
|
46
|
+
// }
|
|
58
47
|
```
|
|
59
48
|
|
|
60
|
-
###
|
|
49
|
+
### getWifiList
|
|
61
50
|
|
|
62
|
-
|
|
51
|
+
사용 가능한 WiFi 네트워크 목록을 스캔합니다. (Android only)
|
|
63
52
|
|
|
64
53
|
```typescript
|
|
65
|
-
bridge.call('
|
|
66
|
-
|
|
67
|
-
|
|
54
|
+
const result = await bridge.call('getWifiList');
|
|
55
|
+
// {
|
|
56
|
+
// success: true,
|
|
57
|
+
// data: [
|
|
58
|
+
// {
|
|
59
|
+
// ssid: 'MyWiFi',
|
|
60
|
+
// bssid: 'AA:BB:CC:DD:EE:FF',
|
|
61
|
+
// rssi: -45,
|
|
62
|
+
// signalLevel: 80,
|
|
63
|
+
// frequency: 5180,
|
|
64
|
+
// security: 'WPA2',
|
|
65
|
+
// channel: 36
|
|
66
|
+
// },
|
|
67
|
+
// ...
|
|
68
|
+
// ]
|
|
69
|
+
// }
|
|
68
70
|
```
|
|
69
71
|
|
|
70
|
-
###
|
|
72
|
+
### isWifiEnabled
|
|
71
73
|
|
|
72
|
-
|
|
74
|
+
WiFi 활성화 상태를 확인합니다.
|
|
73
75
|
|
|
74
76
|
```typescript
|
|
75
|
-
bridge.call('
|
|
77
|
+
const result = await bridge.call('isWifiEnabled');
|
|
78
|
+
// {
|
|
79
|
+
// success: true,
|
|
80
|
+
// isEnabled: true,
|
|
81
|
+
// wifiState: 'ENABLED'
|
|
82
|
+
// }
|
|
76
83
|
```
|
|
77
84
|
|
|
78
|
-
###
|
|
85
|
+
### connectToWifi
|
|
79
86
|
|
|
80
|
-
|
|
87
|
+
WiFi 네트워크에 연결합니다.
|
|
81
88
|
|
|
82
89
|
```typescript
|
|
83
|
-
bridge.call('
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
progress: { current: 50, max: 100 },
|
|
88
|
-
actions: [
|
|
89
|
-
{
|
|
90
|
-
id: 'cancel',
|
|
91
|
-
title: '취소',
|
|
92
|
-
onPress: (actionId, taskId) => {
|
|
93
|
-
bridge.call('stopTask', { taskId });
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
]
|
|
90
|
+
const result = await bridge.call('connectToWifi', {
|
|
91
|
+
ssid: 'MyWiFi',
|
|
92
|
+
password: 'mypassword',
|
|
93
|
+
isHidden: false
|
|
97
94
|
});
|
|
95
|
+
// {
|
|
96
|
+
// success: true,
|
|
97
|
+
// wifiInfo: { ssid: 'MyWiFi', ... }
|
|
98
|
+
// }
|
|
98
99
|
```
|
|
99
100
|
|
|
100
|
-
###
|
|
101
|
+
### disconnect
|
|
101
102
|
|
|
102
|
-
|
|
103
|
+
현재 WiFi 연결을 해제합니다.
|
|
103
104
|
|
|
104
105
|
```typescript
|
|
105
|
-
const
|
|
106
|
-
|
|
106
|
+
const result = await bridge.call('disconnect');
|
|
107
|
+
// { success: true }
|
|
107
108
|
```
|
|
108
109
|
|
|
109
|
-
###
|
|
110
|
+
### checkWifiPermission
|
|
110
111
|
|
|
111
|
-
|
|
112
|
+
WiFi 관련 권한 상태를 확인합니다.
|
|
112
113
|
|
|
113
114
|
```typescript
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
const result = await bridge.call('checkWifiPermission');
|
|
116
|
+
// {
|
|
117
|
+
// locationGranted: true,
|
|
118
|
+
// canAccessWifiInfo: true,
|
|
119
|
+
// requiredPermissions: [],
|
|
120
|
+
// details: {
|
|
121
|
+
// fineLocation: true,
|
|
122
|
+
// coarseLocation: true
|
|
123
|
+
// }
|
|
124
|
+
// }
|
|
118
125
|
```
|
|
119
126
|
|
|
120
|
-
###
|
|
127
|
+
### requestWifiPermission
|
|
121
128
|
|
|
122
|
-
|
|
129
|
+
WiFi 관련 권한을 요청합니다.
|
|
123
130
|
|
|
124
131
|
```typescript
|
|
125
|
-
bridge.call('
|
|
132
|
+
const result = await bridge.call('requestWifiPermission');
|
|
133
|
+
// {
|
|
134
|
+
// locationGranted: true,
|
|
135
|
+
// canAccessWifiInfo: true,
|
|
136
|
+
// requiredPermissions: []
|
|
137
|
+
// }
|
|
126
138
|
```
|
|
127
139
|
|
|
128
140
|
---
|
|
129
141
|
|
|
130
|
-
##
|
|
131
|
-
|
|
132
|
-
### Task Callback
|
|
133
|
-
|
|
134
|
-
작업 등록 시 `callback` 함수를 지정하면 해당 작업의 모든 이벤트를 수신합니다.
|
|
135
|
-
|
|
136
|
-
```typescript
|
|
137
|
-
bridge.call('registerTask', {
|
|
138
|
-
taskId: 'my-task',
|
|
139
|
-
mode: 'persistent',
|
|
140
|
-
callback: (event) => {
|
|
141
|
-
switch (event.type) {
|
|
142
|
-
case 'started':
|
|
143
|
-
console.log('작업 시작됨');
|
|
144
|
-
break;
|
|
145
|
-
case 'stopped':
|
|
146
|
-
console.log('작업 중지됨');
|
|
147
|
-
break;
|
|
148
|
-
case 'trigger':
|
|
149
|
-
console.log(`트리거 발생: ${event.trigger}`, event.data);
|
|
150
|
-
break;
|
|
151
|
-
case 'action':
|
|
152
|
-
console.log(`액션 클릭: ${event.actionId}`);
|
|
153
|
-
break;
|
|
154
|
-
case 'error':
|
|
155
|
-
console.error('에러 발생:', event.error);
|
|
156
|
-
break;
|
|
157
|
-
}
|
|
158
|
-
},
|
|
159
|
-
notification: { title: 'Task', body: 'Running...' }
|
|
160
|
-
});
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
### Action Callback
|
|
164
|
-
|
|
165
|
-
각 알림 액션 버튼에 `onPress` 콜백을 개별 지정할 수 있습니다.
|
|
166
|
-
|
|
167
|
-
```typescript
|
|
168
|
-
bridge.call('registerTask', {
|
|
169
|
-
taskId: 'download-task',
|
|
170
|
-
mode: 'persistent',
|
|
171
|
-
notification: {
|
|
172
|
-
title: '다운로드 중',
|
|
173
|
-
body: '파일 다운로드...',
|
|
174
|
-
actions: [
|
|
175
|
-
{
|
|
176
|
-
id: 'pause',
|
|
177
|
-
title: '일시정지',
|
|
178
|
-
icon: 'ic_pause',
|
|
179
|
-
dismissOnPress: false,
|
|
180
|
-
onPress: async (actionId, taskId) => {
|
|
181
|
-
await pauseDownload();
|
|
182
|
-
bridge.call('updateNotification', {
|
|
183
|
-
taskId,
|
|
184
|
-
title: '일시정지됨',
|
|
185
|
-
body: '다운로드가 일시정지되었습니다'
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
},
|
|
189
|
-
{
|
|
190
|
-
id: 'cancel',
|
|
191
|
-
title: '취소',
|
|
192
|
-
dismissOnPress: true,
|
|
193
|
-
bringToForeground: false,
|
|
194
|
-
onPress: async (actionId, taskId) => {
|
|
195
|
-
await cancelDownload();
|
|
196
|
-
bridge.call('stopTask', { taskId });
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
]
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
### Callback ID
|
|
205
|
-
|
|
206
|
-
`callbackId`를 지정하면 이벤트 수신 시 해당 ID가 함께 전달되어 여러 작업의 이벤트를 구분할 수 있습니다.
|
|
207
|
-
|
|
208
|
-
```typescript
|
|
209
|
-
// 작업 등록
|
|
210
|
-
bridge.call('registerTask', {
|
|
211
|
-
taskId: 'task-1',
|
|
212
|
-
callbackId: 'sync-callback',
|
|
213
|
-
// ...
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
bridge.call('registerTask', {
|
|
217
|
-
taskId: 'task-2',
|
|
218
|
-
callbackId: 'upload-callback',
|
|
219
|
-
// ...
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
// 이벤트 수신
|
|
223
|
-
bridge.on('onTaskEvent', (event) => {
|
|
224
|
-
if (event.callbackId === 'sync-callback') {
|
|
225
|
-
// task-1의 이벤트 처리
|
|
226
|
-
} else if (event.callbackId === 'upload-callback') {
|
|
227
|
-
// task-2의 이벤트 처리
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
---
|
|
233
|
-
|
|
234
|
-
## 트리거 시스템
|
|
235
|
-
|
|
236
|
-
### 트리거 종류
|
|
237
|
-
|
|
238
|
-
| 트리거 | 설명 |
|
|
239
|
-
|--------|------|
|
|
240
|
-
| `interval` | 주기적 실행 (interval 옵션 사용) |
|
|
241
|
-
| `network_change` | 네트워크 상태 변경 (연결/해제) |
|
|
242
|
-
| `location_change` | 위치 변경 (significant location change) |
|
|
243
|
-
| `time_trigger` | 예약된 시간에 실행 |
|
|
244
|
-
| `battery_low` | 배터리 부족 (기본 15% 이하) |
|
|
245
|
-
| `battery_okay` | 배터리 정상 복귀 |
|
|
246
|
-
| `battery_charging` | 충전 시작 |
|
|
247
|
-
| `battery_discharging` | 충전 해제 |
|
|
248
|
-
| `app_foreground` | 앱이 포그라운드로 전환 |
|
|
249
|
-
| `app_background` | 앱이 백그라운드로 전환 |
|
|
250
|
-
| `app_terminate` | 앱 종료 시 |
|
|
251
|
-
| `custom` | 사용자 정의 트리거 |
|
|
252
|
-
|
|
253
|
-
### 간단한 사용법
|
|
254
|
-
|
|
255
|
-
문자열로 트리거 타입만 지정:
|
|
256
|
-
|
|
257
|
-
```typescript
|
|
258
|
-
bridge.call('registerTask', {
|
|
259
|
-
taskId: 'my-task',
|
|
260
|
-
mode: 'persistent',
|
|
261
|
-
triggers: ['network_change', 'battery_low', 'app_background'],
|
|
262
|
-
// ...
|
|
263
|
-
});
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
### 상세 설정
|
|
267
|
-
|
|
268
|
-
객체로 트리거별 옵션 지정:
|
|
269
|
-
|
|
270
|
-
```typescript
|
|
271
|
-
bridge.call('registerTask', {
|
|
272
|
-
taskId: 'my-task',
|
|
273
|
-
mode: 'persistent',
|
|
274
|
-
triggers: [
|
|
275
|
-
// 배터리 30% 이하일 때 트리거
|
|
276
|
-
{
|
|
277
|
-
type: 'battery_low',
|
|
278
|
-
options: { threshold: 30 }
|
|
279
|
-
},
|
|
280
|
-
// WiFi 연결 변경만 감지
|
|
281
|
-
{
|
|
282
|
-
type: 'network_change',
|
|
283
|
-
options: { networkTypes: ['wifi'] }
|
|
284
|
-
},
|
|
285
|
-
// 100m 이상 이동 시 트리거
|
|
286
|
-
{
|
|
287
|
-
type: 'location_change',
|
|
288
|
-
options: { minDistance: 100 }
|
|
289
|
-
},
|
|
290
|
-
// 사용자 정의 트리거
|
|
291
|
-
{
|
|
292
|
-
type: 'custom',
|
|
293
|
-
customId: 'my-custom-trigger'
|
|
294
|
-
}
|
|
295
|
-
],
|
|
296
|
-
callback: (event) => {
|
|
297
|
-
if (event.type === 'trigger') {
|
|
298
|
-
switch (event.trigger) {
|
|
299
|
-
case 'battery_low':
|
|
300
|
-
console.log('배터리 레벨:', event.data?.batteryLevel);
|
|
301
|
-
break;
|
|
302
|
-
case 'network_change':
|
|
303
|
-
console.log('네트워크:', event.data?.networkType, event.data?.isConnected);
|
|
304
|
-
break;
|
|
305
|
-
case 'location_change':
|
|
306
|
-
console.log('위치:', event.data?.location);
|
|
307
|
-
break;
|
|
308
|
-
case 'custom':
|
|
309
|
-
console.log('커스텀 트리거:', event.customTriggerId);
|
|
310
|
-
break;
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
},
|
|
314
|
-
// ...
|
|
315
|
-
});
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
### 트리거 옵션 상세
|
|
319
|
-
|
|
320
|
-
#### battery_low / battery_okay
|
|
321
|
-
|
|
322
|
-
```typescript
|
|
323
|
-
{
|
|
324
|
-
type: 'battery_low',
|
|
325
|
-
options: {
|
|
326
|
-
threshold: 20 // 배터리 임계값 % (기본: 15)
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
이벤트 데이터:
|
|
332
|
-
```typescript
|
|
333
|
-
event.data?.batteryLevel // 현재 배터리 레벨 (%)
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
#### network_change
|
|
337
|
-
|
|
338
|
-
```typescript
|
|
339
|
-
{
|
|
340
|
-
type: 'network_change',
|
|
341
|
-
options: {
|
|
342
|
-
networkTypes: ['wifi', 'cellular'] // 감지할 네트워크 타입
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
이벤트 데이터:
|
|
348
|
-
```typescript
|
|
349
|
-
event.data?.networkType // 'wifi' | 'cellular' | 'ethernet' | 'none'
|
|
350
|
-
event.data?.isConnected // 연결 상태 (boolean)
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
#### location_change
|
|
354
|
-
|
|
355
|
-
```typescript
|
|
356
|
-
{
|
|
357
|
-
type: 'location_change',
|
|
358
|
-
options: {
|
|
359
|
-
minDistance: 50 // 최소 이동 거리 (미터)
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
이벤트 데이터:
|
|
365
|
-
```typescript
|
|
366
|
-
event.data?.location // { latitude: number, longitude: number }
|
|
367
|
-
```
|
|
142
|
+
## 이벤트
|
|
368
143
|
|
|
369
|
-
|
|
144
|
+
### onWifiStateChange
|
|
370
145
|
|
|
371
|
-
|
|
146
|
+
WiFi 상태 변경 시 이벤트를 수신합니다.
|
|
372
147
|
|
|
373
148
|
```typescript
|
|
374
|
-
bridge.
|
|
375
|
-
|
|
376
|
-
mode: 'efficient',
|
|
377
|
-
triggers: ['time_trigger'],
|
|
378
|
-
scheduledTime: Date.now() + 3600000, // 1시간 후
|
|
379
|
-
// ...
|
|
149
|
+
bridge.on('onWifiStateChange', (event) => {
|
|
150
|
+
console.log('WiFi state changed:', event);
|
|
380
151
|
});
|
|
381
152
|
```
|
|
382
153
|
|
|
383
|
-
####
|
|
384
|
-
|
|
385
|
-
사용자 정의 트리거. 네이티브 측에서 직접 발생시킬 수 있음:
|
|
154
|
+
#### WifiStateChangeEvent 구조
|
|
386
155
|
|
|
387
156
|
```typescript
|
|
388
|
-
{
|
|
389
|
-
type: '
|
|
390
|
-
|
|
157
|
+
interface WifiStateChangeEvent {
|
|
158
|
+
type: 'WIFI_STATE_CHANGED' | 'CONNECTION_STATE_CHANGED' | 'NETWORK_INFO_CHANGED';
|
|
159
|
+
wifiState?: 'DISABLED' | 'DISABLING' | 'ENABLED' | 'ENABLING' | 'UNKNOWN';
|
|
160
|
+
connectionState?: 'DISCONNECTED' | 'CONNECTING' | 'CONNECTED' | 'DISCONNECTING' | 'UNKNOWN';
|
|
161
|
+
wifiInfo?: WifiInfo;
|
|
162
|
+
timestamp: number;
|
|
391
163
|
}
|
|
392
164
|
```
|
|
393
165
|
|
|
394
|
-
이벤트에서 `event.customTriggerId`로 식별 가능.
|
|
395
|
-
|
|
396
166
|
---
|
|
397
167
|
|
|
398
|
-
##
|
|
168
|
+
## 타입 정의
|
|
399
169
|
|
|
400
|
-
###
|
|
170
|
+
### WifiInfo
|
|
401
171
|
|
|
402
172
|
```typescript
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
173
|
+
interface WifiInfo {
|
|
174
|
+
ssid: string | null;
|
|
175
|
+
bssid: string | null;
|
|
176
|
+
rssi: number | null; // 신호 강도 (dBm)
|
|
177
|
+
signalLevel: number | null; // 신호 레벨 (0-100)
|
|
178
|
+
frequency: number | null; // 주파수 (MHz)
|
|
179
|
+
linkSpeed: number | null; // 링크 속도 (Mbps)
|
|
180
|
+
ipAddress: string | null;
|
|
181
|
+
isConnected: boolean;
|
|
182
|
+
networkId?: number;
|
|
183
|
+
security?: WifiSecurityType;
|
|
407
184
|
}
|
|
408
185
|
```
|
|
409
186
|
|
|
410
|
-
###
|
|
187
|
+
### WifiNetwork
|
|
411
188
|
|
|
412
189
|
```typescript
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
// 'min': 최소 (무음, 상태바만)
|
|
422
|
-
// 'low': 낮음 (무음)
|
|
423
|
-
// 'default': 기본
|
|
424
|
-
// 'high': 높음 (헤드업 알림)
|
|
425
|
-
// 'max': 최대 (긴급)
|
|
426
|
-
priority: 'high',
|
|
427
|
-
|
|
428
|
-
// 지속 알림 (사용자가 스와이프로 닫을 수 없음)
|
|
429
|
-
// persistent 모드에서는 항상 true
|
|
430
|
-
ongoing: true,
|
|
190
|
+
interface WifiNetwork {
|
|
191
|
+
ssid: string;
|
|
192
|
+
bssid: string;
|
|
193
|
+
rssi: number;
|
|
194
|
+
signalLevel: number;
|
|
195
|
+
frequency: number;
|
|
196
|
+
security: WifiSecurityType;
|
|
197
|
+
channel?: number;
|
|
431
198
|
}
|
|
432
199
|
```
|
|
433
200
|
|
|
434
|
-
###
|
|
201
|
+
### WifiSecurityType
|
|
435
202
|
|
|
436
203
|
```typescript
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
204
|
+
type WifiSecurityType =
|
|
205
|
+
| 'OPEN'
|
|
206
|
+
| 'WEP'
|
|
207
|
+
| 'WPA'
|
|
208
|
+
| 'WPA2'
|
|
209
|
+
| 'WPA3'
|
|
210
|
+
| 'WPA_WPA2'
|
|
211
|
+
| 'WPA2_WPA3'
|
|
212
|
+
| 'UNKNOWN';
|
|
446
213
|
```
|
|
447
214
|
|
|
448
|
-
|
|
215
|
+
### ConnectOptions
|
|
449
216
|
|
|
450
217
|
```typescript
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
218
|
+
interface ConnectOptions {
|
|
219
|
+
ssid: string;
|
|
220
|
+
password?: string;
|
|
221
|
+
security?: WifiSecurityType;
|
|
222
|
+
isHidden?: boolean;
|
|
223
|
+
timeout?: number;
|
|
455
224
|
}
|
|
456
225
|
```
|
|
457
226
|
|
|
458
|
-
###
|
|
459
|
-
|
|
460
|
-
최대 3개까지 지원:
|
|
227
|
+
### PermissionStatus
|
|
461
228
|
|
|
462
229
|
```typescript
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
bringToForeground: false, // 앱을 포그라운드로 가져오지 않음
|
|
473
|
-
onPress: (actionId, taskId) => {
|
|
474
|
-
playPrevious();
|
|
475
|
-
}
|
|
476
|
-
},
|
|
477
|
-
{
|
|
478
|
-
id: 'pause',
|
|
479
|
-
title: '일시정지',
|
|
480
|
-
icon: 'ic_pause',
|
|
481
|
-
onPress: (actionId, taskId) => {
|
|
482
|
-
togglePlayPause();
|
|
483
|
-
}
|
|
484
|
-
},
|
|
485
|
-
{
|
|
486
|
-
id: 'next',
|
|
487
|
-
title: '다음',
|
|
488
|
-
icon: 'ic_next',
|
|
489
|
-
onPress: (actionId, taskId) => {
|
|
490
|
-
playNext();
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
]
|
|
230
|
+
interface PermissionStatus {
|
|
231
|
+
locationGranted: boolean;
|
|
232
|
+
canAccessWifiInfo: boolean;
|
|
233
|
+
requiredPermissions: string[];
|
|
234
|
+
details?: {
|
|
235
|
+
fineLocation?: boolean;
|
|
236
|
+
coarseLocation?: boolean;
|
|
237
|
+
backgroundLocation?: boolean;
|
|
238
|
+
};
|
|
494
239
|
}
|
|
495
240
|
```
|
|
496
241
|
|
|
497
|
-
###
|
|
242
|
+
### WifiErrorCode
|
|
498
243
|
|
|
499
244
|
```typescript
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
245
|
+
type WifiErrorCode =
|
|
246
|
+
| 'PERMISSION_DENIED'
|
|
247
|
+
| 'WIFI_DISABLED'
|
|
248
|
+
| 'NETWORK_NOT_FOUND'
|
|
249
|
+
| 'AUTHENTICATION_FAILED'
|
|
250
|
+
| 'CONNECTION_FAILED'
|
|
251
|
+
| 'TIMEOUT'
|
|
252
|
+
| 'INVALID_SSID'
|
|
253
|
+
| 'INVALID_PASSWORD'
|
|
254
|
+
| 'NOT_SUPPORTED'
|
|
255
|
+
| 'MODULE_NOT_AVAILABLE'
|
|
256
|
+
| 'UNKNOWN';
|
|
507
257
|
```
|
|
508
258
|
|
|
509
259
|
---
|
|
510
260
|
|
|
511
|
-
##
|
|
261
|
+
## 권한 설정
|
|
512
262
|
|
|
513
|
-
###
|
|
263
|
+
### Android
|
|
514
264
|
|
|
515
|
-
|
|
265
|
+
`AndroidManifest.xml`에 자동 추가:
|
|
516
266
|
|
|
517
|
-
```
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
title: '실행 중',
|
|
524
|
-
body: '백그라운드 서비스 동작 중'
|
|
525
|
-
}
|
|
526
|
-
});
|
|
267
|
+
```xml
|
|
268
|
+
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
|
269
|
+
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
|
270
|
+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
271
|
+
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
|
272
|
+
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
|
527
273
|
```
|
|
528
274
|
|
|
529
|
-
|
|
530
|
-
- 시스템에 의해 종료되지 않음 (높은 우선순위)
|
|
531
|
-
- 항상 알림 표시 (ongoing: true 강제)
|
|
532
|
-
- interval 최소값: 1000ms (1초)
|
|
533
|
-
- 배터리 사용량 높음
|
|
275
|
+
> **Note:** Android 8.0 이상에서 WiFi SSID를 가져오려면 위치 권한이 필요합니다.
|
|
534
276
|
|
|
535
|
-
###
|
|
277
|
+
### iOS
|
|
536
278
|
|
|
537
|
-
|
|
279
|
+
`Info.plist`에 추가:
|
|
538
280
|
|
|
539
|
-
```
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
mode: 'efficient',
|
|
543
|
-
interval: 900000, // 최소 15분 (900000ms)
|
|
544
|
-
triggers: ['network_change'],
|
|
545
|
-
notification: { // 선택
|
|
546
|
-
title: '동기화',
|
|
547
|
-
body: '대기 중...'
|
|
548
|
-
}
|
|
549
|
-
});
|
|
281
|
+
```xml
|
|
282
|
+
<key>NSLocationWhenInUseUsageDescription</key>
|
|
283
|
+
<string>WiFi 정보를 가져오기 위해 위치 권한이 필요합니다.</string>
|
|
550
284
|
```
|
|
551
285
|
|
|
552
|
-
|
|
553
|
-
-
|
|
554
|
-
- interval 최소값: 900000ms (15분, 시스템 제한)
|
|
555
|
-
- 정확한 타이밍 보장 안됨
|
|
556
|
-
- 배터리 효율적
|
|
286
|
+
Xcode에서 **Signing & Capabilities**에 다음 추가:
|
|
287
|
+
- **Access WiFi Information** capability
|
|
557
288
|
|
|
558
289
|
---
|
|
559
290
|
|
|
560
|
-
##
|
|
291
|
+
## 플랫폼별 제한사항
|
|
561
292
|
|
|
562
|
-
###
|
|
293
|
+
### Android
|
|
563
294
|
|
|
564
|
-
|
|
295
|
+
- WiFi 스캔 (`getWifiList`)은 Android에서만 지원됩니다.
|
|
296
|
+
- Android 10 이상에서는 WiFi 연결 시 시스템 UI가 표시됩니다.
|
|
297
|
+
- Android 10 미만에서는 레거시 방식으로 연결됩니다.
|
|
565
298
|
|
|
566
|
-
|
|
567
|
-
bridge.on('onTaskEvent', (event) => {
|
|
568
|
-
console.log('Event:', event);
|
|
569
|
-
});
|
|
570
|
-
```
|
|
299
|
+
### iOS
|
|
571
300
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
taskId: string; // 작업 ID
|
|
577
|
-
callbackId?: string; // 등록 시 지정한 콜백 ID
|
|
578
|
-
type: 'started' | 'stopped' | 'restart' | 'error' | 'trigger' | 'action';
|
|
579
|
-
trigger?: TriggerType; // 트리거 종류 (type이 'trigger'일 때)
|
|
580
|
-
customTriggerId?: string; // custom 트리거 식별자
|
|
581
|
-
actionId?: string; // 액션 버튼 ID (type이 'action'일 때)
|
|
582
|
-
error?: string; // 에러 메시지 (type이 'error'일 때)
|
|
583
|
-
data?: { // 트리거 관련 데이터
|
|
584
|
-
batteryLevel?: number;
|
|
585
|
-
networkType?: 'wifi' | 'cellular' | 'ethernet' | 'none';
|
|
586
|
-
isConnected?: boolean;
|
|
587
|
-
location?: { latitude: number; longitude: number };
|
|
588
|
-
[key: string]: unknown;
|
|
589
|
-
};
|
|
590
|
-
timestamp: number; // 타임스탬프
|
|
591
|
-
}
|
|
592
|
-
```
|
|
301
|
+
- WiFi 스캔 (`getWifiList`)은 iOS에서 지원되지 않습니다.
|
|
302
|
+
- WiFi 연결은 iOS 11 이상에서만 지원됩니다.
|
|
303
|
+
- RSSI, frequency, linkSpeed 등 일부 정보는 iOS에서 제공되지 않습니다.
|
|
304
|
+
- WiFi 정보 접근을 위해 **Access WiFi Information** entitlement가 필요합니다.
|
|
593
305
|
|
|
594
306
|
---
|
|
595
307
|
|
|
596
|
-
##
|
|
308
|
+
## 예제
|
|
597
309
|
|
|
598
|
-
###
|
|
310
|
+
### WiFi 정보 표시
|
|
599
311
|
|
|
600
312
|
```typescript
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
callbackId?: string;
|
|
608
|
-
callback?: (event: TaskEvent) => void | Promise<void>;
|
|
609
|
-
notification?: NotificationConfig;
|
|
610
|
-
}
|
|
611
|
-
```
|
|
612
|
-
|
|
613
|
-
### TriggerConfig
|
|
614
|
-
|
|
615
|
-
```typescript
|
|
616
|
-
type TriggerType =
|
|
617
|
-
| 'interval'
|
|
618
|
-
| 'network_change'
|
|
619
|
-
| 'location_change'
|
|
620
|
-
| 'time_trigger'
|
|
621
|
-
| 'battery_low'
|
|
622
|
-
| 'battery_okay'
|
|
623
|
-
| 'battery_charging'
|
|
624
|
-
| 'battery_discharging'
|
|
625
|
-
| 'app_foreground'
|
|
626
|
-
| 'app_background'
|
|
627
|
-
| 'app_terminate'
|
|
628
|
-
| 'custom';
|
|
629
|
-
|
|
630
|
-
type TriggerConfig = TriggerType | {
|
|
631
|
-
type: TriggerType;
|
|
632
|
-
customId?: string;
|
|
633
|
-
options?: {
|
|
634
|
-
threshold?: number;
|
|
635
|
-
minDistance?: number;
|
|
636
|
-
networkTypes?: Array<'wifi' | 'cellular' | 'ethernet'>;
|
|
637
|
-
};
|
|
638
|
-
};
|
|
639
|
-
```
|
|
640
|
-
|
|
641
|
-
### NotificationConfig
|
|
642
|
-
|
|
643
|
-
```typescript
|
|
644
|
-
interface NotificationConfig {
|
|
645
|
-
taskId?: string;
|
|
646
|
-
title: string;
|
|
647
|
-
body: string;
|
|
648
|
-
icon?: string;
|
|
649
|
-
color?: string;
|
|
650
|
-
priority?: 'min' | 'low' | 'default' | 'high' | 'max';
|
|
651
|
-
ongoing?: boolean;
|
|
652
|
-
progress?: {
|
|
653
|
-
current: number;
|
|
654
|
-
max: number;
|
|
655
|
-
indeterminate?: boolean;
|
|
656
|
-
};
|
|
657
|
-
actions?: NotificationAction[];
|
|
658
|
-
channelId?: string;
|
|
659
|
-
channelName?: string;
|
|
660
|
-
channelDescription?: string;
|
|
661
|
-
}
|
|
662
|
-
```
|
|
663
|
-
|
|
664
|
-
### NotificationAction
|
|
313
|
+
async function showWifiInfo() {
|
|
314
|
+
// 권한 확인
|
|
315
|
+
const permission = await bridge.call('checkWifiPermission');
|
|
316
|
+
if (!permission.canAccessWifiInfo) {
|
|
317
|
+
await bridge.call('requestWifiPermission');
|
|
318
|
+
}
|
|
665
319
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
bringToForeground?: boolean; // 기본: false
|
|
320
|
+
// WiFi 정보 조회
|
|
321
|
+
const result = await bridge.call('getCurrentWifiInfo');
|
|
322
|
+
if (result.success && result.data) {
|
|
323
|
+
console.log(`Connected to: ${result.data.ssid}`);
|
|
324
|
+
console.log(`Signal: ${result.data.signalLevel}%`);
|
|
325
|
+
console.log(`IP: ${result.data.ipAddress}`);
|
|
326
|
+
}
|
|
674
327
|
}
|
|
675
328
|
```
|
|
676
329
|
|
|
677
|
-
###
|
|
330
|
+
### WiFi 연결
|
|
678
331
|
|
|
679
332
|
```typescript
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
| 'PERMISSION_DENIED'
|
|
686
|
-
| 'SYSTEM_RESTRICTED'
|
|
687
|
-
| 'WEBVIEW_INIT_FAILED'
|
|
688
|
-
| 'INVALID_INPUT'
|
|
689
|
-
| 'INVALID_INTERVAL'
|
|
690
|
-
| 'INVALID_TRIGGER'
|
|
691
|
-
| 'NOTIFICATION_REQUIRED'
|
|
692
|
-
| 'UNKNOWN';
|
|
693
|
-
```
|
|
694
|
-
|
|
695
|
-
---
|
|
696
|
-
|
|
697
|
-
## 권한 설정
|
|
698
|
-
|
|
699
|
-
### Android
|
|
700
|
-
|
|
701
|
-
`AndroidManifest.xml`에 자동 추가:
|
|
702
|
-
|
|
703
|
-
```xml
|
|
704
|
-
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
705
|
-
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
|
706
|
-
```
|
|
707
|
-
|
|
708
|
-
### iOS
|
|
709
|
-
|
|
710
|
-
`Info.plist`에 추가:
|
|
711
|
-
|
|
712
|
-
```xml
|
|
713
|
-
<key>UIBackgroundModes</key>
|
|
714
|
-
<array>
|
|
715
|
-
<string>fetch</string>
|
|
716
|
-
<string>processing</string>
|
|
717
|
-
</array>
|
|
718
|
-
|
|
719
|
-
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
|
720
|
-
<array>
|
|
721
|
-
<string>$(PRODUCT_BUNDLE_IDENTIFIER).background</string>
|
|
722
|
-
</array>
|
|
723
|
-
```
|
|
724
|
-
|
|
725
|
-
---
|
|
726
|
-
|
|
727
|
-
## 예제: 음악 플레이어
|
|
333
|
+
async function connectWifi(ssid: string, password: string) {
|
|
334
|
+
const result = await bridge.call('connectToWifi', {
|
|
335
|
+
ssid,
|
|
336
|
+
password
|
|
337
|
+
});
|
|
728
338
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
mode: 'persistent',
|
|
734
|
-
triggers: [
|
|
735
|
-
'app_background',
|
|
736
|
-
{ type: 'battery_low', options: { threshold: 10 } }
|
|
737
|
-
],
|
|
738
|
-
callback: (event) => {
|
|
739
|
-
if (event.type === 'trigger' && event.trigger === 'battery_low') {
|
|
740
|
-
// 배터리 부족 시 품질 낮춤
|
|
741
|
-
setStreamQuality('low');
|
|
742
|
-
}
|
|
743
|
-
},
|
|
744
|
-
notification: {
|
|
745
|
-
title: 'Now Playing',
|
|
746
|
-
body: 'Artist - Song Title',
|
|
747
|
-
color: '#1DB954',
|
|
748
|
-
ongoing: true,
|
|
749
|
-
actions: [
|
|
750
|
-
{
|
|
751
|
-
id: 'prev',
|
|
752
|
-
title: '이전',
|
|
753
|
-
icon: 'ic_skip_previous',
|
|
754
|
-
dismissOnPress: false,
|
|
755
|
-
onPress: () => skipToPrevious()
|
|
756
|
-
},
|
|
757
|
-
{
|
|
758
|
-
id: 'play_pause',
|
|
759
|
-
title: '재생/일시정지',
|
|
760
|
-
icon: 'ic_play_pause',
|
|
761
|
-
dismissOnPress: false,
|
|
762
|
-
onPress: () => togglePlayPause()
|
|
763
|
-
},
|
|
764
|
-
{
|
|
765
|
-
id: 'next',
|
|
766
|
-
title: '다음',
|
|
767
|
-
icon: 'ic_skip_next',
|
|
768
|
-
dismissOnPress: false,
|
|
769
|
-
onPress: () => skipToNext()
|
|
770
|
-
}
|
|
771
|
-
]
|
|
339
|
+
if (result.success) {
|
|
340
|
+
console.log('Connected successfully!');
|
|
341
|
+
} else {
|
|
342
|
+
console.error(`Connection failed: ${result.error}`);
|
|
772
343
|
}
|
|
773
|
-
});
|
|
774
|
-
|
|
775
|
-
// 트랙 변경 시 알림 업데이트
|
|
776
|
-
function onTrackChange(track) {
|
|
777
|
-
bridge.call('updateNotification', {
|
|
778
|
-
taskId: 'music-player',
|
|
779
|
-
title: track.title,
|
|
780
|
-
body: `${track.artist} - ${track.album}`,
|
|
781
|
-
});
|
|
782
344
|
}
|
|
783
345
|
```
|
|
784
346
|
|
|
785
|
-
|
|
347
|
+
### 상태 변경 모니터링
|
|
786
348
|
|
|
787
349
|
```typescript
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
callback: async (event) => {
|
|
798
|
-
if (event.type === 'trigger') {
|
|
799
|
-
// WiFi 연결되거나 충전 시작 시 동기화
|
|
800
|
-
await syncFiles();
|
|
801
|
-
}
|
|
802
|
-
},
|
|
803
|
-
notification: {
|
|
804
|
-
title: '동기화 대기 중',
|
|
805
|
-
body: '다음 동기화 예약됨',
|
|
806
|
-
priority: 'low'
|
|
807
|
-
}
|
|
808
|
-
});
|
|
809
|
-
|
|
810
|
-
// 동기화 진행 시 알림 업데이트
|
|
811
|
-
async function syncFiles() {
|
|
812
|
-
const files = await getFilesToSync();
|
|
813
|
-
|
|
814
|
-
for (let i = 0; i < files.length; i++) {
|
|
815
|
-
bridge.call('updateNotification', {
|
|
816
|
-
taskId: 'file-sync',
|
|
817
|
-
title: '동기화 중',
|
|
818
|
-
body: `${files[i].name}`,
|
|
819
|
-
progress: {
|
|
820
|
-
current: i + 1,
|
|
821
|
-
max: files.length
|
|
350
|
+
bridge.on('onWifiStateChange', (event) => {
|
|
351
|
+
switch (event.type) {
|
|
352
|
+
case 'WIFI_STATE_CHANGED':
|
|
353
|
+
console.log(`WiFi state: ${event.wifiState}`);
|
|
354
|
+
break;
|
|
355
|
+
case 'CONNECTION_STATE_CHANGED':
|
|
356
|
+
console.log(`Connection: ${event.connectionState}`);
|
|
357
|
+
if (event.wifiInfo) {
|
|
358
|
+
console.log(`SSID: ${event.wifiInfo.ssid}`);
|
|
822
359
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
await uploadFile(files[i]);
|
|
360
|
+
break;
|
|
826
361
|
}
|
|
827
|
-
|
|
828
|
-
bridge.call('updateNotification', {
|
|
829
|
-
taskId: 'file-sync',
|
|
830
|
-
title: '동기화 완료',
|
|
831
|
-
body: `${files.length}개 파일 동기화됨`,
|
|
832
|
-
progress: undefined
|
|
833
|
-
});
|
|
834
|
-
}
|
|
362
|
+
});
|
|
835
363
|
```
|
|
836
364
|
|
|
837
365
|
---
|
package/package.json
CHANGED