esp-idf-provisioning-web 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.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +129 -0
  3. package/dist/ESPDevice.d.ts +66 -0
  4. package/dist/ESPDevice.d.ts.map +1 -0
  5. package/dist/ESPProvisionManager.d.ts +34 -0
  6. package/dist/ESPProvisionManager.d.ts.map +1 -0
  7. package/dist/constants.d.ts +19 -0
  8. package/dist/constants.d.ts.map +1 -0
  9. package/dist/index.d.ts +16 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.es.js +5085 -0
  12. package/dist/protocol/protocol.d.ts +4 -0
  13. package/dist/protocol/protocol.d.ts.map +1 -0
  14. package/dist/protocol/wifiConfig.d.ts +8 -0
  15. package/dist/protocol/wifiConfig.d.ts.map +1 -0
  16. package/dist/protocol/wifiScan.d.ts +8 -0
  17. package/dist/protocol/wifiScan.d.ts.map +1 -0
  18. package/dist/security/Security.d.ts +26 -0
  19. package/dist/security/Security.d.ts.map +1 -0
  20. package/dist/security/Security0.d.ts +13 -0
  21. package/dist/security/Security0.d.ts.map +1 -0
  22. package/dist/transport/BLETransport.d.ts +22 -0
  23. package/dist/transport/BLETransport.d.ts.map +1 -0
  24. package/dist/transport/SoftAPTransport.d.ts +27 -0
  25. package/dist/transport/SoftAPTransport.d.ts.map +1 -0
  26. package/dist/transport/Transport.d.ts +22 -0
  27. package/dist/transport/Transport.d.ts.map +1 -0
  28. package/dist/types.d.ts +73 -0
  29. package/dist/types.d.ts.map +1 -0
  30. package/dist/utils/crypto.d.ts +44 -0
  31. package/dist/utils/crypto.d.ts.map +1 -0
  32. package/package.json +60 -0
  33. package/src/ESPDevice.ts +231 -0
  34. package/src/ESPProvisionManager.ts +78 -0
  35. package/src/constants.ts +29 -0
  36. package/src/generated/.gitkeep +0 -0
  37. package/src/generated/proto.d.ts +3418 -0
  38. package/src/generated/proto.js +10006 -0
  39. package/src/index.ts +31 -0
  40. package/src/proto/constants.proto +14 -0
  41. package/src/proto/sec0.proto +28 -0
  42. package/src/proto/sec1.proto +45 -0
  43. package/src/proto/sec2.proto +48 -0
  44. package/src/proto/session.proto +25 -0
  45. package/src/proto/wifi_config.proto +57 -0
  46. package/src/proto/wifi_constants.proto +32 -0
  47. package/src/proto/wifi_ctrl.proto +38 -0
  48. package/src/proto/wifi_scan.proto +63 -0
  49. package/src/protocol/protocol.ts +23 -0
  50. package/src/protocol/wifiConfig.ts +108 -0
  51. package/src/protocol/wifiScan.ts +91 -0
  52. package/src/security/Security.ts +29 -0
  53. package/src/security/Security0.ts +40 -0
  54. package/src/transport/BLETransport.ts +111 -0
  55. package/src/transport/SoftAPTransport.ts +89 -0
  56. package/src/transport/Transport.ts +24 -0
  57. package/src/types.ts +86 -0
  58. package/src/utils/crypto.ts +129 -0
@@ -0,0 +1,4 @@
1
+ import * as proto from '../generated/proto.js';
2
+ import { ESPWifiAuthMode } from '../types.js';
3
+ export declare function wifiAuthModeToESPWifiAuthMode(mode: proto.WifiAuthMode): ESPWifiAuthMode;
4
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/protocol/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,KAAK,CAAC,YAAY,GAAG,eAAe,CAmBvF"}
@@ -0,0 +1,8 @@
1
+ import { ESPWifiStatus } from '../types.js';
2
+ export declare function getConfigStatusRequest(): Uint8Array;
3
+ export declare function processConfigStatusResponse(response: Uint8Array): ESPWifiStatus;
4
+ export declare function getConfigUpdateRequest(ssid: string, passphrase: string): Uint8Array;
5
+ export declare function processConfigUpdateResponse(response: Uint8Array): void;
6
+ export declare function getConfigApplyRequest(): Uint8Array;
7
+ export declare function processConfigApplyResponse(response: Uint8Array): void;
8
+ //# sourceMappingURL=wifiConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wifiConfig.d.ts","sourceRoot":"","sources":["../../src/protocol/wifiConfig.ts"],"names":[],"mappings":"AACA,OAAO,EAAoD,aAAa,EAAE,MAAM,aAAa,CAAC;AAI9F,wBAAgB,sBAAsB,IAAI,UAAU,CAKnD;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,UAAU,GAAG,aAAa,CAsD/E;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,UAAU,CAQnF;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,UAAU,GAAG,IAAI,CAUtE;AAED,wBAAgB,qBAAqB,IAAI,UAAU,CAKlD;AAED,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,UAAU,GAAG,IAAI,CAUrE"}
@@ -0,0 +1,8 @@
1
+ import { ESPScanStatusResponse, ESPWifiAp } from '../types.js';
2
+ export declare function getScanStartRequest(blocking?: boolean, passive?: boolean, groupChannels?: number, periodMs?: number): Uint8Array;
3
+ export declare function processScanStartResponse(response: Uint8Array): void;
4
+ export declare function getScanStatusRequest(): Uint8Array;
5
+ export declare function processScanStatusResponse(response: Uint8Array): ESPScanStatusResponse;
6
+ export declare function getScanResultRequest(index: number, count: number): Uint8Array;
7
+ export declare function processScanResultResponse(response: Uint8Array): ESPWifiAp[];
8
+ //# sourceMappingURL=wifiScan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wifiScan.d.ts","sourceRoot":"","sources":["../../src/protocol/wifiScan.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI/D,wBAAgB,mBAAmB,CACjC,QAAQ,GAAE,OAAc,EACxB,OAAO,GAAE,OAAe,EACxB,aAAa,GAAE,MAAU,EACzB,QAAQ,GAAE,MAAY,GACrB,UAAU,CAWZ;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,UAAU,GAAG,IAAI,CASnE;AAED,wBAAgB,oBAAoB,IAAI,UAAU,CAMjD;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,UAAU,GAAG,qBAAqB,CAcrF;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,CAS7E;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,UAAU,GAAG,SAAS,EAAE,CAqB3E"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Base class for security implementations
3
+ */
4
+ export declare abstract class Security {
5
+ /**
6
+ * Encrypt data for transmission
7
+ */
8
+ abstract encrypt(data: Uint8Array): Promise<Uint8Array>;
9
+ /**
10
+ * Decrypt received data
11
+ */
12
+ abstract decrypt(data: Uint8Array): Promise<Uint8Array>;
13
+ /**
14
+ * Get session setup request data
15
+ */
16
+ abstract getSessionSetupRequest(): Promise<Uint8Array>;
17
+ /**
18
+ * Process session setup response
19
+ */
20
+ abstract processSessionSetupResponse(response: Uint8Array): Promise<void>;
21
+ /**
22
+ * Check if security is established
23
+ */
24
+ abstract isEstablished(): boolean;
25
+ }
26
+ //# sourceMappingURL=Security.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Security.d.ts","sourceRoot":"","sources":["../../src/security/Security.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,8BAAsB,QAAQ;IAC5B;;OAEG;IACH,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAEvD;;OAEG;IACH,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAEvD;;OAEG;IACH,QAAQ,CAAC,sBAAsB,IAAI,OAAO,CAAC,UAAU,CAAC;IAEtD;;OAEG;IACH,QAAQ,CAAC,2BAA2B,CAAC,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAEzE;;OAEG;IACH,QAAQ,CAAC,aAAa,IAAI,OAAO;CAClC"}
@@ -0,0 +1,13 @@
1
+ import { Security } from './Security';
2
+ /**
3
+ * Security0 - No encryption (unsecure mode)
4
+ */
5
+ export declare class Security0 extends Security {
6
+ private established;
7
+ encrypt(data: Uint8Array): Promise<Uint8Array>;
8
+ decrypt(data: Uint8Array): Promise<Uint8Array>;
9
+ getSessionSetupRequest(): Promise<Uint8Array>;
10
+ processSessionSetupResponse(response: Uint8Array): Promise<void>;
11
+ isEstablished(): boolean;
12
+ }
13
+ //# sourceMappingURL=Security0.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Security0.d.ts","sourceRoot":"","sources":["../../src/security/Security0.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC;;GAEG;AACH,qBAAa,SAAU,SAAQ,QAAQ;IACrC,OAAO,CAAC,WAAW,CAAkB;IAE/B,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAI9C,OAAO,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAI9C,sBAAsB,IAAI,OAAO,CAAC,UAAU,CAAC;IAU7C,2BAA2B,CAAC,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAStE,aAAa,IAAI,OAAO;CAGzB"}
@@ -0,0 +1,22 @@
1
+ import { Transport } from './Transport';
2
+ /**
3
+ * BLE Transport implementation using Web Bluetooth API
4
+ */
5
+ export declare class BLETransport extends Transport {
6
+ private device;
7
+ private serviceUUID;
8
+ private nuLookup;
9
+ private server?;
10
+ private service?;
11
+ private connected;
12
+ constructor(device: BluetoothDevice, serviceUUID: string, nuLookup: Map<string, string>);
13
+ connect(): Promise<void>;
14
+ disconnect(): void;
15
+ sendData(endpoint: string, data: Uint8Array): Promise<Uint8Array>;
16
+ isConnected(): boolean;
17
+ /**
18
+ * Get the underlying Bluetooth device
19
+ */
20
+ getDevice(): BluetoothDevice;
21
+ }
22
+ //# sourceMappingURL=BLETransport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BLETransport.d.ts","sourceRoot":"","sources":["../../src/transport/BLETransport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAIxC;;GAEG;AACH,qBAAa,YAAa,SAAQ,SAAS;IACzC,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAsB;IAEtC,OAAO,CAAC,MAAM,CAAC,CAA4B;IAC3C,OAAO,CAAC,OAAO,CAAC,CAA6B;IAC7C,OAAO,CAAC,SAAS,CAAkB;gBAEvB,MAAM,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAOjF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA8C9B,UAAU,IAAI,IAAI;IASZ,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAsBvE,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,SAAS,IAAI,eAAe;CAG7B"}
@@ -0,0 +1,27 @@
1
+ import { Transport } from './Transport';
2
+ /**
3
+ * SoftAP Transport implementation using HTTP
4
+ * Note: Browser cannot automatically connect to WiFi networks.
5
+ * User must manually connect to the device's SoftAP before using this transport.
6
+ */
7
+ export declare class SoftAPTransport extends Transport {
8
+ private baseUrl;
9
+ private connected;
10
+ /**
11
+ * @param baseUrl - Base URL of the device (e.g., http://192.168.4.1)
12
+ */
13
+ constructor(baseUrl: string);
14
+ connect(): Promise<void>;
15
+ disconnect(): void;
16
+ sendData(endpoint: string, data: Uint8Array): Promise<Uint8Array>;
17
+ isConnected(): boolean;
18
+ /**
19
+ * Set custom base URL
20
+ */
21
+ setBaseUrl(url: string): void;
22
+ /**
23
+ * Get current base URL
24
+ */
25
+ getBaseUrl(): string;
26
+ }
27
+ //# sourceMappingURL=SoftAPTransport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SoftAPTransport.d.ts","sourceRoot":"","sources":["../../src/transport/SoftAPTransport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC;;;;GAIG;AACH,qBAAa,eAAgB,SAAQ,SAAS;IAC5C,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAkB;IAEnC;;OAEG;gBACS,OAAO,EAAE,MAAM;IAKrB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAqB9B,UAAU,IAAI,IAAI;IAIZ,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IA2BvE,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAI7B;;OAEG;IACH,UAAU,IAAI,MAAM;CAGrB"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Base class for transport implementations
3
+ */
4
+ export declare abstract class Transport {
5
+ /**
6
+ * Connect to the device
7
+ */
8
+ abstract connect(): Promise<void>;
9
+ /**
10
+ * Disconnect from the device
11
+ */
12
+ abstract disconnect(): void;
13
+ /**
14
+ * Send data to a specific endpoint
15
+ */
16
+ abstract sendData(endpoint: string, data: Uint8Array): Promise<Uint8Array>;
17
+ /**
18
+ * Check if transport is connected
19
+ */
20
+ abstract isConnected(): boolean;
21
+ }
22
+ //# sourceMappingURL=Transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Transport.d.ts","sourceRoot":"","sources":["../../src/transport/Transport.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,8BAAsB,SAAS;IAC7B;;OAEG;IACH,QAAQ,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAEjC;;OAEG;IACH,QAAQ,CAAC,UAAU,IAAI,IAAI;IAE3B;;OAEG;IACH,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;IAE1E;;OAEG;IACH,QAAQ,CAAC,WAAW,IAAI,OAAO;CAChC"}
@@ -0,0 +1,73 @@
1
+ export type ESPSecurityConfig = ESPSecurity1Config | ESPSecurity2Config;
2
+ export interface ESPSecurity1Config {
3
+ type: 'Security1';
4
+ proofOfPossession: string;
5
+ }
6
+ export interface ESPSecurity2Config {
7
+ type: 'Security2';
8
+ username: string;
9
+ password: string;
10
+ }
11
+ /**
12
+ * WiFi authentication modes
13
+ */
14
+ export declare enum ESPWifiAuthMode {
15
+ open = 0,
16
+ wep = 1,
17
+ wpa2Enterprise = 2,
18
+ wpa2Psk = 3,
19
+ wpaPsk = 4,
20
+ wpaWpa2Psk = 5,
21
+ wpa3Psk = 6,
22
+ wpa2Wpa3Psk = 7
23
+ }
24
+ /**
25
+ * WiFi STA state
26
+ */
27
+ export declare enum ESPWifiStaState {
28
+ connected = 0,
29
+ connecting = 1,
30
+ disconnected = 2,
31
+ failed = 3
32
+ }
33
+ /**
34
+ * WiFi scan failure reasons
35
+ */
36
+ export declare enum ESPConnectFailReason {
37
+ authError = 1,
38
+ networkNotFound = 2
39
+ }
40
+ export interface ESPWifiStatus {
41
+ staState: ESPWifiStaState;
42
+ failedReason?: ESPConnectFailReason;
43
+ connected?: ESPWifiAp;
44
+ }
45
+ /**
46
+ * WiFi network information returned by device scan
47
+ */
48
+ export interface ESPWifiAp {
49
+ ssid: string;
50
+ rssi: number;
51
+ auth: ESPWifiAuthMode;
52
+ bssid: string;
53
+ channel: number;
54
+ }
55
+ /**
56
+ * Scan status response
57
+ */
58
+ export interface ESPScanStatusResponse {
59
+ scanFinished: boolean;
60
+ count: number;
61
+ }
62
+ /**
63
+ * Device interface for ESP provisioning
64
+ */
65
+ export interface ESPDeviceInterface {
66
+ connect(security?: ESPSecurityConfig): Promise<void>;
67
+ scanWifiList(): Promise<ESPWifiAp[]>;
68
+ disconnect(): void;
69
+ provision(ssid: string, passphrase: string): Promise<void>;
70
+ fetchWifiStatus(): Promise<ESPWifiStatus>;
71
+ getDevice(): BluetoothDevice | URL;
72
+ }
73
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,kBAAkB,GAAG,kBAAkB,CAAC;AAExE,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,WAAW,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,oBAAY,eAAe;IACzB,IAAI,IAAI;IACR,GAAG,IAAI;IACP,cAAc,IAAI;IAClB,OAAO,IAAI;IACX,MAAM,IAAI;IACV,UAAU,IAAI;IACd,OAAO,IAAI;IACX,WAAW,IAAI;CAChB;AAED;;GAEG;AACH,oBAAY,eAAe;IACzB,SAAS,IAAI;IACb,UAAU,IAAI;IACd,YAAY,IAAI;IAChB,MAAM,IAAI;CACX;AAED;;GAEG;AACH,oBAAY,oBAAoB;IAC9B,SAAS,IAAI;IACb,eAAe,IAAI;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,eAAe,CAAC;IAC1B,YAAY,CAAC,EAAE,oBAAoB,CAAC;IACpC,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,QAAQ,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAErD,YAAY,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAErC,UAAU,IAAI,IAAI,CAAC;IAEnB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE3D,eAAe,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;IAE1C,SAAS,IAAI,eAAe,GAAG,GAAG,CAAC;CACpC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Cryptographic utilities for ESP provisioning protocol
3
+ */
4
+ /**
5
+ * Generate random bytes
6
+ */
7
+ export declare function generateRandomBytes(length: number): Uint8Array;
8
+ /**
9
+ * Convert hex string to Uint8Array
10
+ */
11
+ export declare function hexToBytes(hex: string): Uint8Array;
12
+ /**
13
+ * Convert Uint8Array to hex string
14
+ */
15
+ export declare function bytesToHex(bytes: Uint8Array): string;
16
+ /**
17
+ * Convert string to Uint8Array
18
+ */
19
+ export declare function stringToBytes(str: string): Uint8Array;
20
+ /**
21
+ * Convert Uint8Array to string
22
+ */
23
+ export declare function bytesToString(bytes: Uint8Array): string;
24
+ /**
25
+ * SHA-256 hash function
26
+ */
27
+ export declare function sha256(data: Uint8Array): Promise<Uint8Array>;
28
+ /**
29
+ * HMAC-SHA-256
30
+ */
31
+ export declare function hmacSha256(key: Uint8Array, data: Uint8Array): Promise<Uint8Array>;
32
+ /**
33
+ * AES-CTR encryption
34
+ */
35
+ export declare function aesEncrypt(key: Uint8Array, counter: Uint8Array, data: Uint8Array): Promise<Uint8Array>;
36
+ /**
37
+ * AES-CTR decryption
38
+ */
39
+ export declare function aesDecrypt(key: Uint8Array, counter: Uint8Array, data: Uint8Array): Promise<Uint8Array>;
40
+ /**
41
+ * Concatenate multiple Uint8Arrays
42
+ */
43
+ export declare function concatBytes(...arrays: Uint8Array[]): Uint8Array;
44
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/utils/crypto.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAI9D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAMlD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAIpD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAErD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAEvD;AAED;;GAEG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAGlE;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAUvF;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,UAAU,EACnB,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,UAAU,CAAC,CAcrB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,UAAU,EACnB,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,UAAU,CAAC,CAcrB;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,GAAG,UAAU,CAS/D"}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "esp-idf-provisioning-web",
3
+ "version": "0.0.1",
4
+ "description": "ESP-IDF WiFi Provisioning SDK for web browsers",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.esm.js",
7
+ "types": "dist/index.d.ts",
8
+ "keywords": [
9
+ "esp32",
10
+ "esp-idf",
11
+ "provisioning",
12
+ "wifi",
13
+ "ble",
14
+ "bluetooth",
15
+ "web-bluetooth",
16
+ "softap",
17
+ "iot"
18
+ ],
19
+ "author": "",
20
+ "license": "MIT",
21
+ "dependencies": {
22
+ "protobufjs": "^7.0.0"
23
+ },
24
+ "devDependencies": {
25
+ "@types/jest": "^29.5.0",
26
+ "@types/web-bluetooth": "0.0.21",
27
+ "@typescript-eslint/eslint-plugin": "8.46.2",
28
+ "@typescript-eslint/parser": "8.46.2",
29
+ "eslint": "9.38.0",
30
+ "jest": "30.2.0",
31
+ "prettier": "^3.0.0",
32
+ "protobufjs-cli": "^1.0.0",
33
+ "ts-jest": "^29.0.0",
34
+ "typescript": "^5.0.0",
35
+ "vite": "7.1.11"
36
+ },
37
+ "files": [
38
+ "dist",
39
+ "src",
40
+ "README.md",
41
+ "LICENSE"
42
+ ],
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/ho-229/esp-idf-provisioning-web.git"
46
+ },
47
+ "bugs": {
48
+ "url": "https://github.com/ho-229/esp-idf-provisioning-web/issues"
49
+ },
50
+ "scripts": {
51
+ "prebuild": "pbjs -t static-module -w es6 -o src/generated/proto.js src/proto/*.proto && pbts -o src/generated/proto.d.ts src/generated/proto.js",
52
+ "build": "vite build && tsc --emitDeclarationOnly",
53
+ "dev": "vite build --watch",
54
+ "example": "npx http-server -p 8080 -o /examples/ble-provisioning.html",
55
+ "serve": "npx http-server -p 8080",
56
+ "test": "jest",
57
+ "lint": "eslint src --ext .ts",
58
+ "format": "prettier --write \"src/**/*.ts\""
59
+ }
60
+ }
@@ -0,0 +1,231 @@
1
+ import {
2
+ ESPDeviceInterface,
3
+ ESPWifiAp,
4
+ ESPWifiStatus,
5
+ ESPSecurityConfig,
6
+ } from './types';
7
+ import { Transport } from './transport/Transport';
8
+ import { BLETransport } from './transport/BLETransport';
9
+ import { SoftAPTransport } from './transport/SoftAPTransport';
10
+ import { Security } from './security/Security';
11
+ import { Security0 } from './security/Security0';
12
+ import { FALLBACK_NU_LOOKUP, FALLBACK_SERVICE_UUID, PROTO_ENDPOINTS } from './constants';
13
+ import {
14
+ getScanResultRequest,
15
+ getScanStartRequest,
16
+ getScanStatusRequest,
17
+ processScanResultResponse,
18
+ processScanStartResponse,
19
+ processScanStatusResponse,
20
+ } from './protocol/wifiScan';
21
+ import {
22
+ getConfigApplyRequest,
23
+ getConfigStatusRequest,
24
+ getConfigUpdateRequest,
25
+ processConfigApplyResponse,
26
+ processConfigStatusResponse,
27
+ processConfigUpdateResponse,
28
+ } from './protocol/wifiConfig';
29
+
30
+ /**
31
+ * ESP Device class for provisioning operations
32
+ */
33
+ export class ESPDevice implements ESPDeviceInterface {
34
+ private device: BluetoothDevice | URL;
35
+ private transportImpl?: Transport;
36
+ private securityImpl?: Security;
37
+
38
+ /**
39
+ * Create a new ESPDevice instance
40
+ * @param device - BluetoothDevice for BLE or URL for SoftAP
41
+ */
42
+ constructor(device: BluetoothDevice | URL) {
43
+ this.device = device;
44
+ }
45
+
46
+ /**
47
+ * Connect to the device
48
+ * @param proofOfPossession - Proof of possession for secure modes
49
+ * @param softAPPassword - Password for SoftAP mode (not used in browser)
50
+ * @param username - Username for authentication
51
+ */
52
+ async connect(security?: ESPSecurityConfig): Promise<void> {
53
+ // Initialize transport
54
+ await this.initializeTransport();
55
+
56
+ // Initialize security
57
+ this.initializeSecurity(security);
58
+
59
+ // Establish secure session if needed
60
+ if (security) {
61
+ await this.establishSession();
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Scan for available WiFi networks
67
+ * @returns List of WiFi networks
68
+ */
69
+ async scanWifiList(): Promise<ESPWifiAp[]> {
70
+ if (!this.transportImpl || !this.securityImpl) {
71
+ throw new Error('Device not connected');
72
+ }
73
+
74
+ try {
75
+ const scanRequest = getScanStartRequest();
76
+ const scanResponse = await this.sendData(PROTO_ENDPOINTS.PROV_SCAN, scanRequest);
77
+ processScanStartResponse(scanResponse);
78
+
79
+ const scanStatusRequest = getScanStatusRequest();
80
+ const scanStatusResponse = await this.sendData(PROTO_ENDPOINTS.PROV_SCAN, scanStatusRequest);
81
+ const { scanFinished, count } = processScanStatusResponse(scanStatusResponse);
82
+ if (!scanFinished) {
83
+ throw new Error('WiFi scan should have finished');
84
+ }
85
+
86
+ let wifiList: ESPWifiAp[] = [];
87
+ for (let i = 0; i < count; i++) {
88
+ const scanResultRequest = getScanResultRequest(i, 1);
89
+ const scanResultResponse = await this.sendData(
90
+ PROTO_ENDPOINTS.PROV_SCAN,
91
+ scanResultRequest
92
+ );
93
+ const list = processScanResultResponse(scanResultResponse);
94
+ wifiList = wifiList.concat(list);
95
+ }
96
+ return wifiList;
97
+ } catch (error) {
98
+ throw new Error(`WiFi scan failed: ${error}`);
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Provision device with WiFi credentials
104
+ * @note Call fetchWifiStatus() after provisioning to check provisioning status
105
+ * @param ssid - WiFi network SSID
106
+ * @param passphrase - WiFi network password
107
+ */
108
+ async provision(ssid: string, passphrase: string): Promise<void> {
109
+ if (!this.transportImpl || !this.securityImpl) {
110
+ throw new Error('Device not connected');
111
+ }
112
+
113
+ try {
114
+ const configRequest = getConfigUpdateRequest(ssid, passphrase);
115
+ const configResponse = await this.sendData(PROTO_ENDPOINTS.PROV_CONFIG, configRequest);
116
+ processConfigUpdateResponse(configResponse);
117
+
118
+ const applyRequest = getConfigApplyRequest();
119
+ const applyResponse = await this.sendData(PROTO_ENDPOINTS.PROV_CONFIG, applyRequest);
120
+ processConfigApplyResponse(applyResponse);
121
+ } catch (error) {
122
+ throw new Error(`Provisioning failed: ${error}`);
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Fetch WiFi status from device
128
+ * @returns WiFi status
129
+ */
130
+ async fetchWifiStatus(): Promise<ESPWifiStatus> {
131
+ if (!this.transportImpl || !this.securityImpl) {
132
+ throw new Error('Device not connected');
133
+ }
134
+
135
+ try {
136
+ const statusRequest = getConfigStatusRequest();
137
+ const statusResponse = await this.sendData(PROTO_ENDPOINTS.PROV_CONFIG, statusRequest);
138
+ return processConfigStatusResponse(statusResponse);
139
+ } catch (error) {
140
+ throw new Error(`Fetching WiFi status failed: ${error}`);
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Disconnect from device
146
+ */
147
+ disconnect(): void {
148
+ if (this.transportImpl) {
149
+ this.transportImpl.disconnect();
150
+ this.transportImpl = undefined;
151
+ }
152
+ this.securityImpl = undefined;
153
+ }
154
+
155
+ /**
156
+ * Get device
157
+ */
158
+ getDevice(): BluetoothDevice | URL {
159
+ return this.device;
160
+ }
161
+
162
+ /**
163
+ * Initialize security layer based on security type
164
+ */
165
+ private initializeSecurity(security?: ESPSecurityConfig): void {
166
+ if (!security) {
167
+ this.securityImpl = new Security0();
168
+ } else {
169
+ throw new Error('Unsupported security configuration');
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Establish secure session with device
175
+ */
176
+ private async establishSession(): Promise<void> {
177
+ if (!this.transportImpl || !this.securityImpl) {
178
+ throw new Error('Transport or security not initialized');
179
+ }
180
+
181
+ // Get session setup request
182
+ const setupRequest = await this.securityImpl.getSessionSetupRequest();
183
+
184
+ // Send session setup request
185
+ const setupResponse = await this.transportImpl.sendData(
186
+ PROTO_ENDPOINTS.PROV_SESSION,
187
+ setupRequest
188
+ );
189
+
190
+ // Process session setup response
191
+ await this.securityImpl.processSessionSetupResponse(setupResponse);
192
+ }
193
+
194
+ /**
195
+ * Initialize transport layer based on transport type
196
+ */
197
+ private async initializeTransport(): Promise<void> {
198
+ if (this.device instanceof URL) {
199
+ this.transportImpl = new SoftAPTransport(this.device.toString());
200
+ } else { // BluetoothDevice
201
+ this.transportImpl = new BLETransport(
202
+ this.device,
203
+ FALLBACK_SERVICE_UUID,
204
+ new Map(Object.entries(FALLBACK_NU_LOOKUP))
205
+ );
206
+ }
207
+ await this.transportImpl.connect();
208
+ }
209
+
210
+ /**
211
+ * Send custom data to device
212
+ * @param path - Data endpoint path
213
+ * @param data - Data to send
214
+ * @returns Response data from device
215
+ */
216
+ private async sendData(path: string, data: Uint8Array): Promise<Uint8Array> {
217
+ if (!this.transportImpl || !this.securityImpl) {
218
+ throw new Error('Device not connected');
219
+ }
220
+
221
+ // Encrypt data if security is established
222
+ const encryptedData = await this.securityImpl.encrypt(data);
223
+ console.debug('Send encrypted data length:', encryptedData.length);
224
+
225
+ // Send encrypted data
226
+ const encryptedResponse = await this.transportImpl.sendData(path, encryptedData);
227
+
228
+ // Decrypt response
229
+ return await this.securityImpl.decrypt(encryptedResponse);
230
+ }
231
+ }
@@ -0,0 +1,78 @@
1
+ import { ESPDevice } from './ESPDevice';
2
+ import { DEFAULT_BLE_DEVICE_OPTIONS } from './constants';
3
+
4
+ /**
5
+ * ESP Provision Manager for device discovery
6
+ */
7
+ export class ESPProvisionManager {
8
+ /**
9
+ * Search for ESP devices using BLE
10
+ * @param devicePrefix - Prefix to filter device names
11
+ * @param transport - Transport type (only BLE supported for search)
12
+ * @param security - Security type to use
13
+ * @returns Promise resolving to array of discovered devices
14
+ */
15
+ static async searchBLEDevice(
16
+ bleDeviceOptions: RequestDeviceOptions = DEFAULT_BLE_DEVICE_OPTIONS,
17
+ ): Promise<ESPDevice | null> {
18
+ // Check if Web Bluetooth is available
19
+ const availability = await ESPProvisionManager.getBluetoothAvailability();
20
+ if (!availability) {
21
+ throw new Error('Web Bluetooth is not available.');
22
+ }
23
+
24
+ try {
25
+ // Request Bluetooth device with filters
26
+ const device = await navigator.bluetooth.requestDevice(bleDeviceOptions);
27
+ if (!device.name) {
28
+ throw new Error('Device name not available');
29
+ }
30
+
31
+ // Create ESPDevice instance
32
+ return new ESPDevice(device);
33
+ } catch (error) {
34
+ if ((error as Error).name === 'NotFoundError') {
35
+ // User cancelled the device selection
36
+ return null;
37
+ }
38
+ throw new Error(`Device search failed: ${error}`);
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Create an ESPDevice instance for SoftAP mode
44
+ * Note: Browser cannot automatically connect to WiFi.
45
+ * User must manually connect to the device's access point before using this.
46
+ *
47
+ * @param baseURL - Base URL for the SoftAP
48
+ * @returns ESPDevice instance configured for SoftAP
49
+ */
50
+ static createSoftAPDevice(baseURL: URL): ESPDevice {
51
+ return new ESPDevice(baseURL);
52
+ }
53
+
54
+ /**
55
+ * Check if Web Bluetooth is supported
56
+ * @returns true if Web Bluetooth is available
57
+ */
58
+ static isBluetoothSupported(): boolean {
59
+ return typeof navigator !== 'undefined' && 'bluetooth' in navigator;
60
+ }
61
+
62
+ /**
63
+ * Get Bluetooth availability status
64
+ * @returns Promise resolving to availability status
65
+ */
66
+ static async getBluetoothAvailability(): Promise<boolean> {
67
+ if (!this.isBluetoothSupported()) {
68
+ return false;
69
+ }
70
+
71
+ try {
72
+ return await navigator.bluetooth.getAvailability();
73
+ } catch (error) {
74
+ console.error('Failed to get Bluetooth availability:', error);
75
+ return false;
76
+ }
77
+ }
78
+ }