@adminide-stack/yantra-mobile 12.0.131-alpha.1 → 12.0.131-alpha.2

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 (42) hide show
  1. package/lib/features/devices/bleTransport.js +54 -20
  2. package/lib/features/devices/bleTransport.js.map +1 -1
  3. package/lib/features/devices/brands/channel.js +19 -0
  4. package/lib/features/devices/brands/channel.js.map +1 -0
  5. package/lib/features/devices/brands/detect.js +318 -0
  6. package/lib/features/devices/brands/detect.js.map +1 -0
  7. package/lib/features/devices/brands/frames.js +103 -0
  8. package/lib/features/devices/brands/frames.js.map +1 -0
  9. package/lib/features/devices/brands/index.js +30 -0
  10. package/lib/features/devices/brands/index.js.map +1 -0
  11. package/lib/features/devices/brands/qnScale.js +98 -0
  12. package/lib/features/devices/brands/qnScale.js.map +1 -0
  13. package/lib/features/devices/brands/renphoBloodPressure.js +92 -0
  14. package/lib/features/devices/brands/renphoBloodPressure.js.map +1 -0
  15. package/lib/features/devices/brands/renphoFoodScale.js +42 -0
  16. package/lib/features/devices/brands/renphoFoodScale.js.map +1 -0
  17. package/lib/features/devices/brands/renphoJumpRope.js +71 -0
  18. package/lib/features/devices/brands/renphoJumpRope.js.map +1 -0
  19. package/lib/features/devices/brands/renphoRing.js +82 -0
  20. package/lib/features/devices/brands/renphoRing.js.map +1 -0
  21. package/lib/features/devices/brands/renphoScale.js +181 -0
  22. package/lib/features/devices/brands/renphoScale.js.map +1 -0
  23. package/lib/features/devices/brands/renphoTape.js +68 -0
  24. package/lib/features/devices/brands/renphoTape.js.map +1 -0
  25. package/lib/features/devices/brands/uuids.js +38 -0
  26. package/lib/features/devices/brands/uuids.js.map +1 -0
  27. package/lib/features/devices/catalog.js +40 -0
  28. package/lib/features/devices/catalog.js.map +1 -1
  29. package/lib/features/devices/deviceStore.js +5 -2
  30. package/lib/features/devices/deviceStore.js.map +1 -1
  31. package/lib/features/devices/drivers.js +5 -1
  32. package/lib/features/devices/drivers.js.map +1 -1
  33. package/lib/features/devices/gatt/ftms.js +129 -0
  34. package/lib/features/devices/gatt/ftms.js.map +1 -0
  35. package/lib/features/devices/gatt/uuids.js +8 -2
  36. package/lib/features/devices/gatt/uuids.js.map +1 -1
  37. package/lib/features/devices/link.js.map +1 -1
  38. package/lib/features/devices/readings.js +32 -1
  39. package/lib/features/devices/readings.js.map +1 -1
  40. package/lib/features/devices/session.js +24 -5
  41. package/lib/features/devices/session.js.map +1 -1
  42. package/package.json +3 -3
@@ -39,16 +39,30 @@ function getManager() {
39
39
  }
40
40
  return manager;
41
41
  }
42
+ function advertisementOf(device) {
43
+ var _a, _b, _c, _d, _e;
44
+ const serviceData = {};
45
+ for (const [uuid, value] of Object.entries((_a = device.serviceData) != null ? _a : {})) serviceData[uuid] = fromBase64(value);
46
+ return {
47
+ id: device.id,
48
+ name: (_c = (_b = device.localName) != null ? _b : device.name) != null ? _c : void 0,
49
+ rssi: (_d = device.rssi) != null ? _d : void 0,
50
+ manufacturerData: device.manufacturerData ? fromBase64(device.manufacturerData) : void 0,
51
+ serviceUUIDs: (_e = device.serviceUUIDs) != null ? _e : void 0,
52
+ serviceData
53
+ };
54
+ }
42
55
  function linkFor(m, device, offered) {
43
56
  var _a, _b;
44
- const has = (service, characteristic) => [...offered].some((entry) => {
45
- const [s, c] = entry.split("|");
46
- return sameUuid(s, service) && (characteristic === void 0 || c !== void 0 && sameUuid(c, characteristic));
47
- });
57
+ const find = (service, characteristic) => offered.find((o) => sameUuid(o.service, service) && (characteristic === void 0 || o.characteristic !== void 0 && sameUuid(o.characteristic, characteristic)));
48
58
  return {
49
59
  id: device.id,
50
60
  name: (_b = (_a = device.name) != null ? _a : device.localName) != null ? _b : "Bluetooth device",
51
- has,
61
+ has: (service, characteristic) => find(service, characteristic) !== void 0,
62
+ canWriteWithoutResponse: (service, characteristic) => {
63
+ var _a2;
64
+ return ((_a2 = find(service, characteristic)) == null ? void 0 : _a2.writeWithoutResponse) === true;
65
+ },
52
66
  monitor(service, characteristic, onValue) {
53
67
  const sub = device.monitorCharacteristicForService(service, characteristic, (error, c) => {
54
68
  if (error || !(c == null ? void 0 : c.value)) return;
@@ -56,8 +70,12 @@ function linkFor(m, device, offered) {
56
70
  });
57
71
  return () => sub.remove();
58
72
  },
59
- async write(service, characteristic, bytes) {
60
- await device.writeCharacteristicWithResponseForService(service, characteristic, toBase64(bytes));
73
+ async write(service, characteristic, bytes, options) {
74
+ if (options == null ? void 0 : options.withoutResponse) {
75
+ await device.writeCharacteristicWithoutResponseForService(service, characteristic, toBase64(bytes));
76
+ } else {
77
+ await device.writeCharacteristicWithResponseForService(service, characteristic, toBase64(bytes));
78
+ }
61
79
  },
62
80
  async read(service, characteristic) {
63
81
  const c = await device.readCharacteristicForService(service, characteristic);
@@ -90,24 +108,32 @@ async function createBleTransport() {
90
108
  const result = await PermissionsAndroid.requestMultiple(wanted);
91
109
  return wanted.every((p) => result[p] === PermissionsAndroid.RESULTS.GRANTED);
92
110
  },
93
- scan(services, onFound, onError) {
111
+ scan(request, onFound, onError) {
94
112
  const seen = /* @__PURE__ */ new Set();
95
- void m.startDeviceScan([...services], {
96
- allowDuplicates: false
113
+ const filter = request.match ? null : [...request.services];
114
+ void m.startDeviceScan(filter, {
115
+ allowDuplicates: Boolean(request.onAdvertisement)
97
116
  }, (error, device) => {
98
- var _a, _b, _c;
117
+ var _a, _b, _c, _d;
99
118
  if (error) {
100
119
  onError(describeBleError(error));
101
120
  return;
102
121
  }
103
- if (!device || seen.has(device.id)) return;
122
+ if (!device) return;
123
+ const adv = advertisementOf(device);
124
+ (_a = request.onAdvertisement) == null ? void 0 : _a.call(request, adv);
125
+ if (seen.has(device.id)) return;
126
+ const standard = kindForServices(adv.serviceUUIDs);
127
+ const wantsStandard = standard !== void 0 && ((_b = adv.serviceUUIDs) != null ? _b : []).some((u) => request.services.some((s) => sameUuid(u, s)));
128
+ const brand = wantsStandard ? void 0 : (_c = request.match) == null ? void 0 : _c.call(request, adv);
129
+ if (!wantsStandard && !brand && request.match) return;
104
130
  seen.add(device.id);
105
- onFound({
131
+ onFound(__spreadValues({
106
132
  id: device.id,
107
- name: (_b = (_a = device.localName) != null ? _a : device.name) != null ? _b : "Bluetooth device",
108
- rssi: (_c = device.rssi) != null ? _c : void 0,
109
- kind: kindForServices(device.serviceUUIDs)
110
- });
133
+ name: (_d = adv.name) != null ? _d : "Bluetooth device",
134
+ rssi: adv.rssi,
135
+ kind: standard
136
+ }, brand));
111
137
  }).catch((err) => onError(describeBleError(err)));
112
138
  return () => {
113
139
  void m.stopDeviceScan();
@@ -123,10 +149,18 @@ async function createBleTransport() {
123
149
  if (Platform.OS === "android") {
124
150
  await device.requestMTU(185).catch(() => void 0);
125
151
  }
126
- const offered = /* @__PURE__ */ new Set();
152
+ const offered = [];
127
153
  for (const service of await device.services()) {
128
- offered.add(service.uuid);
129
- for (const c of await service.characteristics()) offered.add(`${service.uuid}|${c.uuid}`);
154
+ offered.push({
155
+ service: service.uuid
156
+ });
157
+ for (const c of await service.characteristics()) {
158
+ offered.push({
159
+ service: service.uuid,
160
+ characteristic: c.uuid,
161
+ writeWithoutResponse: c.isWritableWithoutResponse
162
+ });
163
+ }
130
164
  }
131
165
  const sub = m.onDeviceDisconnected(id, () => {
132
166
  sub.remove();
@@ -1 +1 @@
1
- {"version":3,"file":"bleTransport.js","sources":["../../../src/features/devices/bleTransport.ts"],"sourcesContent":["/**\n * The phone's Bluetooth radio, through react-native-ble-plx: is it on and\n * allowed, scanning for health devices, and one connected device as a\n * GattLink for the drivers (drivers.ts).\n *\n * The library is loaded with a dynamic import: it creates its native object\n * when first imported, and a JavaScript update reaching an app binary built\n * before Bluetooth was added would crash at startup on a static import. Here it\n * only means \"not available in this build\".\n */\nimport { PermissionsAndroid, Platform } from 'react-native';\nimport type { BleManager, Device } from 'react-native-ble-plx';\nimport { kindForServices } from './catalog';\nimport { describeBleError, type BleTransport, type ConnectedLink, type RadioState } from './link';\nimport { fromBase64, toBase64 } from './gatt/bytes';\nimport { sameUuid } from './gatt/uuids';\n\nexport type { BleTransport, ConnectedLink, FoundDevice, RadioState } from './link';\n\nconst CONNECT_TIMEOUT_MS = 15_000;\n\nfunction radioState(state: string): RadioState {\n switch (state) {\n case 'PoweredOn':\n return 'ready';\n case 'PoweredOff':\n return 'off';\n case 'Unauthorized':\n return 'unauthorized';\n case 'Unsupported':\n return 'unsupported';\n default:\n return 'unknown';\n }\n}\n\nlet manager: Promise<BleManager | null> | null = null;\n\nfunction getManager(): Promise<BleManager | null> {\n if (!manager) {\n manager = import('react-native-ble-plx').then((m) => new m.BleManager()).catch(() => null);\n }\n return manager;\n}\n\nfunction linkFor(m: BleManager, device: Device, offered: Set<string>): ConnectedLink {\n const has = (service: string, characteristic?: string) =>\n [...offered].some((entry) => {\n const [s, c] = entry.split('|');\n return (\n sameUuid(s, service) &&\n (characteristic === undefined || (c !== undefined && sameUuid(c, characteristic)))\n );\n });\n return {\n id: device.id,\n name: device.name ?? device.localName ?? 'Bluetooth device',\n has,\n monitor(service, characteristic, onValue) {\n const sub = device.monitorCharacteristicForService(service, characteristic, (error, c) => {\n // A monitor error is the connection ending; onDisconnect tells the screen.\n if (error || !c?.value) return;\n onValue(fromBase64(c.value));\n });\n return () => sub.remove();\n },\n async write(service, characteristic, bytes) {\n await device.writeCharacteristicWithResponseForService(service, characteristic, toBase64(bytes));\n },\n async read(service, characteristic) {\n const c = await device.readCharacteristicForService(service, characteristic);\n return fromBase64(c.value);\n },\n async close() {\n await m.cancelDeviceConnection(device.id).catch(() => undefined);\n },\n };\n}\n\nexport async function createBleTransport(): Promise<BleTransport | null> {\n const m = await getManager();\n if (!m) return null;\n return {\n async radio() {\n try {\n return radioState(await m.state());\n } catch {\n return 'unknown';\n }\n },\n\n onRadioChange(listener) {\n const sub = m.onStateChange((state) => listener(radioState(state)), true);\n return () => sub.remove();\n },\n\n async requestPermissions() {\n if (Platform.OS !== 'android') return true;\n const P = PermissionsAndroid.PERMISSIONS;\n const wanted =\n Number(Platform.Version) >= 31 ? [P.BLUETOOTH_SCAN, P.BLUETOOTH_CONNECT] : [P.ACCESS_FINE_LOCATION];\n const result = await PermissionsAndroid.requestMultiple(wanted);\n return wanted.every((p) => result[p] === PermissionsAndroid.RESULTS.GRANTED);\n },\n\n scan(services, onFound, onError) {\n const seen = new Set<string>();\n void m\n .startDeviceScan([...services], { allowDuplicates: false }, (error, device) => {\n if (error) {\n onError(describeBleError(error));\n return;\n }\n if (!device || seen.has(device.id)) return;\n seen.add(device.id);\n onFound({\n id: device.id,\n name: device.localName ?? device.name ?? 'Bluetooth device',\n rssi: device.rssi ?? undefined,\n kind: kindForServices(device.serviceUUIDs),\n });\n })\n .catch((err: unknown) => onError(describeBleError(err)));\n return () => {\n void m.stopDeviceScan();\n };\n },\n\n async connect(id, name, onDisconnect) {\n await m.stopDeviceScan();\n const device = await m.connectToDevice(id, { timeout: CONNECT_TIMEOUT_MS });\n await device.discoverAllServicesAndCharacteristics();\n if (Platform.OS === 'android') {\n // Body composition packets are longer than the default 20 bytes.\n await device.requestMTU(185).catch(() => undefined);\n }\n const offered = new Set<string>();\n for (const service of await device.services()) {\n offered.add(service.uuid);\n for (const c of await service.characteristics()) offered.add(`${service.uuid}|${c.uuid}`);\n }\n const sub = m.onDeviceDisconnected(id, () => {\n sub.remove();\n onDisconnect();\n });\n const link = linkFor(m, device, offered);\n return { ...link, name: device.name ?? device.localName ?? name };\n },\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bleTransport.js","sources":["../../../src/features/devices/bleTransport.ts"],"sourcesContent":["/**\n * The phone's Bluetooth radio, through react-native-ble-plx: is it on and\n * allowed, scanning for health devices, and one connected device as a\n * GattLink for the drivers (drivers.ts).\n *\n * The library is loaded with a dynamic import: it creates its native object\n * when first imported, and a JavaScript update reaching an app binary built\n * before Bluetooth was added would crash at startup on a static import. Here it\n * only means \"not available in this build\".\n */\nimport { PermissionsAndroid, Platform } from 'react-native';\nimport type { BleManager, Device } from 'react-native-ble-plx';\nimport { kindForServices } from './catalog';\nimport {\n describeBleError,\n type Advertisement,\n type BleTransport,\n type ConnectedLink,\n type RadioState,\n} from './link';\nimport { fromBase64, toBase64 } from './gatt/bytes';\nimport { sameUuid } from './gatt/uuids';\n\nexport type { BleTransport, ConnectedLink, FoundDevice, RadioState } from './link';\n\nconst CONNECT_TIMEOUT_MS = 15_000;\n\nfunction radioState(state: string): RadioState {\n switch (state) {\n case 'PoweredOn':\n return 'ready';\n case 'PoweredOff':\n return 'off';\n case 'Unauthorized':\n return 'unauthorized';\n case 'Unsupported':\n return 'unsupported';\n default:\n return 'unknown';\n }\n}\n\nlet manager: Promise<BleManager | null> | null = null;\n\nfunction getManager(): Promise<BleManager | null> {\n if (!manager) {\n manager = import('react-native-ble-plx').then((m) => new m.BleManager()).catch(() => null);\n }\n return manager;\n}\n\nfunction advertisementOf(device: Device): Advertisement {\n const serviceData: Record<string, Uint8Array> = {};\n for (const [uuid, value] of Object.entries(device.serviceData ?? {})) serviceData[uuid] = fromBase64(value);\n return {\n id: device.id,\n name: device.localName ?? device.name ?? undefined,\n rssi: device.rssi ?? undefined,\n manufacturerData: device.manufacturerData ? fromBase64(device.manufacturerData) : undefined,\n serviceUUIDs: device.serviceUUIDs ?? undefined,\n serviceData,\n };\n}\n\ninterface Offered {\n service: string;\n characteristic?: string;\n writeWithoutResponse?: boolean;\n}\n\nfunction linkFor(m: BleManager, device: Device, offered: readonly Offered[]): ConnectedLink {\n const find = (service: string, characteristic?: string) =>\n offered.find(\n (o) =>\n sameUuid(o.service, service) &&\n (characteristic === undefined || (o.characteristic !== undefined && sameUuid(o.characteristic, characteristic))),\n );\n return {\n id: device.id,\n name: device.name ?? device.localName ?? 'Bluetooth device',\n has: (service, characteristic) => find(service, characteristic) !== undefined,\n canWriteWithoutResponse: (service, characteristic) => find(service, characteristic)?.writeWithoutResponse === true,\n monitor(service, characteristic, onValue) {\n const sub = device.monitorCharacteristicForService(service, characteristic, (error, c) => {\n // A monitor error is the connection ending; onDisconnect tells the screen.\n if (error || !c?.value) return;\n onValue(fromBase64(c.value));\n });\n return () => sub.remove();\n },\n async write(service, characteristic, bytes, options) {\n if (options?.withoutResponse) {\n await device.writeCharacteristicWithoutResponseForService(service, characteristic, toBase64(bytes));\n } else {\n await device.writeCharacteristicWithResponseForService(service, characteristic, toBase64(bytes));\n }\n },\n async read(service, characteristic) {\n const c = await device.readCharacteristicForService(service, characteristic);\n return fromBase64(c.value);\n },\n async close() {\n await m.cancelDeviceConnection(device.id).catch(() => undefined);\n },\n };\n}\n\nexport async function createBleTransport(): Promise<BleTransport | null> {\n const m = await getManager();\n if (!m) return null;\n return {\n async radio() {\n try {\n return radioState(await m.state());\n } catch {\n return 'unknown';\n }\n },\n\n onRadioChange(listener) {\n const sub = m.onStateChange((state) => listener(radioState(state)), true);\n return () => sub.remove();\n },\n\n async requestPermissions() {\n if (Platform.OS !== 'android') return true;\n const P = PermissionsAndroid.PERMISSIONS;\n const wanted =\n Number(Platform.Version) >= 31 ? [P.BLUETOOTH_SCAN, P.BLUETOOTH_CONNECT] : [P.ACCESS_FINE_LOCATION];\n const result = await PermissionsAndroid.requestMultiple(wanted);\n return wanted.every((p) => result[p] === PermissionsAndroid.RESULTS.GRANTED);\n },\n\n scan(request, onFound, onError) {\n const seen = new Set<string>();\n // Brand devices advertise no standard service: listen to everything and let the matcher decide.\n const filter = request.match ? null : [...request.services];\n void m\n .startDeviceScan(filter, { allowDuplicates: Boolean(request.onAdvertisement) }, (error, device) => {\n if (error) {\n onError(describeBleError(error));\n return;\n }\n if (!device) return;\n const adv = advertisementOf(device);\n request.onAdvertisement?.(adv);\n if (seen.has(device.id)) return;\n const standard = kindForServices(adv.serviceUUIDs);\n const wantsStandard =\n standard !== undefined &&\n (adv.serviceUUIDs ?? []).some((u) => request.services.some((s) => sameUuid(u, s)));\n const brand = wantsStandard ? undefined : request.match?.(adv);\n if (!wantsStandard && !brand && request.match) return;\n seen.add(device.id);\n onFound({\n id: device.id,\n name: adv.name ?? 'Bluetooth device',\n rssi: adv.rssi,\n kind: standard,\n ...brand,\n });\n })\n .catch((err: unknown) => onError(describeBleError(err)));\n return () => {\n void m.stopDeviceScan();\n };\n },\n\n async connect(id, name, onDisconnect) {\n await m.stopDeviceScan();\n const device = await m.connectToDevice(id, { timeout: CONNECT_TIMEOUT_MS });\n await device.discoverAllServicesAndCharacteristics();\n if (Platform.OS === 'android') {\n // Body composition packets are longer than the default 20 bytes.\n await device.requestMTU(185).catch(() => undefined);\n }\n const offered: Offered[] = [];\n for (const service of await device.services()) {\n offered.push({ service: service.uuid });\n for (const c of await service.characteristics()) {\n offered.push({\n service: service.uuid,\n characteristic: c.uuid,\n writeWithoutResponse: c.isWritableWithoutResponse,\n });\n }\n }\n const sub = m.onDeviceDisconnected(id, () => {\n sub.remove();\n onDisconnect();\n });\n const link = linkFor(m, device, offered);\n return { ...link, name: device.name ?? device.localName ?? name };\n },\n };\n}\n"],"names":["_a"],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import {FrameAssembler,renphoFrame}from'./frames.js';function openRenphoChannel(link, uuids, onFrame) {
2
+ var _a, _b;
3
+ const assembler = new FrameAssembler();
4
+ const withoutResponse = (_b = (_a = link.canWriteWithoutResponse) == null ? void 0 : _a.call(link, uuids.service, uuids.write)) != null ? _b : false;
5
+ const unsub = link.monitor(uuids.service, uuids.notify, (bytes) => assembler.push(bytes, onFrame));
6
+ return {
7
+ async send(cmd, payload) {
8
+ await link.write(uuids.service, uuids.write, renphoFrame(cmd, payload), {
9
+ withoutResponse
10
+ });
11
+ },
12
+ async sendRaw(bytes) {
13
+ await link.write(uuids.service, uuids.write, bytes, {
14
+ withoutResponse
15
+ });
16
+ },
17
+ stop: unsub
18
+ };
19
+ }export{openRenphoChannel};//# sourceMappingURL=channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel.js","sources":["../../../../src/features/devices/brands/channel.ts"],"sourcesContent":["/**\n * The piece every RENPHO common-frame device shares: subscribe to the notify\n * characteristic, reassemble frames that span notifications, and send framed\n * commands on the write characteristic.\n */\nimport type { GattLink } from '../drivers';\nimport { FrameAssembler, renphoFrame, type Frame } from './frames';\n\nexport interface RenphoChannel {\n /** Send a common frame (`55 AA cmd len payload sum`). */\n send(cmd: number, payload?: ArrayLike<number>): Promise<void>;\n /** Send bytes exactly as given (already-framed, or a raw protocol like the rope's). */\n sendRaw(bytes: number[]): Promise<void>;\n stop(): void;\n}\n\nexport function openRenphoChannel(\n link: GattLink,\n uuids: { service: string; write: string; notify: string },\n onFrame: (frame: Frame) => void,\n): RenphoChannel {\n const assembler = new FrameAssembler();\n const withoutResponse = link.canWriteWithoutResponse?.(uuids.service, uuids.write) ?? false;\n const unsub = link.monitor(uuids.service, uuids.notify, (bytes) => assembler.push(bytes, onFrame));\n return {\n async send(cmd, payload) {\n await link.write(uuids.service, uuids.write, renphoFrame(cmd, payload), { withoutResponse });\n },\n async sendRaw(bytes) {\n await link.write(uuids.service, uuids.write, bytes, { withoutResponse });\n },\n stop: unsub,\n };\n}\n"],"names":[],"mappings":"qDAcgB,SAAA,iBAAA,CAAkB,IAAgB,EAAA,KAAA,EAI/C,OAAgD,EAAA;AAlBnD,EAAA,IAAA,EAAA,EAAA,EAAA;AAmBE,EAAM,MAAA,SAAA,GAAY,IAAI,cAAe,EAAA;AACrC,EAAM,MAAA,eAAA,GAAA,CAAkB,gBAAK,uBAAL,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,IAAA,EAA+B,MAAM,OAAS,EAAA,KAAA,CAAM,WAApD,IAA8D,GAAA,EAAA,GAAA,KAAA;AACtF,EAAA,MAAM,KAAQ,GAAA,IAAA,CAAK,OAAQ,CAAA,KAAA,CAAM,OAAS,EAAA,KAAA,CAAM,MAAQ,EAAA,CAAA,KAAA,KAAS,SAAU,CAAA,IAAA,CAAK,KAAO,EAAA,OAAO,CAAC,CAAA;AAC/F,EAAO,OAAA;AAAA,IACL,MAAM,IAAK,CAAA,GAAA,EAAK,OAAS,EAAA;AACvB,MAAM,MAAA,IAAA,CAAK,MAAM,KAAM,CAAA,OAAA,EAAS,MAAM,KAAO,EAAA,WAAA,CAAY,GAAK,EAAA,OAAO,CAAG,EAAA;AAAA,QACtE;AAAA,OACD,CAAA;AAAA,KACH;AAAA,IACA,MAAM,QAAQ,KAAO,EAAA;AACnB,MAAA,MAAM,KAAK,KAAM,CAAA,KAAA,CAAM,OAAS,EAAA,KAAA,CAAM,OAAO,KAAO,EAAA;AAAA,QAClD;AAAA,OACD,CAAA;AAAA,KACH;AAAA,IACA,IAAM,EAAA;AAAA,GACR;AACF"}
@@ -0,0 +1,318 @@
1
+ import {u16be}from'./frames.js';var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ const RENPHO_TYPES = {
21
+ // Body scales -------------------------------------------------------------
22
+ 2: {
23
+ kind: "scale",
24
+ protocol: "renpho-scale"
25
+ },
26
+ // old Lefu-style
27
+ 3: {
28
+ kind: "scale",
29
+ protocol: "renpho-scale"
30
+ },
31
+ // ES-24M, R-A001/2/9
32
+ 16: {
33
+ kind: "scale",
34
+ protocol: "renpho-scale"
35
+ },
36
+ 63: {
37
+ kind: "scale",
38
+ protocol: "renpho-scale"
39
+ },
40
+ // R-A033
41
+ 32: {
42
+ kind: "scale",
43
+ protocol: "renpho-scale"
44
+ },
45
+ // common-device scale
46
+ 33: {
47
+ kind: "scale",
48
+ protocol: "renpho-scale"
49
+ },
50
+ 34: {
51
+ kind: "scale",
52
+ protocol: "renpho-a017-scale"
53
+ },
54
+ // R-A017
55
+ 46: {
56
+ kind: "scale",
57
+ protocol: "renpho-a017-scale"
58
+ },
59
+ // R-A017 (alt id)
60
+ 44: {
61
+ kind: "scale",
62
+ protocol: "renpho-a017-scale"
63
+ },
64
+ // ES-CS20M
65
+ 48: {
66
+ kind: "scale",
67
+ protocol: "renpho-a017-scale"
68
+ },
69
+ // ES-32MD
70
+ 49: {
71
+ kind: "scale",
72
+ protocol: "renpho-a017-scale"
73
+ },
74
+ 59: {
75
+ kind: "scale",
76
+ protocol: "renpho-a017-scale"
77
+ },
78
+ // ES-32MD (alt id)
79
+ 62: {
80
+ kind: "scale",
81
+ protocol: "renpho-a017-scale"
82
+ },
83
+ // ES-CS20M (alt id)
84
+ 51: {
85
+ kind: "scale",
86
+ protocol: "renpho-a017-scale"
87
+ },
88
+ // R-A031
89
+ 54: {
90
+ kind: "scale",
91
+ protocol: "renpho-a017-scale"
92
+ },
93
+ // R-MSB05
94
+ 52: {
95
+ kind: "scale",
96
+ protocol: "renpho-a017-scale"
97
+ },
98
+ // R-A018
99
+ 38: {
100
+ kind: "scale",
101
+ protocol: "renpho-a017-scale"
102
+ },
103
+ // R-MSB02
104
+ 50: {
105
+ kind: "scale",
106
+ protocol: "renpho-a017-scale"
107
+ },
108
+ // R-MSB04
109
+ // Blood pressure ----------------------------------------------------------
110
+ 58: {
111
+ kind: "blood-pressure",
112
+ protocol: "renpho-blood-pressure"
113
+ },
114
+ // TMB-2276-B
115
+ // Food (kitchen) scales ---------------------------------------------------
116
+ 23: {
117
+ kind: "kitchen-scale",
118
+ protocol: "renpho-food-scale"
119
+ },
120
+ 42: {
121
+ kind: "kitchen-scale",
122
+ protocol: "renpho-food-scale"
123
+ },
124
+ 43: {
125
+ kind: "kitchen-scale",
126
+ protocol: "renpho-food-scale"
127
+ },
128
+ 57: {
129
+ kind: "kitchen-scale",
130
+ protocol: "renpho-food-scale"
131
+ },
132
+ 60: {
133
+ kind: "kitchen-scale",
134
+ protocol: "renpho-food-scale"
135
+ },
136
+ // R-MSK03 nutrition scale
137
+ // Tape measures -----------------------------------------------------------
138
+ 10: {
139
+ kind: "tape",
140
+ protocol: "renpho-tape"
141
+ },
142
+ // ES-Tape
143
+ 25: {
144
+ kind: "tape",
145
+ protocol: "renpho-tape"
146
+ },
147
+ // R-Y001
148
+ 36: {
149
+ kind: "tape",
150
+ protocol: "renpho-tape"
151
+ },
152
+ // R-MTX01
153
+ 70: {
154
+ kind: "tape",
155
+ protocol: "renpho-tape"
156
+ },
157
+ // RF-BMF01
158
+ 27: {
159
+ kind: "tape",
160
+ protocol: "renpho-tape-new"
161
+ },
162
+ // R-Y002 family
163
+ 35: {
164
+ kind: "tape",
165
+ protocol: "renpho-tape-new"
166
+ },
167
+ // R-Y002
168
+ // Jump ropes --------------------------------------------------------------
169
+ 1: {
170
+ kind: "jump-rope",
171
+ protocol: "renpho-jump-rope"
172
+ },
173
+ // RENPHO-ROPE-R1
174
+ 11: {
175
+ kind: "jump-rope",
176
+ protocol: "renpho-jump-rope"
177
+ },
178
+ // R-Q008
179
+ 24: {
180
+ kind: "jump-rope",
181
+ protocol: "renpho-jump-rope"
182
+ },
183
+ // R-Q011
184
+ 61: {
185
+ kind: "jump-rope",
186
+ protocol: "renpho-jump-rope"
187
+ },
188
+ // R-FAR01
189
+ // Treadmill ---------------------------------------------------------------
190
+ 6: {
191
+ kind: "treadmill",
192
+ protocol: "renpho-treadmill"
193
+ },
194
+ // R-Q004 (FTMS)
195
+ // Smart ring --------------------------------------------------------------
196
+ 45: {
197
+ kind: "ring",
198
+ protocol: "renpho-ring"
199
+ },
200
+ // Massagers / fascia guns / foot machines: recognised, nothing to store ---
201
+ 29: {
202
+ kind: "scale",
203
+ protocol: "renpho-control-only"
204
+ },
205
+ // R-C001S fascia gun
206
+ 47: {
207
+ kind: "scale",
208
+ protocol: "renpho-control-only"
209
+ },
210
+ 53: {
211
+ kind: "scale",
212
+ protocol: "renpho-control-only"
213
+ },
214
+ 55: {
215
+ kind: "scale",
216
+ protocol: "renpho-control-only"
217
+ },
218
+ 64: {
219
+ kind: "scale",
220
+ protocol: "renpho-control-only"
221
+ },
222
+ 65: {
223
+ kind: "scale",
224
+ protocol: "renpho-control-only"
225
+ },
226
+ 67: {
227
+ kind: "scale",
228
+ protocol: "renpho-control-only"
229
+ },
230
+ 72: {
231
+ kind: "scale",
232
+ protocol: "renpho-control-only"
233
+ },
234
+ 73: {
235
+ kind: "scale",
236
+ protocol: "renpho-control-only"
237
+ }
238
+ };
239
+ function macOf(bytes, start) {
240
+ return Array.from(bytes.subarray(start, start + 6)).map((b) => b.toString(16).padStart(2, "0").toUpperCase()).join(":");
241
+ }
242
+ function detectBrand(adv) {
243
+ var _a, _b;
244
+ const md = adv.manufacturerData;
245
+ const name = (_a = adv.name) != null ? _a : "";
246
+ if (md && md.length === 12 && md[0] === 241 && md[1] === 255) {
247
+ return {
248
+ kind: "ring",
249
+ protocol: "renpho-ring",
250
+ deviceType: 45,
251
+ mac: macOf(md, 4),
252
+ model: name.split(" ")[0] || void 0
253
+ };
254
+ }
255
+ if (md && md.length >= 14 && md[0] === 16 && md[1] === 26) {
256
+ const deviceType = u16be(md, 4);
257
+ const info = RENPHO_TYPES[deviceType];
258
+ if (info) {
259
+ return __spreadProps(__spreadValues({}, identity(info)), {
260
+ deviceType,
261
+ mac: macOf(md, 6),
262
+ model: name || void 0
263
+ });
264
+ }
265
+ }
266
+ if (md && md.length >= 10) {
267
+ const info = RENPHO_TYPES[md[1]];
268
+ if (info && info.protocol === "renpho-jump-rope") {
269
+ return __spreadProps(__spreadValues({}, identity(info)), {
270
+ deviceType: md[1],
271
+ mac: macOf(md, 3)
272
+ });
273
+ }
274
+ }
275
+ const serviceData = adv.serviceData ? Object.values(adv.serviceData) : [];
276
+ const sd = serviceData[serviceData.length - 1];
277
+ if (sd && sd.length >= 24) {
278
+ const deviceType = u16be(sd, 2);
279
+ const info = RENPHO_TYPES[deviceType];
280
+ if (info && info.protocol === "renpho-scale") {
281
+ return __spreadProps(__spreadValues({}, identity(info)), {
282
+ deviceType,
283
+ mac: macOf(sd, 5),
284
+ model: cString(sd, 12, 12) || void 0
285
+ });
286
+ }
287
+ }
288
+ const services = ((_b = adv.serviceUUIDs) != null ? _b : []).map((u) => u.toLowerCase());
289
+ const hasQnService = services.some((u) => u.includes("ffe0") || u.includes("fff0") || u.includes("feb3"));
290
+ const qnName = /^QN-/.test(name) || /^Yolanda/i.test(name) || name === "QN-Scale" || name === "QN-Scale1";
291
+ const qnMd = md && md.length >= 10 && (md[0] === 255 && md[1] === 255 || md[0] === 168 && md[1] === 1);
292
+ if ((hasQnService || qnName) && (qnMd || hasQnService)) {
293
+ return {
294
+ kind: "scale",
295
+ protocol: "qn-scale",
296
+ model: name || void 0
297
+ };
298
+ }
299
+ return void 0;
300
+ }
301
+ function identity(info) {
302
+ return info.protocol === "renpho-control-only" ? {
303
+ protocol: info.protocol
304
+ } : {
305
+ kind: info.kind,
306
+ protocol: info.protocol
307
+ };
308
+ }
309
+ function cString(bytes, start, maxLen) {
310
+ let s = "";
311
+ for (let i = start; i < start + maxLen && i < bytes.length; i++) {
312
+ const c = bytes[i];
313
+ if (c === 0) break;
314
+ if (c < 32 || c > 126) return "";
315
+ s += String.fromCharCode(c);
316
+ }
317
+ return s;
318
+ }export{detectBrand};//# sourceMappingURL=detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.js","sources":["../../../../src/features/devices/brands/detect.ts"],"sourcesContent":["/**\n * Which brand protocol a device speaks, worked out from its advertisement the\n * way RENPHO's own app does it (RHBLEBroadcastParserFactory, QNPScaleManager\n * centralDiscoverPeripheral; ~/Downloads/renpho-rea/PROTOCOLS.md section 1).\n *\n * A brand device does not advertise the Bluetooth SIG service of its kind, so\n * the scan would never surface it on the standard profiles. Here we recognise\n * it by its manufacturer data, service data or name and say which protocol and\n * which kind of device it is, so the session can pick the right driver.\n */\nimport type { DeviceKindId } from '../catalog';\nimport type { Advertisement, DeviceIdentity } from '../link';\nimport { u16be } from './frames';\n\nexport type DeviceProtocolId =\n /** RENPHO common-frame devices (newer, \"10 1A\" manufacturer data). */\n | 'renpho-scale'\n | 'renpho-a017-scale'\n | 'renpho-blood-pressure'\n | 'renpho-food-scale'\n | 'renpho-tape'\n | 'renpho-tape-new'\n | 'renpho-jump-rope'\n | 'renpho-ring'\n | 'renpho-treadmill'\n /** QN / Yolanda scales. */\n | 'qn-scale'\n /** Recognised but no reading to store (massagers, foot machines). */\n | 'renpho-control-only';\n\n/** The RENPHO \"device type\" byte -> what the device is and how Yantra talks to it. */\ninterface TypeInfo {\n kind: DeviceKindId;\n protocol: DeviceProtocolId;\n}\n\n/**\n * RENPHO device types, from RHBLEDeviceModel.commHandler (the switch that maps\n * each type to its handler) and allowDeviceList.json (which model is which\n * type). Only the kinds Yantra reads get a real driver; the massagers are\n * control-only.\n */\nconst RENPHO_TYPES: Record<number, TypeInfo> = {\n // Body scales -------------------------------------------------------------\n 0x02: { kind: 'scale', protocol: 'renpho-scale' }, // old Lefu-style\n 0x03: { kind: 'scale', protocol: 'renpho-scale' }, // ES-24M, R-A001/2/9\n 0x10: { kind: 'scale', protocol: 'renpho-scale' },\n 0x3f: { kind: 'scale', protocol: 'renpho-scale' }, // R-A033\n 0x20: { kind: 'scale', protocol: 'renpho-scale' }, // common-device scale\n 0x21: { kind: 'scale', protocol: 'renpho-scale' },\n 0x22: { kind: 'scale', protocol: 'renpho-a017-scale' }, // R-A017\n 0x2e: { kind: 'scale', protocol: 'renpho-a017-scale' }, // R-A017 (alt id)\n 0x2c: { kind: 'scale', protocol: 'renpho-a017-scale' }, // ES-CS20M\n 0x30: { kind: 'scale', protocol: 'renpho-a017-scale' }, // ES-32MD\n 0x31: { kind: 'scale', protocol: 'renpho-a017-scale' },\n 0x3b: { kind: 'scale', protocol: 'renpho-a017-scale' }, // ES-32MD (alt id)\n 0x3e: { kind: 'scale', protocol: 'renpho-a017-scale' }, // ES-CS20M (alt id)\n 0x33: { kind: 'scale', protocol: 'renpho-a017-scale' }, // R-A031\n 0x36: { kind: 'scale', protocol: 'renpho-a017-scale' }, // R-MSB05\n 0x34: { kind: 'scale', protocol: 'renpho-a017-scale' }, // R-A018\n 0x26: { kind: 'scale', protocol: 'renpho-a017-scale' }, // R-MSB02\n 0x32: { kind: 'scale', protocol: 'renpho-a017-scale' }, // R-MSB04\n // Blood pressure ----------------------------------------------------------\n 0x3a: { kind: 'blood-pressure', protocol: 'renpho-blood-pressure' }, // TMB-2276-B\n // Food (kitchen) scales ---------------------------------------------------\n 0x17: { kind: 'kitchen-scale', protocol: 'renpho-food-scale' },\n 0x2a: { kind: 'kitchen-scale', protocol: 'renpho-food-scale' },\n 0x2b: { kind: 'kitchen-scale', protocol: 'renpho-food-scale' },\n 0x39: { kind: 'kitchen-scale', protocol: 'renpho-food-scale' },\n 0x3c: { kind: 'kitchen-scale', protocol: 'renpho-food-scale' }, // R-MSK03 nutrition scale\n // Tape measures -----------------------------------------------------------\n 0x0a: { kind: 'tape', protocol: 'renpho-tape' }, // ES-Tape\n 0x19: { kind: 'tape', protocol: 'renpho-tape' }, // R-Y001\n 0x24: { kind: 'tape', protocol: 'renpho-tape' }, // R-MTX01\n 0x46: { kind: 'tape', protocol: 'renpho-tape' }, // RF-BMF01\n 0x1b: { kind: 'tape', protocol: 'renpho-tape-new' }, // R-Y002 family\n 0x23: { kind: 'tape', protocol: 'renpho-tape-new' }, // R-Y002\n // Jump ropes --------------------------------------------------------------\n 0x01: { kind: 'jump-rope', protocol: 'renpho-jump-rope' }, // RENPHO-ROPE-R1\n 0x0b: { kind: 'jump-rope', protocol: 'renpho-jump-rope' }, // R-Q008\n 0x18: { kind: 'jump-rope', protocol: 'renpho-jump-rope' }, // R-Q011\n 0x3d: { kind: 'jump-rope', protocol: 'renpho-jump-rope' }, // R-FAR01\n // Treadmill ---------------------------------------------------------------\n 0x06: { kind: 'treadmill', protocol: 'renpho-treadmill' }, // R-Q004 (FTMS)\n // Smart ring --------------------------------------------------------------\n 0x2d: { kind: 'ring', protocol: 'renpho-ring' },\n // Massagers / fascia guns / foot machines: recognised, nothing to store ---\n 0x1d: { kind: 'scale', protocol: 'renpho-control-only' }, // R-C001S fascia gun\n 0x2f: { kind: 'scale', protocol: 'renpho-control-only' },\n 0x35: { kind: 'scale', protocol: 'renpho-control-only' },\n 0x37: { kind: 'scale', protocol: 'renpho-control-only' },\n 0x40: { kind: 'scale', protocol: 'renpho-control-only' },\n 0x41: { kind: 'scale', protocol: 'renpho-control-only' },\n 0x43: { kind: 'scale', protocol: 'renpho-control-only' },\n 0x48: { kind: 'scale', protocol: 'renpho-control-only' },\n 0x49: { kind: 'scale', protocol: 'renpho-control-only' },\n};\n\n/** Six bytes -> \"AA:BB:CC:DD:EE:FF\". */\nfunction macOf(bytes: Uint8Array, start: number): string {\n return Array.from(bytes.subarray(start, start + 6))\n .map((b) => b.toString(16).padStart(2, '0').toUpperCase())\n .join(':');\n}\n\n/** Only these kinds have a Yantra driver (control-only devices store nothing). */\nexport function kindForProtocol(protocol: DeviceProtocolId): DeviceKindId | undefined {\n if (protocol === 'renpho-control-only') return undefined;\n if (protocol === 'qn-scale') return 'scale';\n return RENPHO_TYPES[typeForProtocolSample(protocol)]?.kind;\n}\n\n// A protocol maps to one kind; pick any type that uses it to look the kind up.\nfunction typeForProtocolSample(protocol: DeviceProtocolId): number {\n for (const [type, info] of Object.entries(RENPHO_TYPES)) {\n if (info.protocol === protocol) return Number(type);\n }\n return -1;\n}\n\n/**\n * Recognise a brand device from one advertisement. Returns what it is, or\n * undefined when it is not a brand device Yantra knows (a standard-profile\n * device is matched elsewhere, by its advertised service).\n */\nexport function detectBrand(adv: Advertisement): DeviceIdentity | undefined {\n const md = adv.manufacturerData;\n const name = adv.name ?? '';\n\n // Smart ring: 12-byte manufacturer data starting F1 FF.\n if (md && md.length === 12 && md[0] === 0xf1 && md[1] === 0xff) {\n return {\n kind: 'ring',\n protocol: 'renpho-ring',\n deviceType: 0x2d,\n mac: macOf(md, 4),\n model: name.split(' ')[0] || undefined,\n };\n }\n\n // Newer RENPHO devices: \"10 1A\" manufacturer data with the device type inside.\n if (md && md.length >= 14 && md[0] === 0x10 && md[1] === 0x1a) {\n const deviceType = u16be(md, 4);\n const info = RENPHO_TYPES[deviceType];\n if (info) {\n return { ...identity(info), deviceType, mac: macOf(md, 6), model: name || undefined };\n }\n }\n\n // Jump rope: manufacturer data [manufacturer, deviceType, _, MAC(6)].\n if (md && md.length >= 10) {\n const info = RENPHO_TYPES[md[1]];\n if (info && info.protocol === 'renpho-jump-rope') {\n return { ...identity(info), deviceType: md[1], mac: macOf(md, 3) };\n }\n }\n\n // Old Lefu-style scale: service data, last value, >= 24 bytes.\n const serviceData = adv.serviceData ? Object.values(adv.serviceData) : [];\n const sd = serviceData[serviceData.length - 1];\n if (sd && sd.length >= 24) {\n const deviceType = u16be(sd, 2);\n const info = RENPHO_TYPES[deviceType];\n if (info && info.protocol === 'renpho-scale') {\n return {\n ...identity(info),\n deviceType,\n mac: macOf(sd, 5),\n model: cString(sd, 12, 12) || undefined,\n };\n }\n }\n\n // QN / Yolanda scale: a QN service, or a QN-style name with FF FF / A8 01 data.\n const services = (adv.serviceUUIDs ?? []).map((u) => u.toLowerCase());\n const hasQnService = services.some((u) => u.includes('ffe0') || u.includes('fff0') || u.includes('feb3'));\n const qnName = /^QN-/.test(name) || /^Yolanda/i.test(name) || name === 'QN-Scale' || name === 'QN-Scale1';\n const qnMd = md && md.length >= 10 && ((md[0] === 0xff && md[1] === 0xff) || (md[0] === 0xa8 && md[1] === 0x01));\n if ((hasQnService || qnName) && (qnMd || hasQnService)) {\n return { kind: 'scale', protocol: 'qn-scale', model: name || undefined };\n }\n\n return undefined;\n}\n\nfunction identity(info: TypeInfo): DeviceIdentity {\n return info.protocol === 'renpho-control-only'\n ? { protocol: info.protocol }\n : { kind: info.kind, protocol: info.protocol };\n}\n\n/** A NUL-terminated ASCII string inside a byte range. */\nfunction cString(bytes: Uint8Array, start: number, maxLen: number): string {\n let s = '';\n for (let i = start; i < start + maxLen && i < bytes.length; i++) {\n const c = bytes[i];\n if (c === 0) break;\n if (c < 0x20 || c > 0x7e) return '';\n s += String.fromCharCode(c);\n }\n return s;\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AA8BA,MAAM,YAAyC,GAAA;AAAA;AAAA,EAE7C,CAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,CAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA,EACA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA,EACA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA,EACA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA;AAAA,EAGA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,gBAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA;AAAA,EAGA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,eAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA,EACA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,eAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA,EACA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,eAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA,EACA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,eAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA,EACA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,eAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA;AAAA,EAGA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,MAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,MAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,MAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,MAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,MAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,MAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA;AAAA,EAGA,CAAM,EAAA;AAAA,IACJ,IAAM,EAAA,WAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,WAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,WAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,WAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA;AAAA,EAGA,CAAM,EAAA;AAAA,IACJ,IAAM,EAAA,WAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA;AAAA,EAGA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,MAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA;AAAA,EAEA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA,EACA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA,EACA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA,EACA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA,EACA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA,EACA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA,EACA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA,GACZ;AAAA,EACA,EAAM,EAAA;AAAA,IACJ,IAAM,EAAA,OAAA;AAAA,IACN,QAAU,EAAA;AAAA;AAEd,CAAA;AAGA,SAAS,KAAA,CAAM,OAAmB,KAAuB,EAAA;AACvD,EAAO,OAAA,KAAA,CAAM,KAAK,KAAM,CAAA,QAAA,CAAS,OAAO,KAAQ,GAAA,CAAC,CAAC,CAAA,CAAE,GAAI,CAAA,CAAA,CAAA,KAAK,EAAE,QAAS,CAAA,EAAE,CAAE,CAAA,QAAA,CAAS,CAAG,EAAA,GAAG,EAAE,WAAY,EAAC,CAAE,CAAA,IAAA,CAAK,GAAG,CAAA;AACtH;AAsBO,SAAS,YAAY,GAAgD,EAAA;AAnR5E,EAAA,IAAA,EAAA,EAAA,EAAA;AAoRE,EAAA,MAAM,KAAK,GAAI,CAAA,gBAAA;AACf,EAAM,MAAA,IAAA,GAAA,CAAO,EAAI,GAAA,GAAA,CAAA,IAAA,KAAJ,IAAY,GAAA,EAAA,GAAA,EAAA;AAGzB,EAAI,IAAA,EAAA,IAAM,EAAG,CAAA,MAAA,KAAW,EAAM,IAAA,EAAA,CAAG,CAAC,CAAA,KAAM,GAAQ,IAAA,EAAA,CAAG,CAAC,CAAA,KAAM,GAAM,EAAA;AAC9D,IAAO,OAAA;AAAA,MACL,IAAM,EAAA,MAAA;AAAA,MACN,QAAU,EAAA,aAAA;AAAA,MACV,UAAY,EAAA,EAAA;AAAA,MACZ,GAAA,EAAK,KAAM,CAAA,EAAA,EAAI,CAAC,CAAA;AAAA,MAChB,OAAO,IAAK,CAAA,KAAA,CAAM,GAAG,CAAA,CAAE,CAAC,CAAK,IAAA;AAAA,KAC/B;AAAA;AAIF,EAAI,IAAA,EAAA,IAAM,EAAG,CAAA,MAAA,IAAU,EAAM,IAAA,EAAA,CAAG,CAAC,CAAA,KAAM,EAAQ,IAAA,EAAA,CAAG,CAAC,CAAA,KAAM,EAAM,EAAA;AAC7D,IAAM,MAAA,UAAA,GAAa,KAAM,CAAA,EAAA,EAAI,CAAC,CAAA;AAC9B,IAAM,MAAA,IAAA,GAAO,aAAa,UAAU,CAAA;AACpC,IAAA,IAAI,IAAM,EAAA;AACR,MAAO,OAAA,aAAA,CAAA,cAAA,CAAA,EAAA,EACF,QAAS,CAAA,IAAI,CADX,CAAA,EAAA;AAAA,QAEL,UAAA;AAAA,QACA,GAAA,EAAK,KAAM,CAAA,EAAA,EAAI,CAAC,CAAA;AAAA,QAChB,OAAO,IAAQ,IAAA;AAAA,OACjB,CAAA;AAAA;AACF;AAIF,EAAI,IAAA,EAAA,IAAM,EAAG,CAAA,MAAA,IAAU,EAAI,EAAA;AACzB,IAAA,MAAM,IAAO,GAAA,YAAA,CAAa,EAAG,CAAA,CAAC,CAAC,CAAA;AAC/B,IAAI,IAAA,IAAA,IAAQ,IAAK,CAAA,QAAA,KAAa,kBAAoB,EAAA;AAChD,MAAO,OAAA,aAAA,CAAA,cAAA,CAAA,EAAA,EACF,QAAS,CAAA,IAAI,CADX,CAAA,EAAA;AAAA,QAEL,UAAA,EAAY,GAAG,CAAC,CAAA;AAAA,QAChB,GAAA,EAAK,KAAM,CAAA,EAAA,EAAI,CAAC;AAAA,OAClB,CAAA;AAAA;AACF;AAIF,EAAM,MAAA,WAAA,GAAc,IAAI,WAAc,GAAA,MAAA,CAAO,OAAO,GAAI,CAAA,WAAW,IAAI,EAAC;AACxE,EAAA,MAAM,EAAK,GAAA,WAAA,CAAY,WAAY,CAAA,MAAA,GAAS,CAAC,CAAA;AAC7C,EAAI,IAAA,EAAA,IAAM,EAAG,CAAA,MAAA,IAAU,EAAI,EAAA;AACzB,IAAM,MAAA,UAAA,GAAa,KAAM,CAAA,EAAA,EAAI,CAAC,CAAA;AAC9B,IAAM,MAAA,IAAA,GAAO,aAAa,UAAU,CAAA;AACpC,IAAI,IAAA,IAAA,IAAQ,IAAK,CAAA,QAAA,KAAa,cAAgB,EAAA;AAC5C,MAAO,OAAA,aAAA,CAAA,cAAA,CAAA,EAAA,EACF,QAAS,CAAA,IAAI,CADX,CAAA,EAAA;AAAA,QAEL,UAAA;AAAA,QACA,GAAA,EAAK,KAAM,CAAA,EAAA,EAAI,CAAC,CAAA;AAAA,QAChB,KAAO,EAAA,OAAA,CAAQ,EAAI,EAAA,EAAA,EAAI,EAAE,CAAK,IAAA;AAAA,OAChC,CAAA;AAAA;AACF;AAIF,EAAM,MAAA,QAAA,GAAA,CAAA,CAAY,EAAI,GAAA,GAAA,CAAA,YAAA,KAAJ,IAAoB,GAAA,EAAA,GAAA,IAAI,GAAI,CAAA,CAAA,CAAA,KAAK,CAAE,CAAA,WAAA,EAAa,CAAA;AAClE,EAAA,MAAM,YAAe,GAAA,QAAA,CAAS,IAAK,CAAA,CAAA,CAAA,KAAK,EAAE,QAAS,CAAA,MAAM,CAAK,IAAA,CAAA,CAAE,SAAS,MAAM,CAAA,IAAK,CAAE,CAAA,QAAA,CAAS,MAAM,CAAC,CAAA;AACtG,EAAM,MAAA,MAAA,GAAS,MAAO,CAAA,IAAA,CAAK,IAAI,CAAA,IAAK,WAAY,CAAA,IAAA,CAAK,IAAI,CAAA,IAAK,IAAS,KAAA,UAAA,IAAc,IAAS,KAAA,WAAA;AAC9F,EAAA,MAAM,OAAO,EAAM,IAAA,EAAA,CAAG,UAAU,EAAO,KAAA,EAAA,CAAG,CAAC,CAAM,KAAA,GAAA,IAAQ,GAAG,CAAC,CAAA,KAAM,OAAQ,EAAG,CAAA,CAAC,MAAM,GAAQ,IAAA,EAAA,CAAG,CAAC,CAAM,KAAA,CAAA,CAAA;AACvG,EAAK,IAAA,CAAA,YAAA,IAAgB,MAAY,MAAA,IAAA,IAAQ,YAAe,CAAA,EAAA;AACtD,IAAO,OAAA;AAAA,MACL,IAAM,EAAA,OAAA;AAAA,MACN,QAAU,EAAA,UAAA;AAAA,MACV,OAAO,IAAQ,IAAA;AAAA,KACjB;AAAA;AAEF,EAAO,OAAA,MAAA;AACT;AACA,SAAS,SAAS,IAAgC,EAAA;AAChD,EAAO,OAAA,IAAA,CAAK,aAAa,qBAAwB,GAAA;AAAA,IAC/C,UAAU,IAAK,CAAA;AAAA,GACb,GAAA;AAAA,IACF,MAAM,IAAK,CAAA,IAAA;AAAA,IACX,UAAU,IAAK,CAAA;AAAA,GACjB;AACF;AAGA,SAAS,OAAA,CAAQ,KAAmB,EAAA,KAAA,EAAe,MAAwB,EAAA;AACzE,EAAA,IAAI,CAAI,GAAA,EAAA;AACR,EAAS,KAAA,IAAA,CAAA,GAAI,OAAO,CAAI,GAAA,KAAA,GAAQ,UAAU,CAAI,GAAA,KAAA,CAAM,QAAQ,CAAK,EAAA,EAAA;AAC/D,IAAM,MAAA,CAAA,GAAI,MAAM,CAAC,CAAA;AACjB,IAAA,IAAI,MAAM,CAAG,EAAA;AACb,IAAA,IAAI,CAAI,GAAA,EAAA,IAAQ,CAAI,GAAA,GAAA,EAAa,OAAA,EAAA;AACjC,IAAK,CAAA,IAAA,MAAA,CAAO,aAAa,CAAC,CAAA;AAAA;AAE5B,EAAO,OAAA,CAAA;AACT"}
@@ -0,0 +1,103 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, key + "" , value);
4
+ function sum8(bytes, start = 0, end = bytes.length) {
5
+ let s = 0;
6
+ for (let i = start; i < end; i++) s = s + bytes[i] & 255;
7
+ return s;
8
+ }
9
+ const u16be = (b, i) => (b[i] << 8 | b[i + 1]) >>> 0;
10
+ const u32be = (b, i) => (b[i] << 24 | b[i + 1] << 16 | b[i + 2] << 8 | b[i + 3]) >>> 0;
11
+ const u16le = (b, i) => (b[i] | b[i + 1] << 8) >>> 0;
12
+ const u32le = (b, i) => (b[i] | b[i + 1] << 8 | b[i + 2] << 16 | b[i + 3] << 24) >>> 0;
13
+ const be16 = (n) => [n >> 8 & 255, n & 255];
14
+ const be32 = (n) => [n >>> 24 & 255, n >>> 16 & 255, n >>> 8 & 255, n & 255];
15
+ const le16 = (n) => [n & 255, n >> 8 & 255];
16
+ const le32 = (n) => [n & 255, n >>> 8 & 255, n >>> 16 & 255, n >>> 24 & 255];
17
+ function renphoFrame(cmd, payload = []) {
18
+ const body = [85, 170, cmd & 255, ...be16(payload.length), ...Array.from(payload)];
19
+ return [...body, sum8(body)];
20
+ }
21
+ function parseRenphoFrame(bytes) {
22
+ if (bytes.length < 6 || bytes[0] !== 85 || bytes[1] !== 170) return null;
23
+ const len = u16be(bytes, 3);
24
+ if (bytes.length !== len + 6) return null;
25
+ if (sum8(bytes, 0, bytes.length - 1) !== bytes[bytes.length - 1]) return null;
26
+ return {
27
+ cmd: bytes[2],
28
+ payload: bytes.subarray(5, 5 + len)
29
+ };
30
+ }
31
+ class FrameAssembler {
32
+ constructor() {
33
+ __publicField(this, "buf", []);
34
+ }
35
+ push(chunk, onFrame) {
36
+ for (const b of chunk) this.buf.push(b);
37
+ for (; ; ) {
38
+ while (this.buf.length >= 2 && !(this.buf[0] === 85 && this.buf[1] === 170)) this.buf.shift();
39
+ if (this.buf.length < 5) return;
40
+ const len = u16be(this.buf, 3);
41
+ const total = len + 6;
42
+ if (len > 1024) {
43
+ this.buf.shift();
44
+ this.buf.shift();
45
+ continue;
46
+ }
47
+ if (this.buf.length < total) return;
48
+ const frame = parseRenphoFrame(Uint8Array.from(this.buf.slice(0, total)));
49
+ this.buf = this.buf.slice(total);
50
+ if (frame) onFrame(frame);
51
+ }
52
+ }
53
+ reset() {
54
+ this.buf = [];
55
+ }
56
+ }
57
+ const CRC_HI = [];
58
+ const CRC_LO = [];
59
+ (() => {
60
+ for (let i = 0; i < 256; i++) {
61
+ let crc = i;
62
+ for (let j = 0; j < 8; j++) crc = crc & 1 ? crc >> 1 ^ 40961 : crc >> 1;
63
+ CRC_LO[i] = crc & 255;
64
+ CRC_HI[i] = crc >> 8 & 255;
65
+ }
66
+ })();
67
+ function ropeCrc(bytes, start = 0, end = bytes.length) {
68
+ let hi = 255;
69
+ let lo = 255;
70
+ for (let i = start; i < end; i++) {
71
+ const idx = lo ^ bytes[i];
72
+ lo = hi ^ CRC_LO[idx];
73
+ hi = CRC_HI[idx];
74
+ }
75
+ return [hi, lo];
76
+ }
77
+ function ropeFrame(cmd, payload = []) {
78
+ const body = [cmd & 255, ...be16(payload.length), ...Array.from(payload)];
79
+ return [...body, ...ropeCrc(body)];
80
+ }
81
+ function parseRopeFrame(bytes) {
82
+ if (bytes.length < 5) return null;
83
+ const len = u16be(bytes, 1);
84
+ if (bytes.length !== len + 5) return null;
85
+ const [hi, lo] = ropeCrc(bytes, 0, bytes.length - 2);
86
+ if (bytes[bytes.length - 2] !== hi || bytes[bytes.length - 1] !== lo) return null;
87
+ return {
88
+ cmd: bytes[0],
89
+ payload: bytes.subarray(3, 3 + len)
90
+ };
91
+ }
92
+ function ringFrame(bytes) {
93
+ const out = new Array(16).fill(0);
94
+ for (let i = 0; i < Math.min(15, bytes.length); i++) out[i] = bytes[i] & 255;
95
+ out[15] = sum8(out, 0, 15);
96
+ return out;
97
+ }
98
+ function qnPacket(bytes) {
99
+ return [...bytes, sum8(bytes)];
100
+ }
101
+ function qnChecksumOk(bytes) {
102
+ return bytes.length >= 1 && sum8(bytes, 0, bytes.length - 1) === bytes[bytes.length - 1];
103
+ }export{FrameAssembler,be16,be32,le16,le32,parseRenphoFrame,parseRopeFrame,qnChecksumOk,qnPacket,renphoFrame,ringFrame,ropeCrc,ropeFrame,sum8,u16be,u16le,u32be,u32le};//# sourceMappingURL=frames.js.map