@opendisplay/opendisplay 1.0.1 → 1.1.0

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 CHANGED
@@ -3,7 +3,6 @@
3
3
  TypeScript library for OpenDisplay BLE e-paper displays using Web Bluetooth API. Control your OpenDisplay devices directly from the browser.
4
4
 
5
5
  [![npm version](https://badge.fury.io/js/@opendisplay%2Fopendisplay.svg)](https://www.npmjs.com/package/@opendisplay/opendisplay)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
6
 
8
7
  ## Features
9
8
 
@@ -12,8 +11,6 @@ TypeScript library for OpenDisplay BLE e-paper displays using Web Bluetooth API.
12
11
  - **Automatic Image Processing**: Built-in dithering, encoding, and compression
13
12
  - **Type-Safe**: Full TypeScript support with exported types
14
13
  - **Device Discovery**: Browser-native device picker
15
- - **Protocol Compliance**: Byte-for-byte compatible with [py-opendisplay](https://github.com/OpenDisplay-org/py-opendisplay)
16
-
17
14
  ## Browser Compatibility
18
15
 
19
16
  Web Bluetooth API is required. Supported browsers:
@@ -188,7 +185,7 @@ Write configuration to device. Device must be rebooted for changes to take effec
188
185
  const config = device.config!;
189
186
 
190
187
  // Modify config
191
- config.displays[0].rotation = 180;
188
+ config.displays[0].rotation = 1;
192
189
 
193
190
  // Write back to device
194
191
  await device.writeConfig(config);
@@ -234,7 +231,7 @@ Possible values:
234
231
 
235
232
  ##### `rotation: number`
236
233
 
237
- Display rotation in degrees (0, 90, 180, 270).
234
+ Display rotation steps (by 90 degrees) (0, 1, 2, 3).
238
235
 
239
236
  ##### `config: GlobalConfig | null`
240
237
 
@@ -252,7 +249,7 @@ import { discoverDevices } from '@opendisplay/opendisplay';
252
249
  // Show device picker
253
250
  const device = await discoverDevices();
254
251
  // or with name filter
255
- const device = await discoverDevices('OpenDisplay');
252
+ const device = await discoverDevices('OD');
256
253
  ```
257
254
 
258
255
  ### Types
@@ -441,15 +438,4 @@ npm run lint
441
438
  ## Related Packages
442
439
 
443
440
  - [@opendisplay/epaper-dithering](https://www.npmjs.com/package/@opendisplay/epaper-dithering) - Dithering algorithms for e-paper displays
444
- - [py-opendisplay](https://github.com/OpenDisplay-org/py-opendisplay) - Python version for desktop/server applications
445
-
446
- ## License
447
-
448
- MIT
449
-
450
- ## Links
451
-
452
- - [npm Package](https://www.npmjs.com/package/@opendisplay/opendisplay)
453
- - [GitHub Repository](https://github.com/OpenDisplay-org/opendisplay-js)
454
- - [OpenDisplay Website](https://opendisplay.io)
455
- - [Python Library](https://github.com/OpenDisplay-org/py-opendisplay)
441
+ - [py-opendisplay](https://github.com/OpenDisplay-org/py-opendisplay) - Python version
package/dist/index.cjs CHANGED
@@ -1716,7 +1716,10 @@ var OpenDisplayDevice = class _OpenDisplayDevice {
1716
1716
  * await device.uploadImage(imageData, {
1717
1717
  * refreshMode: RefreshMode.FULL,
1718
1718
  * ditherMode: DitherMode.BURKES,
1719
- * compress: true
1719
+ * compress: true,
1720
+ * onProgress: (current, total, stage) => {
1721
+ * console.log(`${stage}: ${current}/${total} (${Math.floor(current/total*100)}%)`);
1722
+ * }
1720
1723
  * });
1721
1724
  * ```
1722
1725
  */
@@ -1726,9 +1729,12 @@ var OpenDisplayDevice = class _OpenDisplayDevice {
1726
1729
  const refreshMode = options.refreshMode ?? 0 /* FULL */;
1727
1730
  const ditherMode = options.ditherMode ?? import_epaper_dithering4.DitherMode.BURKES;
1728
1731
  const compress = options.compress ?? true;
1732
+ const onProgress = options.onProgress;
1733
+ const onStatusChange = options.onStatusChange;
1729
1734
  console.log(
1730
1735
  `Uploading image (${this.width}x${this.height}, ${import_epaper_dithering4.ColorScheme[this.colorScheme]})`
1731
1736
  );
1737
+ onStatusChange?.("Preparing image...");
1732
1738
  const encodedData = prepareImageForUpload(
1733
1739
  imageData,
1734
1740
  this.width,
@@ -1738,27 +1744,44 @@ var OpenDisplayDevice = class _OpenDisplayDevice {
1738
1744
  );
1739
1745
  let compressedData = null;
1740
1746
  if (compress) {
1747
+ onStatusChange?.("Compressing...");
1741
1748
  compressedData = compressImageData(encodedData, 6);
1742
1749
  if (compressedData.length < MAX_COMPRESSED_SIZE) {
1743
1750
  console.log(`Using compressed upload protocol (size: ${compressedData.length} bytes)`);
1751
+ onStatusChange?.("Uploading...");
1744
1752
  await this.executeUpload({
1745
1753
  imageData: encodedData,
1746
1754
  refreshMode,
1747
1755
  useCompression: true,
1748
1756
  compressedData,
1749
- uncompressedSize: encodedData.length
1757
+ uncompressedSize: encodedData.length,
1758
+ onProgress,
1759
+ onStatusChange
1750
1760
  });
1751
1761
  } else {
1752
1762
  console.log(
1753
1763
  `Compressed size exceeds ${MAX_COMPRESSED_SIZE} bytes, using uncompressed protocol`
1754
1764
  );
1755
- await this.executeUpload({ imageData: encodedData, refreshMode });
1765
+ onStatusChange?.("Uploading...");
1766
+ await this.executeUpload({
1767
+ imageData: encodedData,
1768
+ refreshMode,
1769
+ onProgress,
1770
+ onStatusChange
1771
+ });
1756
1772
  }
1757
1773
  } else {
1758
1774
  console.log("Compression disabled, using uncompressed protocol");
1759
- await this.executeUpload({ imageData: encodedData, refreshMode });
1775
+ onStatusChange?.("Uploading...");
1776
+ await this.executeUpload({
1777
+ imageData: encodedData,
1778
+ refreshMode,
1779
+ onProgress,
1780
+ onStatusChange
1781
+ });
1760
1782
  }
1761
1783
  console.log("Image upload complete");
1784
+ onStatusChange?.("Upload complete!");
1762
1785
  }
1763
1786
  /**
1764
1787
  * Execute image upload using compressed or uncompressed protocol.
@@ -1769,7 +1792,9 @@ var OpenDisplayDevice = class _OpenDisplayDevice {
1769
1792
  refreshMode,
1770
1793
  useCompression = false,
1771
1794
  compressedData,
1772
- uncompressedSize
1795
+ uncompressedSize,
1796
+ onProgress,
1797
+ onStatusChange
1773
1798
  } = params;
1774
1799
  let startCmd;
1775
1800
  let remainingCompressed = null;
@@ -1788,11 +1813,12 @@ var OpenDisplayDevice = class _OpenDisplayDevice {
1788
1813
  validateAckResponse(response, 112 /* DIRECT_WRITE_START */);
1789
1814
  let autoCompleted = false;
1790
1815
  if (useCompression && remainingCompressed && remainingCompressed.length > 0) {
1791
- autoCompleted = await this.sendDataChunks(remainingCompressed);
1816
+ autoCompleted = await this.sendDataChunks(remainingCompressed, onProgress, onStatusChange);
1792
1817
  } else if (!useCompression) {
1793
- autoCompleted = await this.sendDataChunks(imageData);
1818
+ autoCompleted = await this.sendDataChunks(imageData, onProgress, onStatusChange);
1794
1819
  }
1795
1820
  if (!autoCompleted) {
1821
+ onStatusChange?.("Refreshing display...");
1796
1822
  const endCmd = buildDirectWriteEndCommand(refreshMode);
1797
1823
  await this.connection.writeCommand(endCmd);
1798
1824
  response = await this.connection.readResponse(
@@ -1810,11 +1836,13 @@ var OpenDisplayDevice = class _OpenDisplayDevice {
1810
1836
  * - Progress logging
1811
1837
  *
1812
1838
  * @param imageData - Data to send in chunks
1839
+ * @param onProgress - Optional progress callback (current bytes, total bytes, stage)
1840
+ * @param onStatusChange - Optional status message callback
1813
1841
  * @returns True if device auto-completed (sent 0x0072 END early), false if all chunks sent normally
1814
1842
  * @throws {ProtocolError} If unexpected response received
1815
1843
  * @throws {BLETimeoutError} If no response within timeout
1816
1844
  */
1817
- async sendDataChunks(imageData) {
1845
+ async sendDataChunks(imageData, onProgress, onStatusChange) {
1818
1846
  let bytesSent = 0;
1819
1847
  let chunksSent = 0;
1820
1848
  while (bytesSent < imageData.length) {
@@ -1835,6 +1863,7 @@ var OpenDisplayDevice = class _OpenDisplayDevice {
1835
1863
  console.log(
1836
1864
  `No response after chunk ${chunksSent} (${(bytesSent / imageData.length * 100).toFixed(1)}%), waiting for device refresh...`
1837
1865
  );
1866
+ onStatusChange?.("Refreshing display...");
1838
1867
  response = await this.connection.readResponse(
1839
1868
  _OpenDisplayDevice.TIMEOUT_REFRESH
1840
1869
  );
@@ -1844,10 +1873,12 @@ var OpenDisplayDevice = class _OpenDisplayDevice {
1844
1873
  }
1845
1874
  const [command, isAck] = checkResponseType(response);
1846
1875
  if (command === 113 /* DIRECT_WRITE_DATA */) {
1876
+ onProgress?.(bytesSent, imageData.length, "upload");
1847
1877
  } else if (command === 114 /* DIRECT_WRITE_END */) {
1848
1878
  console.log(
1849
1879
  `Received END response after chunk ${chunksSent} - device auto-completed`
1850
1880
  );
1881
+ onProgress?.(imageData.length, imageData.length, "upload");
1851
1882
  return true;
1852
1883
  } else {
1853
1884
  throw new ProtocolError(