@opendisplay/opendisplay 1.0.1 → 1.1.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/dist/index.d.cts CHANGED
@@ -651,7 +651,10 @@ declare class OpenDisplayDevice {
651
651
  * await device.uploadImage(imageData, {
652
652
  * refreshMode: RefreshMode.FULL,
653
653
  * ditherMode: DitherMode.BURKES,
654
- * compress: true
654
+ * compress: true,
655
+ * onProgress: (current, total, stage) => {
656
+ * console.log(`${stage}: ${current}/${total} (${Math.floor(current/total*100)}%)`);
657
+ * }
655
658
  * });
656
659
  * ```
657
660
  */
@@ -659,6 +662,8 @@ declare class OpenDisplayDevice {
659
662
  refreshMode?: RefreshMode;
660
663
  ditherMode?: DitherMode;
661
664
  compress?: boolean;
665
+ onProgress?: (current: number, total: number, stage: string) => void;
666
+ onStatusChange?: (message: string) => void;
662
667
  }): Promise<void>;
663
668
  /**
664
669
  * Execute image upload using compressed or uncompressed protocol.
@@ -673,6 +678,8 @@ declare class OpenDisplayDevice {
673
678
  * - Progress logging
674
679
  *
675
680
  * @param imageData - Data to send in chunks
681
+ * @param onProgress - Optional progress callback (current bytes, total bytes, stage)
682
+ * @param onStatusChange - Optional status message callback
676
683
  * @returns True if device auto-completed (sent 0x0072 END early), false if all chunks sent normally
677
684
  * @throws {ProtocolError} If unexpected response received
678
685
  * @throws {BLETimeoutError} If no response within timeout
package/dist/index.d.ts CHANGED
@@ -651,7 +651,10 @@ declare class OpenDisplayDevice {
651
651
  * await device.uploadImage(imageData, {
652
652
  * refreshMode: RefreshMode.FULL,
653
653
  * ditherMode: DitherMode.BURKES,
654
- * compress: true
654
+ * compress: true,
655
+ * onProgress: (current, total, stage) => {
656
+ * console.log(`${stage}: ${current}/${total} (${Math.floor(current/total*100)}%)`);
657
+ * }
655
658
  * });
656
659
  * ```
657
660
  */
@@ -659,6 +662,8 @@ declare class OpenDisplayDevice {
659
662
  refreshMode?: RefreshMode;
660
663
  ditherMode?: DitherMode;
661
664
  compress?: boolean;
665
+ onProgress?: (current: number, total: number, stage: string) => void;
666
+ onStatusChange?: (message: string) => void;
662
667
  }): Promise<void>;
663
668
  /**
664
669
  * Execute image upload using compressed or uncompressed protocol.
@@ -673,6 +678,8 @@ declare class OpenDisplayDevice {
673
678
  * - Progress logging
674
679
  *
675
680
  * @param imageData - Data to send in chunks
681
+ * @param onProgress - Optional progress callback (current bytes, total bytes, stage)
682
+ * @param onStatusChange - Optional status message callback
676
683
  * @returns True if device auto-completed (sent 0x0072 END early), false if all chunks sent normally
677
684
  * @throws {ProtocolError} If unexpected response received
678
685
  * @throws {BLETimeoutError} If no response within timeout
package/dist/index.js CHANGED
@@ -1340,6 +1340,16 @@ var BLEConnection = class {
1340
1340
  }
1341
1341
  return this.notificationQueue.dequeue(timeoutMs);
1342
1342
  }
1343
+ /**
1344
+ * Clear the notification queue.
1345
+ *
1346
+ * Useful for clearing any stale responses before starting a new operation.
1347
+ * This drains any buffered notifications and cancels pending read requests.
1348
+ */
1349
+ clearQueue() {
1350
+ console.debug(`Clearing notification queue (${this.notificationQueue.size} buffered)`);
1351
+ this.notificationQueue.clear("Queue cleared by request");
1352
+ }
1343
1353
  /**
1344
1354
  * Handle incoming BLE notifications.
1345
1355
  *
@@ -1660,7 +1670,10 @@ var OpenDisplayDevice = class _OpenDisplayDevice {
1660
1670
  * await device.uploadImage(imageData, {
1661
1671
  * refreshMode: RefreshMode.FULL,
1662
1672
  * ditherMode: DitherMode.BURKES,
1663
- * compress: true
1673
+ * compress: true,
1674
+ * onProgress: (current, total, stage) => {
1675
+ * console.log(`${stage}: ${current}/${total} (${Math.floor(current/total*100)}%)`);
1676
+ * }
1664
1677
  * });
1665
1678
  * ```
1666
1679
  */
@@ -1670,9 +1683,12 @@ var OpenDisplayDevice = class _OpenDisplayDevice {
1670
1683
  const refreshMode = options.refreshMode ?? 0 /* FULL */;
1671
1684
  const ditherMode = options.ditherMode ?? DitherMode2.BURKES;
1672
1685
  const compress = options.compress ?? true;
1686
+ const onProgress = options.onProgress;
1687
+ const onStatusChange = options.onStatusChange;
1673
1688
  console.log(
1674
1689
  `Uploading image (${this.width}x${this.height}, ${ColorScheme4[this.colorScheme]})`
1675
1690
  );
1691
+ onStatusChange?.("Preparing image...");
1676
1692
  const encodedData = prepareImageForUpload(
1677
1693
  imageData,
1678
1694
  this.width,
@@ -1682,27 +1698,44 @@ var OpenDisplayDevice = class _OpenDisplayDevice {
1682
1698
  );
1683
1699
  let compressedData = null;
1684
1700
  if (compress) {
1701
+ onStatusChange?.("Compressing...");
1685
1702
  compressedData = compressImageData(encodedData, 6);
1686
1703
  if (compressedData.length < MAX_COMPRESSED_SIZE) {
1687
1704
  console.log(`Using compressed upload protocol (size: ${compressedData.length} bytes)`);
1705
+ onStatusChange?.("Uploading...");
1688
1706
  await this.executeUpload({
1689
1707
  imageData: encodedData,
1690
1708
  refreshMode,
1691
1709
  useCompression: true,
1692
1710
  compressedData,
1693
- uncompressedSize: encodedData.length
1711
+ uncompressedSize: encodedData.length,
1712
+ onProgress,
1713
+ onStatusChange
1694
1714
  });
1695
1715
  } else {
1696
1716
  console.log(
1697
1717
  `Compressed size exceeds ${MAX_COMPRESSED_SIZE} bytes, using uncompressed protocol`
1698
1718
  );
1699
- await this.executeUpload({ imageData: encodedData, refreshMode });
1719
+ onStatusChange?.("Uploading...");
1720
+ await this.executeUpload({
1721
+ imageData: encodedData,
1722
+ refreshMode,
1723
+ onProgress,
1724
+ onStatusChange
1725
+ });
1700
1726
  }
1701
1727
  } else {
1702
1728
  console.log("Compression disabled, using uncompressed protocol");
1703
- await this.executeUpload({ imageData: encodedData, refreshMode });
1729
+ onStatusChange?.("Uploading...");
1730
+ await this.executeUpload({
1731
+ imageData: encodedData,
1732
+ refreshMode,
1733
+ onProgress,
1734
+ onStatusChange
1735
+ });
1704
1736
  }
1705
1737
  console.log("Image upload complete");
1738
+ onStatusChange?.("Upload complete!");
1706
1739
  }
1707
1740
  /**
1708
1741
  * Execute image upload using compressed or uncompressed protocol.
@@ -1713,8 +1746,11 @@ var OpenDisplayDevice = class _OpenDisplayDevice {
1713
1746
  refreshMode,
1714
1747
  useCompression = false,
1715
1748
  compressedData,
1716
- uncompressedSize
1749
+ uncompressedSize,
1750
+ onProgress,
1751
+ onStatusChange
1717
1752
  } = params;
1753
+ this.connection.clearQueue();
1718
1754
  let startCmd;
1719
1755
  let remainingCompressed = null;
1720
1756
  if (useCompression && compressedData && uncompressedSize) {
@@ -1732,11 +1768,12 @@ var OpenDisplayDevice = class _OpenDisplayDevice {
1732
1768
  validateAckResponse(response, 112 /* DIRECT_WRITE_START */);
1733
1769
  let autoCompleted = false;
1734
1770
  if (useCompression && remainingCompressed && remainingCompressed.length > 0) {
1735
- autoCompleted = await this.sendDataChunks(remainingCompressed);
1771
+ autoCompleted = await this.sendDataChunks(remainingCompressed, onProgress, onStatusChange);
1736
1772
  } else if (!useCompression) {
1737
- autoCompleted = await this.sendDataChunks(imageData);
1773
+ autoCompleted = await this.sendDataChunks(imageData, onProgress, onStatusChange);
1738
1774
  }
1739
1775
  if (!autoCompleted) {
1776
+ onStatusChange?.("Refreshing display...");
1740
1777
  const endCmd = buildDirectWriteEndCommand(refreshMode);
1741
1778
  await this.connection.writeCommand(endCmd);
1742
1779
  response = await this.connection.readResponse(
@@ -1754,11 +1791,13 @@ var OpenDisplayDevice = class _OpenDisplayDevice {
1754
1791
  * - Progress logging
1755
1792
  *
1756
1793
  * @param imageData - Data to send in chunks
1794
+ * @param onProgress - Optional progress callback (current bytes, total bytes, stage)
1795
+ * @param onStatusChange - Optional status message callback
1757
1796
  * @returns True if device auto-completed (sent 0x0072 END early), false if all chunks sent normally
1758
1797
  * @throws {ProtocolError} If unexpected response received
1759
1798
  * @throws {BLETimeoutError} If no response within timeout
1760
1799
  */
1761
- async sendDataChunks(imageData) {
1800
+ async sendDataChunks(imageData, onProgress, onStatusChange) {
1762
1801
  let bytesSent = 0;
1763
1802
  let chunksSent = 0;
1764
1803
  while (bytesSent < imageData.length) {
@@ -1779,6 +1818,7 @@ var OpenDisplayDevice = class _OpenDisplayDevice {
1779
1818
  console.log(
1780
1819
  `No response after chunk ${chunksSent} (${(bytesSent / imageData.length * 100).toFixed(1)}%), waiting for device refresh...`
1781
1820
  );
1821
+ onStatusChange?.("Refreshing display...");
1782
1822
  response = await this.connection.readResponse(
1783
1823
  _OpenDisplayDevice.TIMEOUT_REFRESH
1784
1824
  );
@@ -1788,10 +1828,12 @@ var OpenDisplayDevice = class _OpenDisplayDevice {
1788
1828
  }
1789
1829
  const [command, isAck] = checkResponseType(response);
1790
1830
  if (command === 113 /* DIRECT_WRITE_DATA */) {
1831
+ onProgress?.(bytesSent, imageData.length, "upload");
1791
1832
  } else if (command === 114 /* DIRECT_WRITE_END */) {
1792
1833
  console.log(
1793
1834
  `Received END response after chunk ${chunksSent} - device auto-completed`
1794
1835
  );
1836
+ onProgress?.(imageData.length, imageData.length, "upload");
1795
1837
  return true;
1796
1838
  } else {
1797
1839
  throw new ProtocolError(