@wodzik/cubecore 0.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.
Files changed (279) hide show
  1. package/LICENSE +373 -0
  2. package/README.md +118 -0
  3. package/analyze/client.d.ts +18 -0
  4. package/analyze/client.js +42 -0
  5. package/analyze/index.d.ts +114 -0
  6. package/analyze/index.js +151 -0
  7. package/analyze/rotations.d.ts +3 -0
  8. package/analyze/rotations.js +15 -0
  9. package/analyze/roux.d.ts +67 -0
  10. package/analyze/roux.js +95 -0
  11. package/analyze/worker.d.ts +6 -0
  12. package/analyze/worker.js +31 -0
  13. package/analyze/zz.d.ts +47 -0
  14. package/analyze/zz.js +102 -0
  15. package/bld/index.d.ts +14 -0
  16. package/bld/index.js +14 -0
  17. package/bld/memo.d.ts +66 -0
  18. package/bld/memo.js +139 -0
  19. package/bld/scheme.d.ts +30 -0
  20. package/bld/scheme.js +35 -0
  21. package/bld/skin.d.ts +26 -0
  22. package/bld/skin.js +46 -0
  23. package/bld/tracker.d.ts +68 -0
  24. package/bld/tracker.js +145 -0
  25. package/bluetooth/clock.d.ts +22 -0
  26. package/bluetooth/clock.js +37 -0
  27. package/bluetooth/cubeSkins.d.ts +31 -0
  28. package/bluetooth/cubeSkins.js +32 -0
  29. package/bluetooth/gyro.d.ts +31 -0
  30. package/bluetooth/gyro.js +46 -0
  31. package/bluetooth/index.d.ts +5 -0
  32. package/bluetooth/index.js +5 -0
  33. package/bluetooth/session.d.ts +140 -0
  34. package/bluetooth/session.js +201 -0
  35. package/bluetooth/simulated.d.ts +52 -0
  36. package/bluetooth/simulated.js +80 -0
  37. package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-definitions.d.ts +38 -0
  38. package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-definitions.js +51 -0
  39. package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-encrypter.d.ts +35 -0
  40. package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-encrypter.js +70 -0
  41. package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-protocol.d.ts +273 -0
  42. package/bluetooth/vendor/smartcube-web-bluetooth/gan-cube-protocol.js +846 -0
  43. package/bluetooth/vendor/smartcube-web-bluetooth/gan-gen1.d.ts +38 -0
  44. package/bluetooth/vendor/smartcube-web-bluetooth/gan-gen1.js +340 -0
  45. package/bluetooth/vendor/smartcube-web-bluetooth/gan-gen234-packet-validate.d.ts +11 -0
  46. package/bluetooth/vendor/smartcube-web-bluetooth/gan-gen234-packet-validate.js +118 -0
  47. package/bluetooth/vendor/smartcube-web-bluetooth/gan-mac-salt.d.ts +2 -0
  48. package/bluetooth/vendor/smartcube-web-bluetooth/gan-mac-salt.js +15 -0
  49. package/bluetooth/vendor/smartcube-web-bluetooth/gan-smart-cube.d.ts +18 -0
  50. package/bluetooth/vendor/smartcube-web-bluetooth/gan-smart-cube.js +152 -0
  51. package/bluetooth/vendor/smartcube-web-bluetooth/gan-smart-timer.d.ts +74 -0
  52. package/bluetooth/vendor/smartcube-web-bluetooth/gan-smart-timer.js +161 -0
  53. package/bluetooth/vendor/smartcube-web-bluetooth/gan-write-queue.d.ts +7 -0
  54. package/bluetooth/vendor/smartcube-web-bluetooth/gan-write-queue.js +11 -0
  55. package/bluetooth/vendor/smartcube-web-bluetooth/gatt-characteristic-write.d.ts +2 -0
  56. package/bluetooth/vendor/smartcube-web-bluetooth/gatt-characteristic-write.js +36 -0
  57. package/bluetooth/vendor/smartcube-web-bluetooth/index.d.ts +4 -0
  58. package/bluetooth/vendor/smartcube-web-bluetooth/index.js +4 -0
  59. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/address-hints.d.ts +12 -0
  60. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/address-hints.js +122 -0
  61. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/build-picker-options.d.ts +11 -0
  62. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/build-picker-options.js +55 -0
  63. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/default-optional-services.d.ts +21 -0
  64. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/default-optional-services.js +47 -0
  65. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/device-name-scan-filters.d.ts +6 -0
  66. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/device-name-scan-filters.js +24 -0
  67. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/gatt-snapshot.d.ts +6 -0
  68. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/gatt-snapshot.js +57 -0
  69. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-candidates.d.ts +11 -0
  70. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-candidates.js +67 -0
  71. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-probe-moyu32.d.ts +8 -0
  72. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-probe-moyu32.js +98 -0
  73. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-probe-qiyi.d.ts +8 -0
  74. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/mac-probe-qiyi.js +128 -0
  75. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/moyu32-session-crypto.d.ts +5 -0
  76. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/moyu32-session-crypto.js +60 -0
  77. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/normalize-uuid.d.ts +2 -0
  78. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/normalize-uuid.js +8 -0
  79. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/packet-sanity.d.ts +7 -0
  80. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/packet-sanity.js +142 -0
  81. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/profile-rank.d.ts +6 -0
  82. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/profile-rank.js +21 -0
  83. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/types.d.ts +32 -0
  84. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/attachment/types.js +1 -0
  85. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/ble-utils.d.ts +6 -0
  86. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/ble-utils.js +68 -0
  87. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/connect.d.ts +3 -0
  88. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/connect.js +178 -0
  89. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/cubie-cube.d.ts +16 -0
  90. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/cubie-cube.js +141 -0
  91. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/index.d.ts +11 -0
  92. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/index.js +10 -0
  93. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocol.d.ts +26 -0
  94. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocol.js +8 -0
  95. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/gan.d.ts +3 -0
  96. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/gan.js +265 -0
  97. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/giiker.d.ts +3 -0
  98. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/giiker.js +345 -0
  99. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/gocube.d.ts +15 -0
  100. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/gocube.js +382 -0
  101. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu-mhc.d.ts +3 -0
  102. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu-mhc.js +352 -0
  103. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu-v1.d.ts +57 -0
  104. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu-v1.js +243 -0
  105. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu32.d.ts +3 -0
  106. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/moyu32.js +454 -0
  107. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/qiyi.d.ts +3 -0
  108. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/protocols/qiyi.js +424 -0
  109. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/types.d.ts +78 -0
  110. package/bluetooth/vendor/smartcube-web-bluetooth/smartcube/types.js +1 -0
  111. package/bluetooth/vendor/smartcube-web-bluetooth/utils.d.ts +45 -0
  112. package/bluetooth/vendor/smartcube-web-bluetooth/utils.js +150 -0
  113. package/cfop/caseData.d.ts +16 -0
  114. package/cfop/caseData.js +88 -0
  115. package/cfop/cases.d.ts +42 -0
  116. package/cfop/cases.js +116 -0
  117. package/cfop/f2l.d.ts +28 -0
  118. package/cfop/f2l.js +88 -0
  119. package/cfop/f2lData.d.ts +15 -0
  120. package/cfop/f2lData.js +49 -0
  121. package/cfop/index.d.ts +41 -0
  122. package/cfop/index.js +110 -0
  123. package/cfop/slots.d.ts +2 -0
  124. package/cfop/slots.js +1 -0
  125. package/core/arrows.d.ts +24 -0
  126. package/core/arrows.js +22 -0
  127. package/core/bytes.d.ts +27 -0
  128. package/core/bytes.js +145 -0
  129. package/core/centers.d.ts +32 -0
  130. package/core/centers.js +83 -0
  131. package/core/checks.d.ts +53 -0
  132. package/core/checks.js +85 -0
  133. package/core/cubies.d.ts +45 -0
  134. package/core/cubies.js +169 -0
  135. package/core/frames.d.ts +61 -0
  136. package/core/frames.js +126 -0
  137. package/core/geometry.d.ts +58 -0
  138. package/core/geometry.js +132 -0
  139. package/core/index.d.ts +19 -0
  140. package/core/index.js +19 -0
  141. package/core/masks.d.ts +32 -0
  142. package/core/masks.js +39 -0
  143. package/core/method.d.ts +35 -0
  144. package/core/method.js +30 -0
  145. package/core/moves.d.ts +48 -0
  146. package/core/moves.js +88 -0
  147. package/core/notation.d.ts +48 -0
  148. package/core/notation.js +226 -0
  149. package/core/physical.d.ts +53 -0
  150. package/core/physical.js +99 -0
  151. package/core/practice.d.ts +62 -0
  152. package/core/practice.js +87 -0
  153. package/core/sequence.d.ts +90 -0
  154. package/core/sequence.js +157 -0
  155. package/core/stages.d.ts +90 -0
  156. package/core/stages.js +21 -0
  157. package/core/state.d.ts +55 -0
  158. package/core/state.js +140 -0
  159. package/core/stateCodec.d.ts +12 -0
  160. package/core/stateCodec.js +51 -0
  161. package/core/stream.d.ts +45 -0
  162. package/core/stream.js +75 -0
  163. package/core/tracker.d.ts +62 -0
  164. package/core/tracker.js +121 -0
  165. package/element/alg.d.ts +45 -0
  166. package/element/alg.js +150 -0
  167. package/element/base.d.ts +2 -0
  168. package/element/base.js +3 -0
  169. package/element/bld.d.ts +82 -0
  170. package/element/bld.js +222 -0
  171. package/element/index.d.ts +8 -0
  172. package/element/index.js +21 -0
  173. package/element/model.d.ts +36 -0
  174. package/element/model.js +64 -0
  175. package/element/player.d.ts +196 -0
  176. package/element/player.js +680 -0
  177. package/element/practice.d.ts +75 -0
  178. package/element/practice.js +150 -0
  179. package/element/scramble.d.ts +50 -0
  180. package/element/scramble.js +107 -0
  181. package/element/sequence.d.ts +126 -0
  182. package/element/sequence.js +254 -0
  183. package/element/styles.d.ts +32 -0
  184. package/element/styles.js +196 -0
  185. package/image/cache.d.ts +21 -0
  186. package/image/cache.js +66 -0
  187. package/image/index.d.ts +2 -0
  188. package/image/index.js +2 -0
  189. package/image/png-node.d.ts +6 -0
  190. package/image/png-node.js +10 -0
  191. package/image/svg.d.ts +36 -0
  192. package/image/svg.js +215 -0
  193. package/lbl/index.d.ts +10 -0
  194. package/lbl/index.js +31 -0
  195. package/methods/index.d.ts +20 -0
  196. package/methods/index.js +33 -0
  197. package/package.json +133 -0
  198. package/petrus/index.d.ts +11 -0
  199. package/petrus/index.js +27 -0
  200. package/react/index.d.ts +176 -0
  201. package/react/index.js +228 -0
  202. package/render/arrows.d.ts +68 -0
  203. package/render/arrows.js +190 -0
  204. package/render/build/attachments.d.ts +35 -0
  205. package/render/build/attachments.js +94 -0
  206. package/render/build/models.d.ts +9 -0
  207. package/render/build/models.js +27 -0
  208. package/render/build/tiles.d.ts +48 -0
  209. package/render/build/tiles.js +194 -0
  210. package/render/gltf.d.ts +10 -0
  211. package/render/gltf.js +106 -0
  212. package/render/index.d.ts +12 -0
  213. package/render/index.js +12 -0
  214. package/render/layers.d.ts +21 -0
  215. package/render/layers.js +24 -0
  216. package/render/pictures.d.ts +46 -0
  217. package/render/pictures.js +80 -0
  218. package/render/pieceModels.d.ts +38 -0
  219. package/render/pieceModels.js +43 -0
  220. package/render/renderer.d.ts +165 -0
  221. package/render/renderer.js +647 -0
  222. package/render/tile.d.ts +114 -0
  223. package/render/tile.js +390 -0
  224. package/render/viewports.d.ts +25 -0
  225. package/render/viewports.js +26 -0
  226. package/roux/cmll.d.ts +35 -0
  227. package/roux/cmll.js +121 -0
  228. package/roux/cmllData.d.ts +12 -0
  229. package/roux/cmllData.js +49 -0
  230. package/roux/index.d.ts +52 -0
  231. package/roux/index.js +175 -0
  232. package/skin/attachments.d.ts +82 -0
  233. package/skin/attachments.js +110 -0
  234. package/skin/index.d.ts +3 -0
  235. package/skin/index.js +3 -0
  236. package/skin/shapes.d.ts +112 -0
  237. package/skin/shapes.js +147 -0
  238. package/skin/skin.d.ts +911 -0
  239. package/skin/skin.js +412 -0
  240. package/solve/client.d.ts +58 -0
  241. package/solve/client.js +56 -0
  242. package/solve/handle.d.ts +6 -0
  243. package/solve/handle.js +38 -0
  244. package/solve/index.d.ts +8 -0
  245. package/solve/index.js +7 -0
  246. package/solve/lse.d.ts +42 -0
  247. package/solve/lse.js +151 -0
  248. package/solve/pieces.d.ts +18 -0
  249. package/solve/pieces.js +24 -0
  250. package/solve/protocol.d.ts +70 -0
  251. package/solve/protocol.js +16 -0
  252. package/solve/scramble.d.ts +76 -0
  253. package/solve/scramble.js +187 -0
  254. package/solve/stage.d.ts +108 -0
  255. package/solve/stage.js +532 -0
  256. package/solve/twophase.d.ts +36 -0
  257. package/solve/twophase.js +229 -0
  258. package/solve/worker.d.ts +6 -0
  259. package/solve/worker.js +15 -0
  260. package/timeline/codec.d.ts +23 -0
  261. package/timeline/codec.js +84 -0
  262. package/timeline/index.d.ts +7 -0
  263. package/timeline/index.js +7 -0
  264. package/timeline/recording.d.ts +19 -0
  265. package/timeline/recording.js +20 -0
  266. package/timeline/replay.d.ts +68 -0
  267. package/timeline/replay.js +145 -0
  268. package/timeline/segments.d.ts +31 -0
  269. package/timeline/segments.js +39 -0
  270. package/timeline/share.d.ts +30 -0
  271. package/timeline/share.js +62 -0
  272. package/timeline/stats.d.ts +34 -0
  273. package/timeline/stats.js +56 -0
  274. package/timeline/timings.d.ts +30 -0
  275. package/timeline/timings.js +54 -0
  276. package/zz/index.d.ts +29 -0
  277. package/zz/index.js +68 -0
  278. package/zz/seeds.d.ts +9 -0
  279. package/zz/seeds.js +29 -0
@@ -0,0 +1,152 @@
1
+ import * as def from './gan-cube-definitions.js';
2
+ import { GanGen2CubeEncrypter, GanGen3CubeEncrypter, GanGen4CubeEncrypter } from './gan-cube-encrypter.js';
3
+ import { GanGen1CubeConnection } from './gan-gen1.js';
4
+ import { macStringToSaltOrThrow } from './gan-mac-salt.js';
5
+ import { isValidGanGen2Packet, isValidGanGen3Packet, isValidGanGen4Packet, } from './gan-gen234-packet-validate.js';
6
+ import { normalizeUuid } from './smartcube/attachment/normalize-uuid.js';
7
+ import { GanCubeClassicConnection, GanGen2ProtocolDriver, GanGen3ProtocolDriver, GanGen4ProtocolDriver } from './gan-cube-protocol.js';
8
+ /** Iterate over all known GAN cube CICs to find Manufacturer Specific Data */
9
+ function getManufacturerDataBytes(manufacturerData) {
10
+ // Workaround for Bluefy browser which may return raw DataView directly instead of Map
11
+ if (manufacturerData instanceof DataView) {
12
+ return new DataView(manufacturerData.buffer.slice(2, 11));
13
+ }
14
+ for (var id of def.GAN_CIC_LIST) {
15
+ if (manufacturerData.has(id)) {
16
+ return new DataView(manufacturerData.get(id).buffer.slice(0, 9));
17
+ }
18
+ }
19
+ return;
20
+ }
21
+ /** Extract MAC from last 6 bytes of Manufacturer Specific Data */
22
+ function extractMAC(manufacturerData) {
23
+ var mac = [];
24
+ var dataView = getManufacturerDataBytes(manufacturerData);
25
+ if (dataView && dataView.byteLength >= 6) {
26
+ for (let i = 1; i <= 6; i++) {
27
+ mac.push(dataView.getUint8(dataView.byteLength - i).toString(16).toUpperCase().padStart(2, "0"));
28
+ }
29
+ }
30
+ return mac.join(":");
31
+ }
32
+ /** If browser supports Web Bluetooth watchAdvertisements() API, try to retrieve MAC address automatically */
33
+ async function autoRetrieveMacAddress(device) {
34
+ return new Promise((resolve) => {
35
+ if (typeof device.watchAdvertisements != 'function') {
36
+ resolve(null);
37
+ }
38
+ var abortController = new AbortController();
39
+ var onAdvEvent = (evt) => {
40
+ device.removeEventListener("advertisementreceived", onAdvEvent);
41
+ abortController.abort();
42
+ var mac = extractMAC(evt.manufacturerData);
43
+ resolve(mac || null);
44
+ };
45
+ var onAbort = () => {
46
+ device.removeEventListener("advertisementreceived", onAdvEvent);
47
+ abortController.abort();
48
+ resolve(null);
49
+ };
50
+ device.addEventListener("advertisementreceived", onAdvEvent);
51
+ device.watchAdvertisements({ signal: abortController.signal }).catch(onAbort);
52
+ setTimeout(onAbort, 5000);
53
+ });
54
+ }
55
+ function hasGanGen1Profile(serviceUuids) {
56
+ const primary = normalizeUuid(def.GAN_GEN1_PRIMARY_SERVICE);
57
+ const deviceInfo = normalizeUuid(def.GAN_GEN1_DEVICE_INFO_SERVICE);
58
+ return serviceUuids.has(primary) && serviceUuids.has(deviceInfo);
59
+ }
60
+ /**
61
+ * Initiate new connection with the GAN Smart Cube device
62
+ * @param customMacAddressProvider Optional custom provider for cube MAC address
63
+ * @returns Object representing connection API and state
64
+ */
65
+ async function connectGanCube(customMacAddressProvider) {
66
+ // Request user for the bluetooth device (popup selection dialog)
67
+ const nameFilters = [
68
+ { namePrefix: "GAN" },
69
+ { namePrefix: "MG" },
70
+ { namePrefix: "AiCube" },
71
+ ];
72
+ const cicFilters = def.GAN_CIC_LIST.map((companyIdentifier) => ({
73
+ manufacturerData: [{ companyIdentifier }],
74
+ }));
75
+ var device = await navigator.bluetooth.requestDevice({
76
+ filters: [...nameFilters, ...cicFilters],
77
+ optionalServices: [
78
+ def.GAN_GEN1_PRIMARY_SERVICE,
79
+ def.GAN_GEN1_DEVICE_INFO_SERVICE,
80
+ def.GAN_GEN2_SERVICE,
81
+ def.GAN_GEN3_SERVICE,
82
+ def.GAN_GEN4_SERVICE,
83
+ "0000180a-0000-1000-8000-00805f9b34fb",
84
+ "00001800-0000-1000-8000-00805f9b34fb",
85
+ ],
86
+ });
87
+ // Connect to GATT and get primary services
88
+ var gatt = await device.gatt.connect();
89
+ var services = await gatt.getPrimaryServices();
90
+ const serviceUuidSet = new Set(services.map((s) => normalizeUuid(s.uuid)));
91
+ if (hasGanGen1Profile(serviceUuidSet)) {
92
+ return GanGen1CubeConnection.create(device);
93
+ }
94
+ // Retrieve cube MAC address needed for key salting
95
+ var mac = customMacAddressProvider && await customMacAddressProvider(device, false)
96
+ || await autoRetrieveMacAddress(device)
97
+ || customMacAddressProvider && await customMacAddressProvider(device, true);
98
+ if (!mac)
99
+ throw new Error('Unable to determine cube MAC address, connection is not possible!');
100
+ device.mac = mac;
101
+ // Create encryption salt from MAC address bytes placed in reverse order
102
+ var salt = macStringToSaltOrThrow(mac);
103
+ const g2 = normalizeUuid(def.GAN_GEN2_SERVICE);
104
+ const g3 = normalizeUuid(def.GAN_GEN3_SERVICE);
105
+ const g4 = normalizeUuid(def.GAN_GEN4_SERVICE);
106
+ let pick = null;
107
+ if (serviceUuidSet.has(g2))
108
+ pick = 'g2';
109
+ else if (serviceUuidSet.has(g3))
110
+ pick = 'g3';
111
+ else if (serviceUuidSet.has(g4))
112
+ pick = 'g4';
113
+ var conn = null;
114
+ // Resolve type of connected cube device and setup appropriate encryption / protocol driver
115
+ if (pick === 'g2') {
116
+ const service = await gatt.getPrimaryService(def.GAN_GEN2_SERVICE);
117
+ let commandCharacteristic = await service.getCharacteristic(def.GAN_GEN2_COMMAND_CHARACTERISTIC);
118
+ let stateCharacteristic = await service.getCharacteristic(def.GAN_GEN2_STATE_CHARACTERISTIC);
119
+ let key = device.name?.startsWith('AiCube') ? def.GAN_ENCRYPTION_KEYS[1] : def.GAN_ENCRYPTION_KEYS[0];
120
+ let encrypter = new GanGen2CubeEncrypter(new Uint8Array(key.key), new Uint8Array(key.iv), salt);
121
+ let driver = new GanGen2ProtocolDriver();
122
+ conn = await GanCubeClassicConnection.create(device, commandCharacteristic, stateCharacteristic, encrypter, driver, {
123
+ validateDecrypted: isValidGanGen2Packet,
124
+ });
125
+ }
126
+ else if (pick === 'g3') {
127
+ const service = await gatt.getPrimaryService(def.GAN_GEN3_SERVICE);
128
+ let commandCharacteristic = await service.getCharacteristic(def.GAN_GEN3_COMMAND_CHARACTERISTIC);
129
+ let stateCharacteristic = await service.getCharacteristic(def.GAN_GEN3_STATE_CHARACTERISTIC);
130
+ let key = def.GAN_ENCRYPTION_KEYS[0];
131
+ let encrypter = new GanGen3CubeEncrypter(new Uint8Array(key.key), new Uint8Array(key.iv), salt);
132
+ let driver = new GanGen3ProtocolDriver();
133
+ conn = await GanCubeClassicConnection.create(device, commandCharacteristic, stateCharacteristic, encrypter, driver, {
134
+ validateDecrypted: isValidGanGen3Packet,
135
+ });
136
+ }
137
+ else if (pick === 'g4') {
138
+ const service = await gatt.getPrimaryService(def.GAN_GEN4_SERVICE);
139
+ let commandCharacteristic = await service.getCharacteristic(def.GAN_GEN4_COMMAND_CHARACTERISTIC);
140
+ let stateCharacteristic = await service.getCharacteristic(def.GAN_GEN4_STATE_CHARACTERISTIC);
141
+ let key = def.GAN_ENCRYPTION_KEYS[0];
142
+ let encrypter = new GanGen4CubeEncrypter(new Uint8Array(key.key), new Uint8Array(key.iv), salt);
143
+ let driver = new GanGen4ProtocolDriver();
144
+ conn = await GanCubeClassicConnection.create(device, commandCharacteristic, stateCharacteristic, encrypter, driver, {
145
+ validateDecrypted: isValidGanGen4Packet,
146
+ });
147
+ }
148
+ if (!conn)
149
+ throw new Error("Can't find target BLE services - wrong or unsupported cube device model");
150
+ return conn;
151
+ }
152
+ export { connectGanCube };
@@ -0,0 +1,74 @@
1
+ import { Observable } from 'rxjs';
2
+ /**
3
+ * GAN Smart Timer events/states
4
+ */
5
+ declare enum GanTimerState {
6
+ /** Fired when timer is disconnected from bluetooth */
7
+ DISCONNECT = 0,
8
+ /** Grace delay is expired and timer is ready to start */
9
+ GET_SET = 1,
10
+ /** Hands removed from the timer before grace delay expired */
11
+ HANDS_OFF = 2,
12
+ /** Timer is running */
13
+ RUNNING = 3,
14
+ /** Timer is stopped, this event includes recorded time */
15
+ STOPPED = 4,
16
+ /** Timer is reset and idle */
17
+ IDLE = 5,
18
+ /** Hands are placed on the timer */
19
+ HANDS_ON = 6,
20
+ /** Timer moves to this state immediately after STOPPED */
21
+ FINISHED = 7
22
+ }
23
+ /**
24
+ * Representation of time value
25
+ */
26
+ interface GanTimerTime {
27
+ readonly minutes: number;
28
+ readonly seconds: number;
29
+ readonly milliseconds: number;
30
+ readonly asTimestamp: number;
31
+ toString(): string;
32
+ }
33
+ /**
34
+ * Timer state event
35
+ */
36
+ interface GanTimerEvent {
37
+ /** Current timer state */
38
+ state: GanTimerState;
39
+ /** Recorder time value in case of STOPPED event */
40
+ recordedTime?: GanTimerTime;
41
+ }
42
+ /**
43
+ * Representation of recorded in timer memory time values
44
+ */
45
+ interface GanTimerRecordedTimes {
46
+ displayTime: GanTimerTime;
47
+ previousTimes: [GanTimerTime, GanTimerTime, GanTimerTime];
48
+ }
49
+ /**
50
+ * GAN Timer connection object representing connection API and state
51
+ */
52
+ interface GanTimerConnection {
53
+ /** RxJS Subject to subscribe for cube event messages */
54
+ events$: Observable<GanTimerEvent>;
55
+ /** Retrieve last time values recored by timer */
56
+ getRecordedTimes(): Promise<GanTimerRecordedTimes>;
57
+ /** Disconnect from timer */
58
+ disconnect(): void;
59
+ }
60
+ /**
61
+ * Construct time object
62
+ */
63
+ declare function makeTime(min: number, sec: number, msec: number): GanTimerTime;
64
+ /**
65
+ * Construct time object from milliseconds timestamp
66
+ */
67
+ declare function makeTimeFromTimestamp(timestamp: number): GanTimerTime;
68
+ /**
69
+ * Initiate new connection with the GAN Smart Timer device
70
+ * @returns Connection connection object representing connection API and state
71
+ */
72
+ declare function connectGanTimer(): Promise<GanTimerConnection>;
73
+ export type { GanTimerConnection, GanTimerEvent, GanTimerTime, GanTimerRecordedTimes };
74
+ export { connectGanTimer, makeTime, makeTimeFromTimestamp, GanTimerState };
@@ -0,0 +1,161 @@
1
+ import { Subject } from 'rxjs';
2
+ // GAN Smart Timer bluetooth service and characteristic UUIDs
3
+ const GAN_TIMER_SERVICE = '0000fff0-0000-1000-8000-00805f9b34fb';
4
+ const GAN_TIMER_TIME_CHARACTERISTIC = '0000fff2-0000-1000-8000-00805f9b34fb';
5
+ const GAN_TIMER_STATE_CHARACTERISTIC = '0000fff5-0000-1000-8000-00805f9b34fb';
6
+ /**
7
+ * GAN Smart Timer events/states
8
+ */
9
+ var GanTimerState;
10
+ (function (GanTimerState) {
11
+ /** Fired when timer is disconnected from bluetooth */
12
+ GanTimerState[GanTimerState["DISCONNECT"] = 0] = "DISCONNECT";
13
+ /** Grace delay is expired and timer is ready to start */
14
+ GanTimerState[GanTimerState["GET_SET"] = 1] = "GET_SET";
15
+ /** Hands removed from the timer before grace delay expired */
16
+ GanTimerState[GanTimerState["HANDS_OFF"] = 2] = "HANDS_OFF";
17
+ /** Timer is running */
18
+ GanTimerState[GanTimerState["RUNNING"] = 3] = "RUNNING";
19
+ /** Timer is stopped, this event includes recorded time */
20
+ GanTimerState[GanTimerState["STOPPED"] = 4] = "STOPPED";
21
+ /** Timer is reset and idle */
22
+ GanTimerState[GanTimerState["IDLE"] = 5] = "IDLE";
23
+ /** Hands are placed on the timer */
24
+ GanTimerState[GanTimerState["HANDS_ON"] = 6] = "HANDS_ON";
25
+ /** Timer moves to this state immediately after STOPPED */
26
+ GanTimerState[GanTimerState["FINISHED"] = 7] = "FINISHED";
27
+ })(GanTimerState || (GanTimerState = {}));
28
+ /**
29
+ * Construct time object
30
+ */
31
+ function makeTime(min, sec, msec) {
32
+ return {
33
+ minutes: min,
34
+ seconds: sec,
35
+ milliseconds: msec,
36
+ asTimestamp: 60000 * min + 1000 * sec + msec,
37
+ toString: () => `${min.toString(10)}:${sec.toString(10).padStart(2, '0')}.${msec.toString(10).padStart(3, '0')}`
38
+ };
39
+ }
40
+ /**
41
+ * Construct time object from raw event data
42
+ */
43
+ function makeTimeFromRaw(data, offset) {
44
+ var min = data.getUint8(offset);
45
+ var sec = data.getUint8(offset + 1);
46
+ var msec = data.getUint16(offset + 2, true);
47
+ return makeTime(min, sec, msec);
48
+ }
49
+ /**
50
+ * Construct time object from milliseconds timestamp
51
+ */
52
+ function makeTimeFromTimestamp(timestamp) {
53
+ var min = Math.trunc(timestamp / 60000);
54
+ var sec = Math.trunc(timestamp % 60000 / 1000);
55
+ var msec = Math.trunc(timestamp % 1000);
56
+ return makeTime(min, sec, msec);
57
+ }
58
+ /**
59
+ * Calculate ArrayBuffer checksum using CRC-16/CCIT-FALSE algorithm variation
60
+ */
61
+ function crc16ccit(buff) {
62
+ var dataView = new DataView(buff);
63
+ var crc = 0xFFFF;
64
+ for (let i = 0; i < dataView.byteLength; ++i) {
65
+ crc ^= dataView.getUint8(i) << 8;
66
+ for (let j = 0; j < 8; ++j) {
67
+ crc = (crc & 0x8000) > 0 ? (crc << 1) ^ 0x1021 : crc << 1;
68
+ }
69
+ }
70
+ return crc & 0xFFFF;
71
+ }
72
+ /**
73
+ * Ensure received timer event has valid data: check data magic and CRC
74
+ */
75
+ function validateEventData(data) {
76
+ try {
77
+ if (data?.byteLength == 0 || data.getUint8(0) != 0xFE) {
78
+ return false;
79
+ }
80
+ var eventCRC = data.getUint16(data.byteLength - 2, true);
81
+ var calculatedCRC = crc16ccit(data.buffer.slice(2, data.byteLength - 2));
82
+ return eventCRC == calculatedCRC;
83
+ }
84
+ catch (err) {
85
+ return false;
86
+ }
87
+ }
88
+ /**
89
+ * Construct event object from raw data
90
+ */
91
+ function buildTimerEvent(data) {
92
+ var evt = {
93
+ state: data.getUint8(3)
94
+ };
95
+ if (evt.state == GanTimerState.STOPPED) {
96
+ evt.recordedTime = makeTimeFromRaw(data, 4);
97
+ }
98
+ return evt;
99
+ }
100
+ /**
101
+ * Initiate new connection with the GAN Smart Timer device
102
+ * @returns Connection connection object representing connection API and state
103
+ */
104
+ async function connectGanTimer() {
105
+ // Request user for the bluetooth device (popup selection dialog)
106
+ var device = await navigator.bluetooth.requestDevice({
107
+ filters: [
108
+ { namePrefix: "GAN" },
109
+ { namePrefix: "gan" },
110
+ { namePrefix: "Gan" }
111
+ ],
112
+ optionalServices: [GAN_TIMER_SERVICE]
113
+ });
114
+ // Connect to GATT server
115
+ var server = await device.gatt.connect();
116
+ // Connect to main timer service and characteristics
117
+ var service = await server.getPrimaryService(GAN_TIMER_SERVICE);
118
+ var timeCharacteristic = await service.getCharacteristic(GAN_TIMER_TIME_CHARACTERISTIC);
119
+ var stateCharacteristic = await service.getCharacteristic(GAN_TIMER_STATE_CHARACTERISTIC);
120
+ // Subscribe to value updates of the timer state characteristic
121
+ var eventSubject = new Subject();
122
+ var onStateChanged = async (evt) => {
123
+ var chr = evt.target;
124
+ var data = chr.value;
125
+ if (validateEventData(data)) {
126
+ eventSubject.next(buildTimerEvent(data));
127
+ }
128
+ else {
129
+ eventSubject.error("Invalid event data received from Timer");
130
+ }
131
+ };
132
+ stateCharacteristic.addEventListener('characteristicvaluechanged', onStateChanged);
133
+ stateCharacteristic.startNotifications();
134
+ // This action retrieves latest recorded times from timer
135
+ var getRecordedTimesAction = async () => {
136
+ var data = await timeCharacteristic.readValue();
137
+ return data?.byteLength >= 16 ?
138
+ Promise.resolve({
139
+ displayTime: makeTimeFromRaw(data, 0),
140
+ previousTimes: [makeTimeFromRaw(data, 4), makeTimeFromRaw(data, 8), makeTimeFromRaw(data, 12)]
141
+ }) : Promise.reject("Invalid time characteristic value received from Timer");
142
+ };
143
+ // Manual disconnect action
144
+ var disconnectAction = async () => {
145
+ device.removeEventListener('gattserverdisconnected', disconnectAction);
146
+ stateCharacteristic.removeEventListener('characteristicvaluechanged', onStateChanged);
147
+ await stateCharacteristic.stopNotifications().catch(() => { });
148
+ eventSubject.next({ state: GanTimerState.DISCONNECT });
149
+ eventSubject.complete();
150
+ if (server.connected) {
151
+ server.disconnect();
152
+ }
153
+ };
154
+ device.addEventListener('gattserverdisconnected', disconnectAction);
155
+ return {
156
+ events$: eventSubject,
157
+ getRecordedTimes: getRecordedTimesAction,
158
+ disconnect: disconnectAction,
159
+ };
160
+ }
161
+ export { connectGanTimer, makeTime, makeTimeFromTimestamp, GanTimerState };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Serializes GATT write operations (writeValueWithResponse / writeValueWithoutResponse) to avoid overlapping operations on some stacks.
3
+ */
4
+ export declare class GattWriteQueue {
5
+ private tail;
6
+ enqueue<T>(fn: () => Promise<T>): Promise<T>;
7
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Serializes GATT write operations (writeValueWithResponse / writeValueWithoutResponse) to avoid overlapping operations on some stacks.
3
+ */
4
+ export class GattWriteQueue {
5
+ tail = Promise.resolve();
6
+ enqueue(fn) {
7
+ const run = this.tail.then(() => fn());
8
+ this.tail = run.then(() => { }, () => { });
9
+ return run;
10
+ }
11
+ }
@@ -0,0 +1,2 @@
1
+ /// <reference types="web-bluetooth" />
2
+ export declare function writeGattCharacteristicValue(characteristic: BluetoothRemoteGATTCharacteristic, value: BufferSource): Promise<void>;
@@ -0,0 +1,36 @@
1
+ import { GattWriteQueue } from './gan-write-queue.js';
2
+ /**
3
+ * One in-flight GATT operation per connection on common stacks; overlapping writes throw
4
+ * NetworkError "GATT operation already in progress". Queue writes per device.
5
+ */
6
+ const writeQueuesByDevice = new WeakMap();
7
+ function writeQueueForCharacteristic(characteristic) {
8
+ const device = characteristic.service.device;
9
+ let q = writeQueuesByDevice.get(device);
10
+ if (!q) {
11
+ q = new GattWriteQueue();
12
+ writeQueuesByDevice.set(device, q);
13
+ }
14
+ return q;
15
+ }
16
+ /**
17
+ * Writes using explicit Web Bluetooth APIs (writeValue is deprecated).
18
+ * Chooses write-with-response vs write-without-response from characteristic.properties.
19
+ * When both are supported, prefers write-with-response for reliable cross-platform command traffic.
20
+ */
21
+ function writeGattCharacteristicValueNow(characteristic, value) {
22
+ const { write, writeWithoutResponse } = characteristic.properties;
23
+ if (!write && !writeWithoutResponse) {
24
+ return Promise.reject(new Error('Characteristic is not writable'));
25
+ }
26
+ if (writeWithoutResponse && !write) {
27
+ return characteristic.writeValueWithoutResponse(value);
28
+ }
29
+ if (write && !writeWithoutResponse) {
30
+ return characteristic.writeValueWithResponse(value);
31
+ }
32
+ return characteristic.writeValueWithResponse(value);
33
+ }
34
+ export function writeGattCharacteristicValue(characteristic, value) {
35
+ return writeQueueForCharacteristic(characteristic).enqueue(() => writeGattCharacteristicValueNow(characteristic, value));
36
+ }
@@ -0,0 +1,4 @@
1
+ export * from './gan-smart-timer.js';
2
+ export * from './gan-smart-cube.js';
3
+ export * from './utils.js';
4
+ export * from './smartcube/index.js';
@@ -0,0 +1,4 @@
1
+ export * from './gan-smart-timer.js';
2
+ export * from './gan-smart-cube.js';
3
+ export * from './utils.js';
4
+ export * from './smartcube/index.js';
@@ -0,0 +1,12 @@
1
+ /// <reference types="web-bluetooth" />
2
+ export declare function getCachedMacForDevice(device: BluetoothDevice): string | null;
3
+ export declare function setCachedMacForDevice(device: BluetoothDevice, mac: string): void;
4
+ export declare function removeCachedMacForDevice(device: BluetoothDevice): void;
5
+ /**
6
+ * Wait for manufacturer data from advertisements (single shared listener).
7
+ * Merges all packets until timeout: the first BLE advertisement often has an empty
8
+ * manufacturerData map; MAC-bearing data appears on later frames.
9
+ */
10
+ export declare function waitForManufacturerData(device: BluetoothDevice, timeoutMs?: number): Promise<BluetoothManufacturerData | null>;
11
+ /** GAN-style MAC from manufacturer data (last 6 bytes, reversed order in payload). */
12
+ export declare function macFromGanManufacturerData(mf: BluetoothManufacturerData | DataView): string | null;
@@ -0,0 +1,122 @@
1
+ import * as ganDef from '../../gan-cube-definitions.js';
2
+ const STORAGE_PREFIX = 'smartcube-ble-mac:';
3
+ export function getCachedMacForDevice(device) {
4
+ if (typeof localStorage === 'undefined') {
5
+ return null;
6
+ }
7
+ try {
8
+ return localStorage.getItem(STORAGE_PREFIX + device.id);
9
+ }
10
+ catch {
11
+ return null;
12
+ }
13
+ }
14
+ export function setCachedMacForDevice(device, mac) {
15
+ if (typeof localStorage === 'undefined') {
16
+ return;
17
+ }
18
+ try {
19
+ localStorage.setItem(STORAGE_PREFIX + device.id, mac);
20
+ }
21
+ catch {
22
+ /* ignore quota */
23
+ }
24
+ }
25
+ export function removeCachedMacForDevice(device) {
26
+ if (typeof localStorage === 'undefined') {
27
+ return;
28
+ }
29
+ try {
30
+ localStorage.removeItem(STORAGE_PREFIX + device.id);
31
+ }
32
+ catch {
33
+ /* ignore */
34
+ }
35
+ }
36
+ function mergeManufacturerDataInto(acc, mf) {
37
+ if (!mf || typeof mf.keys !== 'function') {
38
+ return;
39
+ }
40
+ for (const id of mf.keys()) {
41
+ const v = mf.get(id);
42
+ if (v) {
43
+ acc.set(id, new DataView(v.buffer.slice(v.byteOffset, v.byteOffset + v.byteLength)));
44
+ }
45
+ }
46
+ }
47
+ /**
48
+ * Wait for manufacturer data from advertisements (single shared listener).
49
+ * Merges all packets until timeout: the first BLE advertisement often has an empty
50
+ * manufacturerData map; MAC-bearing data appears on later frames.
51
+ */
52
+ export async function waitForManufacturerData(device, timeoutMs = 5000) {
53
+ if (typeof device.watchAdvertisements !== 'function') {
54
+ return null;
55
+ }
56
+ const name = (device.name || '').trim();
57
+ /** MoYu32-style names rarely expose useful MF in the picker; keep connect snappy. */
58
+ const emptyFirstAdvExit = name.startsWith('WCU_');
59
+ return new Promise((resolve) => {
60
+ const abortController = new AbortController();
61
+ const merged = new Map();
62
+ let sawAdvertisement = false;
63
+ let finished = false;
64
+ const cleanup = () => {
65
+ device.removeEventListener('advertisementreceived', onAdvEvent);
66
+ abortController.abort();
67
+ clearTimeout(maxTimer);
68
+ };
69
+ const finish = (value) => {
70
+ if (finished) {
71
+ return;
72
+ }
73
+ finished = true;
74
+ cleanup();
75
+ resolve(value);
76
+ };
77
+ const onAdvEvent = (evt) => {
78
+ const adv = evt;
79
+ mergeManufacturerDataInto(merged, adv.manufacturerData ?? null);
80
+ const isFirstAdv = !sawAdvertisement;
81
+ sawAdvertisement = true;
82
+ if (merged.size > 0) {
83
+ finish(merged);
84
+ return;
85
+ }
86
+ if (emptyFirstAdvExit && isFirstAdv) {
87
+ finish(null);
88
+ }
89
+ };
90
+ const maxTimer = setTimeout(() => {
91
+ finish(merged.size > 0 ? merged : null);
92
+ }, timeoutMs);
93
+ device.addEventListener('advertisementreceived', onAdvEvent);
94
+ device.watchAdvertisements({ signal: abortController.signal }).catch(() => {
95
+ clearTimeout(maxTimer);
96
+ finish(null);
97
+ });
98
+ });
99
+ }
100
+ /** GAN-style MAC from manufacturer data (last 6 bytes, reversed order in payload). */
101
+ export function macFromGanManufacturerData(mf) {
102
+ function getBytes(manufacturerData) {
103
+ if (manufacturerData instanceof DataView) {
104
+ return new DataView(manufacturerData.buffer.slice(2, 11));
105
+ }
106
+ for (const id of ganDef.GAN_CIC_LIST) {
107
+ if (manufacturerData.has(id)) {
108
+ return new DataView(manufacturerData.get(id).buffer.slice(0, 9));
109
+ }
110
+ }
111
+ return undefined;
112
+ }
113
+ const dataView = getBytes(mf);
114
+ if (!dataView || dataView.byteLength < 6) {
115
+ return null;
116
+ }
117
+ const mac = [];
118
+ for (let i = 1; i <= 6; i++) {
119
+ mac.push(dataView.getUint8(dataView.byteLength - i).toString(16).toUpperCase().padStart(2, '0'));
120
+ }
121
+ return mac.join(':');
122
+ }
@@ -0,0 +1,11 @@
1
+ import type { SmartCubeProtocol } from '../protocol.js';
2
+ import type { DeviceSelectionMode } from './types.js';
3
+ export interface BuildRequestDeviceOptionsExtras {
4
+ deviceName?: string;
5
+ }
6
+ /**
7
+ * Build Web Bluetooth `requestDevice` options for the given mode.
8
+ * Manufacturer IDs appear in `filters` (OR with name filters) and in `optionalManufacturerData`
9
+ * so `watchAdvertisements` exposes manufacturer data when the device matched by name prefix.
10
+ */
11
+ export declare function buildRequestDeviceOptions(protocols: SmartCubeProtocol[], mode: DeviceSelectionMode, extras?: BuildRequestDeviceOptionsExtras): RequestDeviceOptions;
@@ -0,0 +1,55 @@
1
+ import { mergeOptionalServiceUuids } from './default-optional-services.js';
2
+ import { extraScanFiltersForKnownDeviceName } from './device-name-scan-filters.js';
3
+ function mergeManufacturerCompanyIds(protocols) {
4
+ const all = new Set();
5
+ for (const p of protocols) {
6
+ if (p.optionalManufacturerData) {
7
+ for (const cic of p.optionalManufacturerData) {
8
+ all.add(cic);
9
+ }
10
+ }
11
+ }
12
+ return Array.from(all).sort((a, b) => a - b);
13
+ }
14
+ /**
15
+ * Build Web Bluetooth `requestDevice` options for the given mode.
16
+ * Manufacturer IDs appear in `filters` (OR with name filters) and in `optionalManufacturerData`
17
+ * so `watchAdvertisements` exposes manufacturer data when the device matched by name prefix.
18
+ */
19
+ export function buildRequestDeviceOptions(protocols, mode, extras) {
20
+ const protocolServices = [];
21
+ for (const p of protocols) {
22
+ for (const s of p.optionalServices) {
23
+ protocolServices.push(s);
24
+ }
25
+ }
26
+ const optionalServices = mergeOptionalServiceUuids(protocolServices);
27
+ const cics = mergeManufacturerCompanyIds(protocols);
28
+ const optionalManufacturerData = cics.length > 0 ? cics : undefined;
29
+ if (mode === 'any') {
30
+ return {
31
+ acceptAllDevices: true,
32
+ optionalServices,
33
+ optionalManufacturerData,
34
+ };
35
+ }
36
+ const filters = [];
37
+ for (const p of protocols) {
38
+ for (const f of p.nameFilters) {
39
+ filters.push(f);
40
+ }
41
+ }
42
+ for (const f of extraScanFiltersForKnownDeviceName(extras?.deviceName)) {
43
+ filters.push(f);
44
+ }
45
+ for (const companyIdentifier of cics) {
46
+ filters.push({
47
+ manufacturerData: [{ companyIdentifier }],
48
+ });
49
+ }
50
+ return {
51
+ filters,
52
+ optionalServices,
53
+ optionalManufacturerData,
54
+ };
55
+ }