@busy-app/busy-lib 0.0.1 → 0.0.3

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/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";var d=Object.defineProperty;var w=(e,t,n)=>t in e?d(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var a=(e,t,n)=>w(e,typeof t!="symbol"?t+"":t,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const y=require("openapi-fetch");function m(e){const t=e.split(".");if(t.length!==4)return!1;for(const n of t){if(n.length===0||n.length>1&&n[0]==="0"||!/^\d+$/.test(n))return!1;const r=Number(n);if(r<0||r>255)return!1}return!0}const g=(e,t)=>{if(typeof FormData<"u"&&e instanceof FormData||typeof Buffer<"u"&&typeof Buffer.isBuffer=="function"&&Buffer.isBuffer(e)||typeof File<"u"&&e instanceof File||typeof Blob<"u"&&e instanceof Blob||typeof ArrayBuffer<"u"&&e instanceof ArrayBuffer||typeof ArrayBuffer<"u"&&ArrayBuffer.isView&&ArrayBuffer.isView(e))return e;let n;return t&&(t instanceof Headers?n=t.get("Content-Type")??t.get("content-type")??void 0:typeof t=="object"&&(n=t["Content-Type"]??t["content-type"]),n==="application/x-www-form-urlencoded")?e&&typeof e=="object"&&!(e instanceof URLSearchParams)?new URLSearchParams(e).toString():String(e):JSON.stringify(e)};let o=null;function S(e){o=y({baseUrl:e,bodySerializer:g})}async function E(e){const{appId:t,fileName:n,file:r}=e;if(!o)throw new Error("API client is not initialized");const{data:i,error:s}=await o.POST("/v0/assets/upload",{params:{query:{app_id:t,file:n}},headers:{"Content-Type":"application/octet-stream"},body:r});if(s)throw s;return i}async function A(e){const{appId:t}=e;if(!o)throw new Error("API client is not initialized");const{data:n,error:r}=await o.DELETE("/v0/assets/upload",{params:{query:{app_id:t}}});if(r)throw r;return n}const B={timeout:5,x:0,y:0,display:"front"};function k(e){return{...B,...e}}async function T(e){const{appId:t,elements:n}=e;if(!o)throw new Error("API client is not initialized");const r=n.map(k),{data:i,error:s}=await o.POST("/v0/display/draw",{body:{app_id:t,elements:r}});if(s)throw s;return i}async function D(){if(!o)throw new Error("API client is not initialized");const{data:e,error:t}=await o.DELETE("/v0/display/draw");if(t)throw t;return e}async function L(e){const{appId:t,path:n}=e;if(!o)throw new Error("API client is not initialized");const{data:r,error:i}=await o.POST("/v0/audio/play",{params:{query:{app_id:t,path:n}}});if(i)throw i;return r}async function v(){if(!o)throw new Error("API client is not initialized");const{data:e,error:t}=await o.DELETE("/v0/audio/play");if(t)throw t;return e}class P{constructor(t="10.0.4.20"){a(this,"ip");if(!m(t))throw new Error(`Incorrect IPv4: ${t}`);this.ip=t,console.log(this.ip),S(`http://${this.ip}/api/`)}async uploadAsset(t){return await E(t)}async deleteAssets(t){return await A(t)}async drawDisplay(t){return await T(t)}async clearDisplay(){return await D()}async playSound(t){return await L(t)}async stopSound(){return await v()}}var l=(e=>(e[e.FRONT=0]="FRONT",e[e.BACK=1]="BACK",e))(l||{});const C=3e3,I=new Set([1001,1006,1012,1013,1014,3008]);function h(e,t){if(t<0||t>=e.length)throw new Error(`Index ${t} is out of bounds (0…${e.length-1})`);const n=e[t];if(n===void 0)throw new Error(`Unexpected undefined at index ${t}`);return n}function O(e,t){let n=0;const r=e.length,i=[];for(;n<r;){const s=h(e,n);if(n+=1,(s&128)!==0){const c=s&127;for(let f=0;f<c*t;f++)i.push(e[n+f]);n+=c*t}else{const c=s,f=e.slice(n,n+t);for(let p=0;p<c;p++)for(let u=0;u<t;u++)i.push(f[u]);n+=t}}return new Uint8Array(i)}function U(e){const t=new Uint8Array(e.length*2);let n=0,r=0;for(;n<e.length;){const i=h(e,n),s=i&15,c=i>>4&15;t[r]=s,t[r+1]=c,n+=1,r+=2}return t}class N{constructor(t){a(this,"connected",!1);a(this,"dataListeners",[]);a(this,"stopListeners",[]);a(this,"errorListeners",[]);a(this,"socket",null);a(this,"deviceScreen");if(this.config=t,!(()=>typeof window<"u"&&typeof window.document<"u"))throw new Error("not browser");this.deviceScreen=l[this.config.deviceScreen.toUpperCase()]}onData(t){this.dataListeners.push(t)}onStop(t){this.stopListeners.push(t)}onError(t){this.errorListeners.push(t)}emitData(t){for(const n of this.dataListeners)n(t)}emitStop(){for(const t of this.stopListeners)t()}emitError(t){for(const n of this.errorListeners)n(t)}async openWebsocket(){this.socket&&await this.closeWebsocket();let t="";this.config.mode==="cloud"?t=`${this.config.domain}/bars/${this.config.idDevice}/ws`:this.config.mode==="local"&&(t=`${this.config.barUrl}/api/v0/screen/ws`),this.socket=new WebSocket(t),this.socket.onopen=()=>{this.socket&&(this.config.mode==="cloud"?this.socket.send(JSON.stringify({token:this.config.token,display:this.deviceScreen})):this.config.mode==="local"&&this.socket.send(JSON.stringify({display:this.deviceScreen})),this.connected=!0)},this.socket.binaryType="arraybuffer",this.socket.onmessage=n=>{try{if(typeof n.data=="string")return;const r=new Uint8Array(n.data);if(this.config.mode==="cloud")this.emitData(r);else if(this.config.mode==="local"){let i;const s=this.deviceScreen===l.FRONT?3:2;try{const c=O(r,s);this.deviceScreen===l.BACK?i=U(c):i=c,this.emitData(i)}catch{this.emitData(r)}}}catch{this.connected=!1,this.emitStop()}},this.socket.onerror=n=>{this.connected=!1,this.emitError({code:1006,message:"WebSocket error occurred",raw:n}),this.emitStop()},this.socket.onclose=async n=>{if(this.socket=null,this.connected=!1,n.code===C||I.has(n.code)){this.emitError({code:n.code,message:n.reason,raw:n});return}this.emitStop()}}closeWebsocket(){return this.connected=!1,new Promise(t=>{this.socket?(this.socket.onclose=()=>{t()},this.socket.close(),this.socket=null):t(),this.emitStop()})}}exports.BusyBar=P;exports.ScreenStream=N;
@@ -0,0 +1,859 @@
1
+ declare interface AudioParams {
2
+ appId: paths["/v0/audio/play"]["post"]["parameters"]["query"]["app_id"];
3
+ path: paths["/v0/audio/play"]["post"]["parameters"]["query"]["path"];
4
+ }
5
+
6
+ declare interface BaseConfig {
7
+ deviceScreen: Lowercase<keyof typeof DeviceScreen>;
8
+ }
9
+
10
+ /**
11
+ * Main library class for interacting with the Busy Bar API.
12
+ *
13
+ * @class
14
+ */
15
+ export declare class BusyBar {
16
+ /**
17
+ * Device IPv4 address.
18
+ * @type {IPv4}
19
+ * @readonly
20
+ */
21
+ readonly ip: IPv4;
22
+ /**
23
+ * Creates an instance of BUSY Bar.
24
+ * Initializes the API client with the provided IPv4 address.
25
+ *
26
+ * @param {IPv4} [ip="10.0.4.20"] - The IPv4 address of the device.
27
+ * @throws {Error} If the provided IP is not a valid IPv4 address.
28
+ */
29
+ constructor(ip?: IPv4);
30
+ /**
31
+ * Uploads an asset to the device.
32
+ *
33
+ * @param {UploadParams} params - Parameters for the upload.
34
+ * @param {UploadParams['appId']} params.appId - Application ID for organizing assets.
35
+ * @param {UploadParams['fileName']} params.fileName - Filename for the uploaded asset.
36
+ * @param {UploadParams['file']} params.file - File data to upload.
37
+ * @returns {Promise<{ result: string }>} Result of the upload operation.
38
+ */
39
+ uploadAsset(params: UploadParams): Promise<{
40
+ result: string;
41
+ }>;
42
+ /**
43
+ * Deletes all assets for a specific application from the device.
44
+ *
45
+ * @param {DeleteParams} params - Parameters for the delete.
46
+ * @param {DeleteParams['appId']} params.appId - Application ID whose assets should be deleted.
47
+ * @returns {Promise<{ result: string }>} Result of the delete operation.
48
+ */
49
+ deleteAssets(params: DeleteParams): Promise<{
50
+ result: string;
51
+ }>;
52
+ /**
53
+ * Draws elements on the device display.
54
+ *
55
+ * @param {DrawParams} params - Parameters for the draw operation.
56
+ * @param {DrawParams['appId']} params.appId - Application ID for organizing display elements.
57
+ * @param {DrawParams['elements'][]} params.elements - Array of display elements (text or image).
58
+ * @returns {Promise<{ result: string }>} Result of the draw operation.
59
+ */
60
+ drawDisplay(params: DrawParams): Promise<{
61
+ result: string;
62
+ }>;
63
+ /**
64
+ * Clears the device display and stops the Canvas application if running.
65
+ *
66
+ * @returns {Promise<{ result: string }>} Result of the clear operation.
67
+ */
68
+ clearDisplay(): Promise<{
69
+ result: string;
70
+ }>;
71
+ /**
72
+ * Plays an audio file from the assets directory.
73
+ *
74
+ * @param {AudioParams} params - Parameters for the audio playback.
75
+ * @param {AudioParams['appId']} params.appId - Application ID for organizing assets.
76
+ * @param {AudioParams['path']} params.path - Path to the audio file within the app's assets directory.
77
+ * @returns {Promise<{ result: string }>} Result of the play operation.
78
+ */
79
+ playSound(params: AudioParams): Promise<{
80
+ result: string;
81
+ }>;
82
+ /**
83
+ * Stops any currently playing audio on the device.
84
+ *
85
+ * @returns {Promise<{ result: string }>} Result of the stop operation.
86
+ */
87
+ stopSound(): Promise<{
88
+ result: string;
89
+ }>;
90
+ }
91
+
92
+ declare interface components {
93
+ schemas: {
94
+ /** @example {
95
+ * "state": 1
96
+ * } */
97
+ LedState: {
98
+ /**
99
+ * @description Current LED state (0 for off, 1 for on)
100
+ * @enum {integer}
101
+ */
102
+ state: 0 | 1;
103
+ };
104
+ /** @example {
105
+ * "state": 1
106
+ * } */
107
+ LedStateRequest: {
108
+ /**
109
+ * @description LED state (0 for off, 1 for on)
110
+ * @enum {integer}
111
+ */
112
+ state: 0 | 1;
113
+ };
114
+ SuccessResponse: {
115
+ /**
116
+ * @description Success status message
117
+ * @example OK
118
+ */
119
+ result: string;
120
+ };
121
+ LedStateResponse: components["schemas"]["SuccessResponse"] & {
122
+ /**
123
+ * @description Current LED state (0 for off, 1 for on)
124
+ * @enum {integer}
125
+ */
126
+ state: 0 | 1;
127
+ };
128
+ /** @example {
129
+ * "branch": "main",
130
+ * "version": "1.0.0",
131
+ * "build_date": "2024-01-01",
132
+ * "commit_hash": "abc123def456"
133
+ * } */
134
+ VersionInfo: {
135
+ /**
136
+ * @description Git branch name
137
+ * @example main
138
+ */
139
+ branch: string;
140
+ /**
141
+ * @description Firmware version
142
+ * @example 1.0.0
143
+ */
144
+ version: string;
145
+ /**
146
+ * @description Build date
147
+ * @example 2024-01-01
148
+ */
149
+ build_date: string;
150
+ /**
151
+ * @description Git commit hash (may include -dirty suffix)
152
+ * @example abc123def456-dirty
153
+ */
154
+ commit_hash: string;
155
+ };
156
+ /**
157
+ * Format: binary
158
+ * @description Binary data upload
159
+ */
160
+ BinaryUpload: string;
161
+ FileUpload: {
162
+ /**
163
+ * Format: binary
164
+ * @description File to upload
165
+ */
166
+ file: string;
167
+ };
168
+ /** @example {
169
+ * "error": "Invalid parameter",
170
+ * "code": 400
171
+ * } */
172
+ Error: {
173
+ /** @description Error message */
174
+ error: string;
175
+ /** @description Error code */
176
+ code?: number;
177
+ };
178
+ /** @example {
179
+ * "app_id": "my_app",
180
+ * "elements": [
181
+ * {
182
+ * "id": "0",
183
+ * "timeout": 5,
184
+ * "type": "text",
185
+ * "text": "Hello, world!",
186
+ * "x": 0,
187
+ * "y": 0,
188
+ * "display": "front"
189
+ * },
190
+ * {
191
+ * "id": "1",
192
+ * "timeout": 6,
193
+ * "type": "image",
194
+ * "path": "data.png",
195
+ * "x": 0,
196
+ * "y": 0,
197
+ * "display": "back"
198
+ * }
199
+ * ]
200
+ * } */
201
+ DisplayElements: {
202
+ /**
203
+ * @description Application ID for organizing assets
204
+ * @example my_app
205
+ */
206
+ app_id: string;
207
+ /** @description Array of elements to display */
208
+ elements: components["schemas"]["DisplayElement"][];
209
+ };
210
+ DisplayElement: {
211
+ /** @description Unique identifier for the element */
212
+ id: string;
213
+ /** @description Time in seconds the element should be displayed (0 for no timeout) */
214
+ timeout?: number;
215
+ /**
216
+ * @description Type of display element
217
+ * @enum {string}
218
+ */
219
+ type: "text" | "image";
220
+ /** @description X coordinate for placement on display */
221
+ x: number;
222
+ /** @description Y coordinate for placement on display */
223
+ y: number;
224
+ /**
225
+ * @description Which display to show the element on (for dual-display devices)
226
+ * @default front
227
+ * @enum {string}
228
+ */
229
+ display: "front" | "back";
230
+ };
231
+ TextElement: Omit<components["schemas"]["DisplayElement"], "type"> & {
232
+ /** @description Text content to display */
233
+ text: string;
234
+ } & {
235
+ /**
236
+ * @description discriminator enum property added by openapi-typescript
237
+ * @enum {string}
238
+ */
239
+ type: "text";
240
+ };
241
+ ImageElement: Omit<components["schemas"]["DisplayElement"], "type"> & {
242
+ /** @description Path to the image file in the app's assets */
243
+ path: string;
244
+ } & {
245
+ /**
246
+ * @description discriminator enum property added by openapi-typescript
247
+ * @enum {string}
248
+ */
249
+ type: "image";
250
+ };
251
+ };
252
+ responses: never;
253
+ parameters: never;
254
+ requestBodies: never;
255
+ headers: never;
256
+ pathItems: never;
257
+ }
258
+
259
+ declare type CustomElement = CustomTextElement | CustomImageElement;
260
+
261
+ declare type CustomImageElement = MakeOptional<components["schemas"]["ImageElement"], OptionalFields>;
262
+
263
+ declare type CustomTextElement = MakeOptional<components["schemas"]["TextElement"], OptionalFields>;
264
+
265
+ declare type DataListener = (data: Uint8Array) => void;
266
+
267
+ declare interface DeleteParams {
268
+ appId: paths["/v0/assets/upload"]["delete"]["parameters"]["query"]["app_id"];
269
+ }
270
+
271
+ declare type DeviceConfig = LocalConfig | SiteConfig;
272
+
273
+ declare enum DeviceScreen {
274
+ FRONT = 0,
275
+ BACK = 1
276
+ }
277
+
278
+ declare interface DrawParams {
279
+ appId: paths["/v0/display/draw"]["post"]["requestBody"]["content"]["application/json"]["app_id"];
280
+ elements: CustomElement[];
281
+ }
282
+
283
+ declare type ErrorListener = (payload: ErrorPayload) => void;
284
+
285
+ declare interface ErrorPayload {
286
+ code: number;
287
+ message: string;
288
+ raw: Error | CloseEvent | Event;
289
+ }
290
+
291
+ declare type IPv4 = string;
292
+
293
+ declare interface LocalConfig extends BaseConfig {
294
+ mode: "local";
295
+ barUrl: string;
296
+ }
297
+
298
+ declare type MakeOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
299
+
300
+ declare interface operations {
301
+ getLedState: {
302
+ parameters: {
303
+ query?: {
304
+ /** @description LED state (0 for off, 1 for on) - alternative to JSON body */
305
+ state?: 0 | 1;
306
+ };
307
+ header?: never;
308
+ path?: never;
309
+ cookie?: never;
310
+ };
311
+ requestBody?: never;
312
+ responses: {
313
+ /** @description LED state retrieved successfully */
314
+ 200: {
315
+ headers: {
316
+ [name: string]: unknown;
317
+ };
318
+ content: {
319
+ "application/json": components["schemas"]["LedStateResponse"];
320
+ };
321
+ };
322
+ /** @description Internal server error */
323
+ 500: {
324
+ headers: {
325
+ [name: string]: unknown;
326
+ };
327
+ content: {
328
+ "application/json": components["schemas"]["Error"];
329
+ };
330
+ };
331
+ };
332
+ };
333
+ setLedState: {
334
+ parameters: {
335
+ query?: {
336
+ /** @description LED state (0 for off, 1 for on) - alternative to JSON body */
337
+ state?: 0 | 1;
338
+ };
339
+ header?: never;
340
+ path?: never;
341
+ cookie?: never;
342
+ };
343
+ /** @description LED state as JSON (alternative to query parameter) */
344
+ requestBody?: {
345
+ content: {
346
+ "application/json": components["schemas"]["LedStateRequest"];
347
+ };
348
+ };
349
+ responses: {
350
+ /** @description LED state set successfully */
351
+ 200: {
352
+ headers: {
353
+ [name: string]: unknown;
354
+ };
355
+ content: {
356
+ "application/json": components["schemas"]["LedStateResponse"];
357
+ };
358
+ };
359
+ /** @description Invalid state parameter */
360
+ 400: {
361
+ headers: {
362
+ [name: string]: unknown;
363
+ };
364
+ content: {
365
+ "application/json": components["schemas"]["Error"];
366
+ };
367
+ };
368
+ };
369
+ };
370
+ getVersion: {
371
+ parameters: {
372
+ query?: never;
373
+ header?: never;
374
+ path?: never;
375
+ cookie?: never;
376
+ };
377
+ requestBody?: never;
378
+ responses: {
379
+ /** @description Version information retrieved successfully */
380
+ 200: {
381
+ headers: {
382
+ [name: string]: unknown;
383
+ };
384
+ content: {
385
+ "application/json": components["schemas"]["VersionInfo"];
386
+ };
387
+ };
388
+ /** @description Internal server error */
389
+ 500: {
390
+ headers: {
391
+ [name: string]: unknown;
392
+ };
393
+ content: {
394
+ "application/json": components["schemas"]["Error"];
395
+ };
396
+ };
397
+ };
398
+ };
399
+ uploadAssetWithAppId: {
400
+ parameters: {
401
+ query: {
402
+ /**
403
+ * @description Application ID for organizing assets
404
+ * @example my_app
405
+ */
406
+ app_id: string;
407
+ /**
408
+ * @description Filename for the uploaded asset
409
+ * @example data.png
410
+ */
411
+ file: string;
412
+ };
413
+ header?: never;
414
+ path?: never;
415
+ cookie?: never;
416
+ };
417
+ requestBody: {
418
+ content: {
419
+ "application/octet-stream": string;
420
+ };
421
+ };
422
+ responses: {
423
+ /** @description File uploaded successfully */
424
+ 200: {
425
+ headers: {
426
+ [name: string]: unknown;
427
+ };
428
+ content: {
429
+ "application/json": components["schemas"]["SuccessResponse"];
430
+ };
431
+ };
432
+ /** @description Invalid parameters or upload failed */
433
+ 400: {
434
+ headers: {
435
+ [name: string]: unknown;
436
+ };
437
+ content: {
438
+ "application/json": components["schemas"]["Error"];
439
+ };
440
+ };
441
+ /** @description File too large */
442
+ 413: {
443
+ headers: {
444
+ [name: string]: unknown;
445
+ };
446
+ content: {
447
+ "application/json": components["schemas"]["Error"];
448
+ };
449
+ };
450
+ };
451
+ };
452
+ deleteAppAssets: {
453
+ parameters: {
454
+ query: {
455
+ /**
456
+ * @description Application ID whose assets should be deleted
457
+ * @example my_app
458
+ */
459
+ app_id: string;
460
+ };
461
+ header?: never;
462
+ path?: never;
463
+ cookie?: never;
464
+ };
465
+ requestBody?: never;
466
+ responses: {
467
+ /** @description Assets deleted successfully */
468
+ 200: {
469
+ headers: {
470
+ [name: string]: unknown;
471
+ };
472
+ content: {
473
+ "application/json": components["schemas"]["SuccessResponse"];
474
+ };
475
+ };
476
+ /** @description Invalid app_id or deletion failed */
477
+ 400: {
478
+ headers: {
479
+ [name: string]: unknown;
480
+ };
481
+ content: {
482
+ "application/json": components["schemas"]["Error"];
483
+ };
484
+ };
485
+ };
486
+ };
487
+ drawOnDisplay: {
488
+ parameters: {
489
+ query?: never;
490
+ header?: never;
491
+ path?: never;
492
+ cookie?: never;
493
+ };
494
+ requestBody: {
495
+ content: {
496
+ "application/json": components["schemas"]["DisplayElements"];
497
+ };
498
+ };
499
+ responses: {
500
+ /** @description Drawing command executed successfully */
501
+ 200: {
502
+ headers: {
503
+ [name: string]: unknown;
504
+ };
505
+ content: {
506
+ "application/json": components["schemas"]["SuccessResponse"];
507
+ };
508
+ };
509
+ /** @description Invalid drawing data */
510
+ 400: {
511
+ headers: {
512
+ [name: string]: unknown;
513
+ };
514
+ content: {
515
+ "application/json": components["schemas"]["Error"];
516
+ };
517
+ };
518
+ /** @description Display error */
519
+ 500: {
520
+ headers: {
521
+ [name: string]: unknown;
522
+ };
523
+ content: {
524
+ "application/json": components["schemas"]["Error"];
525
+ };
526
+ };
527
+ };
528
+ };
529
+ clearDisplay: {
530
+ parameters: {
531
+ query?: never;
532
+ header?: never;
533
+ path?: never;
534
+ cookie?: never;
535
+ };
536
+ requestBody?: never;
537
+ responses: {
538
+ /** @description Display cleared successfully */
539
+ 200: {
540
+ headers: {
541
+ [name: string]: unknown;
542
+ };
543
+ content: {
544
+ "application/json": components["schemas"]["SuccessResponse"];
545
+ };
546
+ };
547
+ /** @description Display error */
548
+ 500: {
549
+ headers: {
550
+ [name: string]: unknown;
551
+ };
552
+ content: {
553
+ "application/json": components["schemas"]["Error"];
554
+ };
555
+ };
556
+ };
557
+ };
558
+ playAudio: {
559
+ parameters: {
560
+ query: {
561
+ /**
562
+ * @description Application ID for organizing assets
563
+ * @example my_app
564
+ */
565
+ app_id: string;
566
+ /**
567
+ * @description Path to audio file within app's assets directory
568
+ * @example data.snd
569
+ */
570
+ path: string;
571
+ };
572
+ header?: never;
573
+ path?: never;
574
+ cookie?: never;
575
+ };
576
+ requestBody?: never;
577
+ responses: {
578
+ /** @description Audio playback started successfully */
579
+ 200: {
580
+ headers: {
581
+ [name: string]: unknown;
582
+ };
583
+ content: {
584
+ "application/json": components["schemas"]["SuccessResponse"];
585
+ };
586
+ };
587
+ /** @description Invalid file path or file not found */
588
+ 400: {
589
+ headers: {
590
+ [name: string]: unknown;
591
+ };
592
+ content: {
593
+ "application/json": components["schemas"]["Error"];
594
+ };
595
+ };
596
+ /** @description Audio system error */
597
+ 500: {
598
+ headers: {
599
+ [name: string]: unknown;
600
+ };
601
+ content: {
602
+ "application/json": components["schemas"]["Error"];
603
+ };
604
+ };
605
+ };
606
+ };
607
+ stopAudio: {
608
+ parameters: {
609
+ query?: never;
610
+ header?: never;
611
+ path?: never;
612
+ cookie?: never;
613
+ };
614
+ requestBody?: never;
615
+ responses: {
616
+ /** @description Audio playback stopped successfully */
617
+ 200: {
618
+ headers: {
619
+ [name: string]: unknown;
620
+ };
621
+ content: {
622
+ "application/json": components["schemas"]["SuccessResponse"];
623
+ };
624
+ };
625
+ /** @description Audio system error */
626
+ 500: {
627
+ headers: {
628
+ [name: string]: unknown;
629
+ };
630
+ content: {
631
+ "application/json": components["schemas"]["Error"];
632
+ };
633
+ };
634
+ };
635
+ };
636
+ connectWebSocket: {
637
+ parameters: {
638
+ query?: never;
639
+ header?: never;
640
+ path?: never;
641
+ cookie?: never;
642
+ };
643
+ requestBody?: never;
644
+ responses: {
645
+ /** @description WebSocket connection established */
646
+ 101: {
647
+ headers: {
648
+ [name: string]: unknown;
649
+ };
650
+ content?: never;
651
+ };
652
+ /** @description WebSocket upgrade failed */
653
+ 400: {
654
+ headers: {
655
+ [name: string]: unknown;
656
+ };
657
+ content: {
658
+ "application/json": components["schemas"]["Error"];
659
+ };
660
+ };
661
+ /** @description Upgrade required */
662
+ 426: {
663
+ headers: {
664
+ [name: string]: unknown;
665
+ };
666
+ content: {
667
+ "application/json": components["schemas"]["Error"];
668
+ };
669
+ };
670
+ };
671
+ };
672
+ }
673
+
674
+ declare type OptionalFields = "timeout" | "x" | "y" | "display";
675
+
676
+ /**
677
+ * This file was auto-generated by openapi-typescript.
678
+ * Do not make direct changes to the file.
679
+ */
680
+ declare interface paths {
681
+ "/v0/led": {
682
+ parameters: {
683
+ query?: never;
684
+ header?: never;
685
+ path?: never;
686
+ cookie?: never;
687
+ };
688
+ /**
689
+ * Get LED state
690
+ * @description Retrieves the current LED state
691
+ */
692
+ get: operations["getLedState"];
693
+ put?: never;
694
+ /**
695
+ * Set LED state
696
+ * @description Sets the LED state via query parameter or JSON body
697
+ */
698
+ post: operations["setLedState"];
699
+ delete?: never;
700
+ options?: never;
701
+ head?: never;
702
+ patch?: never;
703
+ trace?: never;
704
+ };
705
+ "/v0/version": {
706
+ parameters: {
707
+ query?: never;
708
+ header?: never;
709
+ path?: never;
710
+ cookie?: never;
711
+ };
712
+ /**
713
+ * Get firmware version information
714
+ * @description Retrieves firmware version, branch, build date, and commit information
715
+ */
716
+ get: operations["getVersion"];
717
+ put?: never;
718
+ post?: never;
719
+ delete?: never;
720
+ options?: never;
721
+ head?: never;
722
+ patch?: never;
723
+ trace?: never;
724
+ };
725
+ "/v0/assets/upload": {
726
+ parameters: {
727
+ query?: never;
728
+ header?: never;
729
+ path?: never;
730
+ cookie?: never;
731
+ };
732
+ get?: never;
733
+ put?: never;
734
+ /**
735
+ * Upload asset file with app ID
736
+ * @description Uploads a file to a specific app's assets directory
737
+ */
738
+ post: operations["uploadAssetWithAppId"];
739
+ /**
740
+ * Delete app assets
741
+ * @description Deletes all assets for a specific app ID
742
+ */
743
+ delete: operations["deleteAppAssets"];
744
+ options?: never;
745
+ head?: never;
746
+ patch?: never;
747
+ trace?: never;
748
+ };
749
+ "/v0/display/draw": {
750
+ parameters: {
751
+ query?: never;
752
+ header?: never;
753
+ path?: never;
754
+ cookie?: never;
755
+ };
756
+ get?: never;
757
+ put?: never;
758
+ /**
759
+ * Draw on display
760
+ * @description Sends drawing data to the display.
761
+ * Supports JSON-defined display elements.
762
+ *
763
+ */
764
+ post: operations["drawOnDisplay"];
765
+ /**
766
+ * Clear display
767
+ * @description Clears the display and stops the Canvas application if running
768
+ */
769
+ delete: operations["clearDisplay"];
770
+ options?: never;
771
+ head?: never;
772
+ patch?: never;
773
+ trace?: never;
774
+ };
775
+ "/v0/audio/play": {
776
+ parameters: {
777
+ query?: never;
778
+ header?: never;
779
+ path?: never;
780
+ cookie?: never;
781
+ };
782
+ get?: never;
783
+ put?: never;
784
+ /**
785
+ * Play audio file
786
+ * @description Plays an audio file from the assets directory.
787
+ * Supported formats include .snd files.
788
+ *
789
+ */
790
+ post: operations["playAudio"];
791
+ /**
792
+ * Stop audio playback
793
+ * @description Stops any currently playing audio
794
+ */
795
+ delete: operations["stopAudio"];
796
+ options?: never;
797
+ head?: never;
798
+ patch?: never;
799
+ trace?: never;
800
+ };
801
+ "/ws_test": {
802
+ parameters: {
803
+ query?: never;
804
+ header?: never;
805
+ path?: never;
806
+ cookie?: never;
807
+ };
808
+ /**
809
+ * WebSocket test endpoint
810
+ * @description WebSocket connection for real-time communication and testing.
811
+ * Upgrade from HTTP to WebSocket protocol is required.
812
+ *
813
+ */
814
+ get: operations["connectWebSocket"];
815
+ put?: never;
816
+ post?: never;
817
+ delete?: never;
818
+ options?: never;
819
+ head?: never;
820
+ patch?: never;
821
+ trace?: never;
822
+ };
823
+ }
824
+
825
+ export declare class ScreenStream {
826
+ private config;
827
+ connected: boolean;
828
+ private dataListeners;
829
+ private stopListeners;
830
+ private errorListeners;
831
+ private socket;
832
+ private deviceScreen;
833
+ constructor(config: DeviceConfig);
834
+ onData(listener: DataListener): void;
835
+ onStop(listener: StopListener): void;
836
+ onError(listener: ErrorListener): void;
837
+ protected emitData(data: Uint8Array): void;
838
+ protected emitStop(): void;
839
+ protected emitError(payload: ErrorPayload): void;
840
+ openWebsocket(): Promise<void>;
841
+ closeWebsocket(): Promise<void>;
842
+ }
843
+
844
+ declare interface SiteConfig extends BaseConfig {
845
+ mode: "cloud";
846
+ domain: string;
847
+ token: string;
848
+ idDevice: string;
849
+ }
850
+
851
+ declare type StopListener = () => void;
852
+
853
+ declare interface UploadParams {
854
+ appId: paths["/v0/assets/upload"]["post"]["parameters"]["query"]["app_id"];
855
+ fileName: paths["/v0/assets/upload"]["post"]["parameters"]["query"]["file"];
856
+ file: Buffer | Blob | File | ArrayBuffer;
857
+ }
858
+
859
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,328 @@
1
+ var d = Object.defineProperty;
2
+ var w = (e, t, n) => t in e ? d(e, t, { enumerable: !0, configurable: !0, writable: !0, value: n }) : e[t] = n;
3
+ var a = (e, t, n) => w(e, typeof t != "symbol" ? t + "" : t, n);
4
+ import y from "openapi-fetch";
5
+ function m(e) {
6
+ const t = e.split(".");
7
+ if (t.length !== 4)
8
+ return !1;
9
+ for (const n of t) {
10
+ if (n.length === 0 || n.length > 1 && n[0] === "0" || !/^\d+$/.test(n))
11
+ return !1;
12
+ const r = Number(n);
13
+ if (r < 0 || r > 255)
14
+ return !1;
15
+ }
16
+ return !0;
17
+ }
18
+ const g = (e, t) => {
19
+ if (typeof FormData < "u" && e instanceof FormData || typeof Buffer < "u" && typeof Buffer.isBuffer == "function" && Buffer.isBuffer(e) || typeof File < "u" && e instanceof File || typeof Blob < "u" && e instanceof Blob || typeof ArrayBuffer < "u" && e instanceof ArrayBuffer || typeof ArrayBuffer < "u" && ArrayBuffer.isView && ArrayBuffer.isView(e))
20
+ return e;
21
+ let n;
22
+ return t && (t instanceof Headers ? n = t.get("Content-Type") ?? t.get("content-type") ?? void 0 : typeof t == "object" && (n = t["Content-Type"] ?? t["content-type"]), n === "application/x-www-form-urlencoded") ? e && typeof e == "object" && !(e instanceof URLSearchParams) ? new URLSearchParams(e).toString() : String(e) : JSON.stringify(e);
23
+ };
24
+ let o = null;
25
+ function E(e) {
26
+ o = y({
27
+ baseUrl: e,
28
+ bodySerializer: g
29
+ });
30
+ }
31
+ async function S(e) {
32
+ const { appId: t, fileName: n, file: r } = e;
33
+ if (!o)
34
+ throw new Error("API client is not initialized");
35
+ const { data: i, error: s } = await o.POST("/v0/assets/upload", {
36
+ params: {
37
+ query: {
38
+ app_id: t,
39
+ file: n
40
+ }
41
+ },
42
+ headers: {
43
+ "Content-Type": "application/octet-stream"
44
+ },
45
+ body: r
46
+ });
47
+ if (s)
48
+ throw s;
49
+ return i;
50
+ }
51
+ async function A(e) {
52
+ const { appId: t } = e;
53
+ if (!o)
54
+ throw new Error("API client is not initialized");
55
+ const { data: n, error: r } = await o.DELETE("/v0/assets/upload", {
56
+ params: {
57
+ query: {
58
+ app_id: t
59
+ }
60
+ }
61
+ });
62
+ if (r)
63
+ throw r;
64
+ return n;
65
+ }
66
+ const B = { timeout: 5, x: 0, y: 0, display: "front" };
67
+ function k(e) {
68
+ return { ...B, ...e };
69
+ }
70
+ async function D(e) {
71
+ const { appId: t, elements: n } = e;
72
+ if (!o)
73
+ throw new Error("API client is not initialized");
74
+ const r = n.map(k), { data: i, error: s } = await o.POST("/v0/display/draw", {
75
+ body: {
76
+ app_id: t,
77
+ elements: r
78
+ }
79
+ });
80
+ if (s)
81
+ throw s;
82
+ return i;
83
+ }
84
+ async function L() {
85
+ if (!o)
86
+ throw new Error("API client is not initialized");
87
+ const { data: e, error: t } = await o.DELETE("/v0/display/draw");
88
+ if (t)
89
+ throw t;
90
+ return e;
91
+ }
92
+ async function T(e) {
93
+ const { appId: t, path: n } = e;
94
+ if (!o)
95
+ throw new Error("API client is not initialized");
96
+ const { data: r, error: i } = await o.POST("/v0/audio/play", {
97
+ params: {
98
+ query: {
99
+ app_id: t,
100
+ path: n
101
+ }
102
+ }
103
+ });
104
+ if (i)
105
+ throw i;
106
+ return r;
107
+ }
108
+ async function v() {
109
+ if (!o)
110
+ throw new Error("API client is not initialized");
111
+ const { data: e, error: t } = await o.DELETE("/v0/audio/play");
112
+ if (t)
113
+ throw t;
114
+ return e;
115
+ }
116
+ class $ {
117
+ /**
118
+ * Creates an instance of BUSY Bar.
119
+ * Initializes the API client with the provided IPv4 address.
120
+ *
121
+ * @param {IPv4} [ip="10.0.4.20"] - The IPv4 address of the device.
122
+ * @throws {Error} If the provided IP is not a valid IPv4 address.
123
+ */
124
+ constructor(t = "10.0.4.20") {
125
+ /**
126
+ * Device IPv4 address.
127
+ * @type {IPv4}
128
+ * @readonly
129
+ */
130
+ a(this, "ip");
131
+ if (!m(t))
132
+ throw new Error(`Incorrect IPv4: ${t}`);
133
+ this.ip = t, console.log(this.ip), E(`http://${this.ip}/api/`);
134
+ }
135
+ /**
136
+ * Uploads an asset to the device.
137
+ *
138
+ * @param {UploadParams} params - Parameters for the upload.
139
+ * @param {UploadParams['appId']} params.appId - Application ID for organizing assets.
140
+ * @param {UploadParams['fileName']} params.fileName - Filename for the uploaded asset.
141
+ * @param {UploadParams['file']} params.file - File data to upload.
142
+ * @returns {Promise<{ result: string }>} Result of the upload operation.
143
+ */
144
+ async uploadAsset(t) {
145
+ return await S(t);
146
+ }
147
+ /**
148
+ * Deletes all assets for a specific application from the device.
149
+ *
150
+ * @param {DeleteParams} params - Parameters for the delete.
151
+ * @param {DeleteParams['appId']} params.appId - Application ID whose assets should be deleted.
152
+ * @returns {Promise<{ result: string }>} Result of the delete operation.
153
+ */
154
+ async deleteAssets(t) {
155
+ return await A(t);
156
+ }
157
+ /**
158
+ * Draws elements on the device display.
159
+ *
160
+ * @param {DrawParams} params - Parameters for the draw operation.
161
+ * @param {DrawParams['appId']} params.appId - Application ID for organizing display elements.
162
+ * @param {DrawParams['elements'][]} params.elements - Array of display elements (text or image).
163
+ * @returns {Promise<{ result: string }>} Result of the draw operation.
164
+ */
165
+ async drawDisplay(t) {
166
+ return await D(t);
167
+ }
168
+ /**
169
+ * Clears the device display and stops the Canvas application if running.
170
+ *
171
+ * @returns {Promise<{ result: string }>} Result of the clear operation.
172
+ */
173
+ async clearDisplay() {
174
+ return await L();
175
+ }
176
+ /**
177
+ * Plays an audio file from the assets directory.
178
+ *
179
+ * @param {AudioParams} params - Parameters for the audio playback.
180
+ * @param {AudioParams['appId']} params.appId - Application ID for organizing assets.
181
+ * @param {AudioParams['path']} params.path - Path to the audio file within the app's assets directory.
182
+ * @returns {Promise<{ result: string }>} Result of the play operation.
183
+ */
184
+ async playSound(t) {
185
+ return await T(t);
186
+ }
187
+ /**
188
+ * Stops any currently playing audio on the device.
189
+ *
190
+ * @returns {Promise<{ result: string }>} Result of the stop operation.
191
+ */
192
+ async stopSound() {
193
+ return await v();
194
+ }
195
+ }
196
+ var l = /* @__PURE__ */ ((e) => (e[e.FRONT = 0] = "FRONT", e[e.BACK = 1] = "BACK", e))(l || {});
197
+ const C = 3e3, I = /* @__PURE__ */ new Set([1001, 1006, 1012, 1013, 1014, 3008]);
198
+ function h(e, t) {
199
+ if (t < 0 || t >= e.length)
200
+ throw new Error(`Index ${t} is out of bounds (0…${e.length - 1})`);
201
+ const n = e[t];
202
+ if (n === void 0)
203
+ throw new Error(`Unexpected undefined at index ${t}`);
204
+ return n;
205
+ }
206
+ function P(e, t) {
207
+ let n = 0;
208
+ const r = e.length, i = [];
209
+ for (; n < r; ) {
210
+ const s = h(e, n);
211
+ if (n += 1, (s & 128) !== 0) {
212
+ const c = s & 127;
213
+ for (let f = 0; f < c * t; f++)
214
+ i.push(e[n + f]);
215
+ n += c * t;
216
+ } else {
217
+ const c = s, f = e.slice(n, n + t);
218
+ for (let p = 0; p < c; p++)
219
+ for (let u = 0; u < t; u++)
220
+ i.push(f[u]);
221
+ n += t;
222
+ }
223
+ }
224
+ return new Uint8Array(i);
225
+ }
226
+ function U(e) {
227
+ const t = new Uint8Array(e.length * 2);
228
+ let n = 0, r = 0;
229
+ for (; n < e.length; ) {
230
+ const i = h(e, n), s = i & 15, c = i >> 4 & 15;
231
+ t[r] = s, t[r + 1] = c, n += 1, r += 2;
232
+ }
233
+ return t;
234
+ }
235
+ class F {
236
+ constructor(t) {
237
+ a(this, "connected", !1);
238
+ a(this, "dataListeners", []);
239
+ a(this, "stopListeners", []);
240
+ a(this, "errorListeners", []);
241
+ a(this, "socket", null);
242
+ a(this, "deviceScreen");
243
+ if (this.config = t, !(() => typeof window < "u" && typeof window.document < "u"))
244
+ throw new Error("not browser");
245
+ this.deviceScreen = l[this.config.deviceScreen.toUpperCase()];
246
+ }
247
+ onData(t) {
248
+ this.dataListeners.push(t);
249
+ }
250
+ onStop(t) {
251
+ this.stopListeners.push(t);
252
+ }
253
+ onError(t) {
254
+ this.errorListeners.push(t);
255
+ }
256
+ emitData(t) {
257
+ for (const n of this.dataListeners)
258
+ n(t);
259
+ }
260
+ emitStop() {
261
+ for (const t of this.stopListeners)
262
+ t();
263
+ }
264
+ emitError(t) {
265
+ for (const n of this.errorListeners)
266
+ n(t);
267
+ }
268
+ async openWebsocket() {
269
+ this.socket && await this.closeWebsocket();
270
+ let t = "";
271
+ this.config.mode === "cloud" ? t = `${this.config.domain}/bars/${this.config.idDevice}/ws` : this.config.mode === "local" && (t = `${this.config.barUrl}/api/v0/screen/ws`), this.socket = new WebSocket(t), this.socket.onopen = () => {
272
+ this.socket && (this.config.mode === "cloud" ? this.socket.send(
273
+ JSON.stringify({
274
+ token: this.config.token,
275
+ display: this.deviceScreen
276
+ })
277
+ ) : this.config.mode === "local" && this.socket.send(JSON.stringify({ display: this.deviceScreen })), this.connected = !0);
278
+ }, this.socket.binaryType = "arraybuffer", this.socket.onmessage = (n) => {
279
+ try {
280
+ if (typeof n.data == "string")
281
+ return;
282
+ const r = new Uint8Array(n.data);
283
+ if (this.config.mode === "cloud")
284
+ this.emitData(r);
285
+ else if (this.config.mode === "local") {
286
+ let i;
287
+ const s = this.deviceScreen === l.FRONT ? 3 : 2;
288
+ try {
289
+ const c = P(r, s);
290
+ this.deviceScreen === l.BACK ? i = U(c) : i = c, this.emitData(i);
291
+ } catch {
292
+ this.emitData(r);
293
+ }
294
+ }
295
+ } catch {
296
+ this.connected = !1, this.emitStop();
297
+ }
298
+ }, this.socket.onerror = (n) => {
299
+ this.connected = !1, this.emitError({
300
+ code: 1006,
301
+ // Standard «abnormal closure» code per RFC-6455
302
+ message: "WebSocket error occurred",
303
+ raw: n
304
+ }), this.emitStop();
305
+ }, this.socket.onclose = async (n) => {
306
+ if (this.socket = null, this.connected = !1, n.code === C || I.has(n.code)) {
307
+ this.emitError({
308
+ code: n.code,
309
+ message: n.reason,
310
+ raw: n
311
+ });
312
+ return;
313
+ }
314
+ this.emitStop();
315
+ };
316
+ }
317
+ closeWebsocket() {
318
+ return this.connected = !1, new Promise((t) => {
319
+ this.socket ? (this.socket.onclose = () => {
320
+ t();
321
+ }, this.socket.close(), this.socket = null) : t(), this.emitStop();
322
+ });
323
+ }
324
+ }
325
+ export {
326
+ $ as BusyBar,
327
+ F as ScreenStream
328
+ };
package/package.json CHANGED
@@ -2,7 +2,10 @@
2
2
  "name": "@busy-app/busy-lib",
3
3
  "private": false,
4
4
  "description": "A library for interacting with the BUSY Bar API",
5
- "version": "0.0.1",
5
+ "version": "0.0.3",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
6
9
  "type": "module",
7
10
  "main": "dist/index.cjs",
8
11
  "module": "dist/index.js",
@@ -17,10 +20,6 @@
17
20
  "files": [
18
21
  "dist"
19
22
  ],
20
- "scripts": {
21
- "build": "vite build",
22
- "docs:generate": "typedoc"
23
- },
24
23
  "dependencies": {
25
24
  "openapi-fetch": "^0.14.0"
26
25
  },
@@ -34,7 +33,10 @@
34
33
  "vite-tsconfig-paths": "^5.1.4"
35
34
  },
36
35
  "engines": {
37
- "node": "22.15.1"
36
+ "node": ">=18"
38
37
  },
39
- "packageManager": "pnpm@9.12.2+sha512.22721b3a11f81661ae1ec68ce1a7b879425a1ca5b991c975b074ac220b187ce56c708fe5db69f4c962c989452eee76c82877f4ee80f474cebd61ee13461b6228"
40
- }
38
+ "scripts": {
39
+ "build": "vite build",
40
+ "docs:generate": "typedoc"
41
+ }
42
+ }