@tempivo/sensor-beacon 1.1.6 → 1.1.8

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 (43) hide show
  1. package/CHANGELOG.md +25 -1
  2. package/README.md +26 -5
  3. package/android/src/main/java/expo/modules/tempivosensorbeacon/SensorBeaconNative.java +30 -0
  4. package/android/src/main/java/expo/modules/tempivosensorbeacon/TempivoSensorBeaconModule.kt +24 -0
  5. package/android-aar/library/build.gradle +1 -0
  6. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/SensorBleRuntime.kt +80 -2
  7. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorCalibration.kt +4 -8
  8. package/android-aar/library/src/main/java/com/tempivo/sensor/beacon/TempivoSensorSession.kt +31 -0
  9. package/dist/calibration.d.ts +8 -0
  10. package/dist/calibration.js +49 -14
  11. package/dist/cellular-network-status.d.ts +69 -0
  12. package/dist/cellular-network-status.js +292 -0
  13. package/dist/channel-capability.d.ts +43 -0
  14. package/dist/channel-capability.js +108 -0
  15. package/dist/config-errors.d.ts +31 -0
  16. package/dist/config-errors.js +244 -0
  17. package/dist/index.d.ts +14 -2
  18. package/dist/index.js +8 -2
  19. package/dist/native-bridge.d.ts +15 -0
  20. package/dist/native-bridge.js +21 -0
  21. package/dist/native-module.d.ts +26 -0
  22. package/dist/native-module.js +132 -1
  23. package/dist/plmn-by-country.d.ts +14 -0
  24. package/dist/plmn-by-country.js +35 -0
  25. package/dist/plmn-labels.d.ts +14 -0
  26. package/dist/plmn-labels.js +37 -0
  27. package/dist/production-defaults.js +63 -9
  28. package/dist/proto/partner-to-proto.d.ts +0 -4
  29. package/dist/proto/partner-to-proto.js +12 -0
  30. package/dist/react-native.d.ts +23 -2
  31. package/dist/react-native.js +20 -2
  32. package/dist/session-types.d.ts +2 -0
  33. package/dist/tempivo-sensor-beacon.aar +0 -0
  34. package/ios/Frameworks/TempivoSensorBridge.xcframework/Info.plist +4 -4
  35. package/ios/Frameworks/TempivoSensorBridge.xcframework/ios-arm64/TempivoSensorBridge.framework/Headers/TempivoSensorBridge.h +1 -0
  36. package/ios/Frameworks/TempivoSensorBridge.xcframework/ios-arm64/TempivoSensorBridge.framework/TempivoSensorBridge +2 -2
  37. package/ios/Frameworks/TempivoSensorBridge.xcframework/ios-arm64-simulator/TempivoSensorBridge.framework/Headers/TempivoSensorBridge.h +1 -0
  38. package/ios/Frameworks/TempivoSensorBridge.xcframework/ios-arm64-simulator/TempivoSensorBridge.framework/TempivoSensorBridge +2 -2
  39. package/ios/Sources/TempivoSensorBeacon/TempivoSensorCalibration.swift +13 -10
  40. package/ios/Sources/TempivoSensorBeacon/TempivoSensorSession.swift +11 -0
  41. package/ios/Sources/TempivoSensorBeacon/TempivoSensorSessionTypes.swift +4 -1
  42. package/ios/TempivoSensorBeaconModule.swift +11 -0
  43. package/package.json +1 -1
@@ -0,0 +1,244 @@
1
+ /**
2
+ * Decode ProtoConfig.configurationErrors (uint32 codes) from BLE get.
3
+ * Wire: (side << 14) | (parameter << 8) | value
4
+ */
5
+ const SIDE_BY_INDEX = {
6
+ 0: 'BLE',
7
+ 1: 'SERVER',
8
+ 2: 'INTERNAL',
9
+ };
10
+ const PARAMETER_BY_INDEX = {
11
+ 0: 'RULE0',
12
+ 1: 'RULE1',
13
+ 2: 'RULE2',
14
+ 3: 'RULE3',
15
+ 4: 'RULE4',
16
+ 5: 'RULE5',
17
+ 6: 'RULE6',
18
+ 7: 'RULE7',
19
+ 8: 'RULE8',
20
+ 9: 'RULE9',
21
+ 10: 'RULE10',
22
+ 11: 'RULE11',
23
+ 12: 'CACHE',
24
+ 13: 'MEASUREMENT_PERIOD',
25
+ 14: 'BLE_TURNOFF_TIME',
26
+ 15: 'TRANSFER_LIMIT',
27
+ 16: 'TRANSMISSION_INTERVAL',
28
+ 17: 'DATA_SERVER_CONFIG',
29
+ 18: 'UPDATE_SERVER_CONFIG',
30
+ 19: 'CLOUD_TOKEN',
31
+ 20: 'SUPERVISION_PERIOD',
32
+ 21: 'DEVICE_INFO_ENDPOINT',
33
+ 22: 'TIME_ENDPOINT',
34
+ 23: 'BLUETOOTH_TX_POWER',
35
+ 24: 'TIME_SYNCHRONIZATION_CONFIG',
36
+ 25: 'DNS_TTL_CONFIG',
37
+ 26: 'CELLULAR_CONFIG',
38
+ 27: 'RULE_CALENDAR_0',
39
+ 28: 'RULE_CALENDAR_1',
40
+ 29: 'RULE_CALENDAR_2',
41
+ 30: 'RULE_CALENDAR_3',
42
+ 31: 'RULE_CALENDAR_4',
43
+ 32: 'RULE_CALENDAR_5',
44
+ 33: 'LED',
45
+ 34: 'NETWORK',
46
+ 35: 'ENCRYPTION',
47
+ 59: 'CONFIG_PAYLOAD_OR_REQUEST',
48
+ 60: 'DATA_SENDING_ALGORITHM',
49
+ 61: 'OUTPUT_CONTROL',
50
+ };
51
+ const VALUE_BY_INDEX = {
52
+ 0: 'NO_ERROR',
53
+ 1: 'VALUE_OUT_OF_RANGE',
54
+ 2: 'IMPOSSIBLE_TO_CONNECT_TO_THE_SET_SERVER',
55
+ 3: 'INVALID_IP_ADDRESS',
56
+ 4: 'INVALID_APN',
57
+ 5: 'INVALID_PLMN',
58
+ 6: 'INVALID_BANDS',
59
+ 7: 'INVALID_CLOUD_TOKEN_VALUE',
60
+ 8: 'INVALID_CLOUD_TOKEN_CONFIG',
61
+ 9: 'INVALID_CONDITION',
62
+ 10: 'INVALID_ACTION',
63
+ 11: 'INVALID_SLOTS',
64
+ 12: 'INVALID_PARAMS',
65
+ 13: 'TIMEOUT',
66
+ 14: 'INVALID_COAP_ENDPOINT',
67
+ 15: 'FAILED_TO_DECODE_PROTOBUF_PAYLOAD',
68
+ 16: 'INCORRECT_SENSOR_BEHAVIOR_NO_MEASUREMENTS_AT_PLANNED_TRANSMISSION_OVER_NB_IOT',
69
+ 17: 'INVALID_CLOUD_TOKEN_COAP_OPTION',
70
+ 18: 'INVALID_PAYLOAD_SIGNATURE_COAP_OPTION',
71
+ 19: 'INVALID_DNS_SERVER_IP_ADDRESS',
72
+ 20: 'INVALID_COAP_PORT',
73
+ 21: 'INVALID_UDP_PORT',
74
+ 22: 'INVALID_CHANNEL_CONTROL_REQUEST',
75
+ 23: 'INVALID_RULE_MASK',
76
+ 24: 'INVALID_CALIBRATION_PARAMETERS_REQUEST',
77
+ 25: 'PREVIOUS_CONFIGURATION_IS_IN_PROGRESS',
78
+ };
79
+ const SIDE_LABEL = {
80
+ BLE: 'BLE apply',
81
+ SERVER: 'Server connectivity test',
82
+ INTERNAL: 'Device internal',
83
+ UNRECOGNIZED: 'Unknown source',
84
+ };
85
+ /** 1-based rule/calendar labels for partners (RULE0 → Alarm rule 1). */
86
+ const PARAMETER_LABEL = {
87
+ RULE0: 'Alarm rule 1',
88
+ RULE1: 'Alarm rule 2',
89
+ RULE2: 'Alarm rule 3',
90
+ RULE3: 'Alarm rule 4',
91
+ RULE4: 'Alarm rule 5',
92
+ RULE5: 'Alarm rule 6',
93
+ RULE6: 'Alarm rule 7',
94
+ RULE7: 'Alarm rule 8',
95
+ RULE8: 'Alarm rule 9',
96
+ RULE9: 'Alarm rule 10',
97
+ RULE10: 'Alarm rule 11',
98
+ RULE11: 'Alarm rule 12',
99
+ CACHE: 'Cache',
100
+ MEASUREMENT_PERIOD: 'Measurement period',
101
+ BLE_TURNOFF_TIME: 'BLE turnoff time',
102
+ TRANSFER_LIMIT: 'Transfer limit',
103
+ TRANSMISSION_INTERVAL: 'Transmission interval',
104
+ DATA_SERVER_CONFIG: 'Data server (SERVER_DATA)',
105
+ UPDATE_SERVER_CONFIG: 'Update server (SERVER_UPDATE)',
106
+ CLOUD_TOKEN: 'Cloud token',
107
+ SUPERVISION_PERIOD: 'Supervision period',
108
+ DEVICE_INFO_ENDPOINT: 'Device info endpoint',
109
+ TIME_ENDPOINT: 'Time endpoint',
110
+ BLUETOOTH_TX_POWER: 'BLE TX power',
111
+ TIME_SYNCHRONIZATION_CONFIG: 'Time sync',
112
+ DNS_TTL_CONFIG: 'DNS TTL',
113
+ CELLULAR_CONFIG: 'Cellular config',
114
+ RULE_CALENDAR_0: 'Calendar 1',
115
+ RULE_CALENDAR_1: 'Calendar 2',
116
+ RULE_CALENDAR_2: 'Calendar 3',
117
+ RULE_CALENDAR_3: 'Calendar 4',
118
+ RULE_CALENDAR_4: 'Calendar 5',
119
+ RULE_CALENDAR_5: 'Calendar 6',
120
+ LED: 'LED config',
121
+ NETWORK: 'Network (APN/PLMN/bands)',
122
+ ENCRYPTION: 'Encryption key',
123
+ CONFIG_PAYLOAD_OR_REQUEST: 'Config payload',
124
+ DATA_SENDING_ALGORITHM: 'Data sending',
125
+ OUTPUT_CONTROL: 'Output control',
126
+ UNRECOGNIZED: 'Unknown parameter',
127
+ };
128
+ const VALUE_LABEL = {
129
+ NO_ERROR: 'No error',
130
+ VALUE_OUT_OF_RANGE: 'Value out of range',
131
+ IMPOSSIBLE_TO_CONNECT_TO_THE_SET_SERVER: 'Cannot reach configured server',
132
+ INVALID_IP_ADDRESS: 'Invalid IP address',
133
+ INVALID_APN: 'Invalid APN',
134
+ INVALID_PLMN: 'Invalid PLMN',
135
+ INVALID_BANDS: 'Invalid bands',
136
+ INVALID_CLOUD_TOKEN_VALUE: 'Invalid cloud token value',
137
+ INVALID_CLOUD_TOKEN_CONFIG: 'Invalid cloud token config',
138
+ INVALID_CONDITION: 'Invalid rule condition',
139
+ INVALID_ACTION: 'Invalid rule action',
140
+ INVALID_SLOTS: 'Invalid channel slots',
141
+ INVALID_PARAMS: 'Invalid parameters',
142
+ TIMEOUT: 'Connection timeout',
143
+ INVALID_COAP_ENDPOINT: 'Invalid CoAP endpoint',
144
+ FAILED_TO_DECODE_PROTOBUF_PAYLOAD: 'Invalid config payload',
145
+ INCORRECT_SENSOR_BEHAVIOR_NO_MEASUREMENTS_AT_PLANNED_TRANSMISSION_OVER_NB_IOT: 'No measurements at planned NB-IoT transmission',
146
+ INVALID_CLOUD_TOKEN_COAP_OPTION: 'Invalid cloud token CoAP option',
147
+ INVALID_PAYLOAD_SIGNATURE_COAP_OPTION: 'Invalid payload signature option',
148
+ INVALID_DNS_SERVER_IP_ADDRESS: 'Invalid DNS server IP',
149
+ INVALID_COAP_PORT: 'Invalid CoAP port',
150
+ INVALID_UDP_PORT: 'Invalid UDP port',
151
+ INVALID_CHANNEL_CONTROL_REQUEST: 'Invalid output control request',
152
+ INVALID_RULE_MASK: 'Invalid rule mask',
153
+ INVALID_CALIBRATION_PARAMETERS_REQUEST: 'Invalid calibration request',
154
+ PREVIOUS_CONFIGURATION_IS_IN_PROGRESS: 'Previous config still applying',
155
+ UNRECOGNIZED: 'Unknown reason',
156
+ };
157
+ export function decodeConfigurationError(raw) {
158
+ const code = Math.trunc(raw);
159
+ const valueIndex = code & 0xff;
160
+ const parameterIndex = (code >> 8) & 0x3f;
161
+ const sideIndex = (code >> 14) & 0x3;
162
+ const side = SIDE_BY_INDEX[sideIndex] ?? 'UNRECOGNIZED';
163
+ const parameter = PARAMETER_BY_INDEX[parameterIndex] ?? 'UNRECOGNIZED';
164
+ const value = VALUE_BY_INDEX[valueIndex] ?? 'UNRECOGNIZED';
165
+ return {
166
+ raw: code,
167
+ side,
168
+ parameter,
169
+ value,
170
+ label: `${SIDE_LABEL[side]} · ${PARAMETER_LABEL[parameter]} · ${VALUE_LABEL[value]}`,
171
+ };
172
+ }
173
+ export function decodeConfigurationErrorCodes(rawCodes) {
174
+ return rawCodes
175
+ .filter((c) => typeof c === 'number' && Number.isFinite(c))
176
+ .map((c) => decodeConfigurationError(c));
177
+ }
178
+ function asUint32List(value) {
179
+ if (!Array.isArray(value))
180
+ return [];
181
+ const out = [];
182
+ for (const item of value) {
183
+ if (typeof item === 'number' && Number.isFinite(item)) {
184
+ out.push(Math.trunc(item));
185
+ continue;
186
+ }
187
+ if (typeof item === 'string' && /^\d+$/.test(item.trim())) {
188
+ out.push(parseInt(item.trim(), 10));
189
+ }
190
+ }
191
+ return out;
192
+ }
193
+ function asUnixSeconds(value) {
194
+ if (typeof value === 'number' && Number.isFinite(value) && value > 0)
195
+ return Math.trunc(value);
196
+ if (typeof value === 'string' && /^\d+$/.test(value.trim())) {
197
+ const n = parseInt(value.trim(), 10);
198
+ return n > 0 ? n : null;
199
+ }
200
+ return null;
201
+ }
202
+ function pad2(n) {
203
+ return n < 10 ? `0${n}` : String(n);
204
+ }
205
+ /**
206
+ * Format ProtoConfig.configurationErrorTimestamp as `YYYY-MM-DD HH:mm:ss UTC`.
207
+ * Accepts unix seconds (number/string) or ISO / loader UTC strings.
208
+ */
209
+ export function formatConfigurationErrorTimestamp(value) {
210
+ if (value == null || value === '')
211
+ return null;
212
+ let seconds = asUnixSeconds(value);
213
+ if (seconds == null && typeof value === 'string') {
214
+ const trimmed = value.trim();
215
+ const utcMatch = /^(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2}:\d{2})\s+UTC$/i.exec(trimmed);
216
+ if (utcMatch) {
217
+ const d = new Date(`${utcMatch[1]}T${utcMatch[2]}Z`);
218
+ if (!Number.isNaN(d.getTime()))
219
+ seconds = Math.floor(d.getTime() / 1000);
220
+ }
221
+ else {
222
+ const d = new Date(trimmed);
223
+ if (!Number.isNaN(d.getTime()))
224
+ seconds = Math.floor(d.getTime() / 1000);
225
+ }
226
+ }
227
+ if (seconds == null || seconds <= 0)
228
+ return null;
229
+ const d = new Date(seconds * 1000);
230
+ if (Number.isNaN(d.getTime()))
231
+ return null;
232
+ return (`${d.getUTCFullYear()}-${pad2(d.getUTCMonth() + 1)}-${pad2(d.getUTCDate())} ` +
233
+ `${pad2(d.getUTCHours())}:${pad2(d.getUTCMinutes())}:${pad2(d.getUTCSeconds())} UTC`);
234
+ }
235
+ /** Parse `configurationErrors` + timestamp from a decoded ProtoConfig object. */
236
+ export function configurationErrorsFromProtoConfig(proto) {
237
+ const errors = decodeConfigurationErrorCodes(asUint32List(proto.configurationErrors ?? proto.configuration_errors));
238
+ const errorTimestampUnix = asUnixSeconds(proto.configurationErrorTimestamp ?? proto.configuration_error_timestamp);
239
+ return {
240
+ errors,
241
+ errorTimestampUnix,
242
+ errorTimestampUtc: formatConfigurationErrorTimestamp(errorTimestampUnix),
243
+ };
244
+ }
package/dist/index.d.ts CHANGED
@@ -9,8 +9,20 @@ export { isMissingAdvertisementFirmware, sensorModelFromFirmware, } from './mode
9
9
  export type { SensorStickerModel } from './model.js';
10
10
  export { buildSensorQrConnectJson, draftSensorQrConnectJsonFromScanDevice, formatSensorReadingLine, mergeScanTargetIntoSensorQr, normalizeSensorStickerModel, parseSensorQrForConnect, pinFromSensorQrRecord, resolveSensorBleConnectCredentialsFromPin, sensorQrConnectJsonFromLoose, sensorQrSerialMatchesSelected, serialHexFromSensorQrScan, tryParseSensorQrJson, } from './connect-helpers.js';
11
11
  export type { ParsedSensorQrLoose, SensorBleConnectCredentials, } from './connect-helpers.js';
12
- export { calibrationFromExtendedJson, decodeLaboratoryCalibrationTimestamp, } from './calibration.js';
12
+ export { calibrationFromExtendedJson, decodeLaboratoryCalibrationTimestamp, formatLaboratoryCalibration, normalizeLaboratoryCalibration, } from './calibration.js';
13
13
  export { BLE_MAX_MEASUREMENT_INTERVAL_MINUTES, BLE_MAX_TRANSMISSION_INTERVAL_SECONDS, BLE_MIN_MEASUREMENT_INTERVAL_MINUTES, BLE_MIN_TRANSMISSION_INTERVAL_SECONDS, DEFAULT_ALERT_HYSTERESIS_C, compileSensorProfileToBleJson, configurationFromApiProfile, parseSensorConfiguration, parseSensorConfigurationFromDevice, parseSensorConfigurationFromDeviceJson, parseSensorConfigurationJson, pickPartnerConfigurationJson, } from './profile.js';
14
+ export { PROBE_CHANNEL_INDEX_0, alertsRequireProbe, assertProbeAlertsAllowed, channelTypesFromProtoConfig, deviceSupportsProbeFromBeaconReadings, deviceSupportsProbeFromChannelTypes, isTemperatureReadingType, resolveProbeSupported, } from './channel-capability.js';
15
+ export type { ProbeCapabilitySource } from './channel-capability.js';
16
+ export { configurationErrorsFromProtoConfig, decodeConfigurationError, decodeConfigurationErrorCodes, formatConfigurationErrorTimestamp, } from './config-errors.js';
17
+ export type { TempivoConfigErrorParameter, TempivoConfigErrorSide, TempivoConfigErrorValue, TempivoConfigurationError, TempivoConfigurationErrors, } from './config-errors.js';
14
18
  export type { TempivoSchedule, TempivoScheduleAlways, TempivoScheduleWeek, TempivoSensorCalibration, TempivoSensorConfiguration, TempivoSensorQr, TempivoSensorSessionType, TempivoTemperatureAlert, TempivoTemperatureChannel, TempivoTemperatureMaxAlert, TempivoTemperatureMinAlert, TempivoTemperatureRangeAlert, TempivoTriggerTransmissionResult, } from './session-types.js';
15
- export { addDeviceFoundListener, connect, disconnect, getCalibration, getConfiguration, getProtoConfiguration, isNativeSensorBeaconAvailable, isScanning, requestPermissions, setConfigurationJson, startScan, stopScan, triggerTransmission, } from './native-bridge.js';
19
+ export { addDeviceFoundListener, connect, clearMeasurementQueue, deleteMeasurements, disconnect, getCalibration, getCellularNetworkStatus, getCellularNetworkStatusJson, getConfiguration, getConfigurationErrors, getProtoConfiguration, isNativeSensorBeaconAvailable, isScanning, requestPermissions, setConfigurationJson, setOperator, startScan, stopScan, triggerTransmission, } from './native-bridge.js';
20
+ export { cellularNetworkStatusViewFromJson, formatCellularNetworkStatusLabel, formatCellularNetworkStatusLabelWithOperator, formatCellularNetworkStatusSummary, formatCellularServerDetailLabel, isCellularConnectionFullyOk, isCellularRegistrationOk, isCellularServerCommunicationOk, isSimCardOk, isValidCellularIpAddress, normalizeCellularSignalStrengthDbm, parseCellularNetworkStatusJson, parseCellularNetworkStatusObject, resolveCellularStatusTone, } from './cellular-network-status.js';
21
+ export type { TempivoCellularNetworkStatus, TempivoCellularNetworkStatusView, TempivoCellularStatusLocale, TempivoCellularStatusTone, } from './cellular-network-status.js';
22
+ export { PREFERRED_PLMN_BY_COUNTRY_ISO2, PREFERRED_PLMN_FALLBACK, normalizeCountryIso2, resolveSetOperator, } from './plmn-by-country.js';
23
+ export type { SetOperatorArg } from './plmn-by-country.js';
24
+ export { getOperatorNameFromPlmn, PLMN_OPERATOR_LABELS } from './plmn-labels.js';
25
+ export type { TempivoPlmnOperatorOption } from './plmn-labels.js';
16
26
  export type { TempivoDeviceFoundSubscription, TempivoNativeCalibration, TempivoNativeMeasurement, TempivoNativePermissionResponse, TempivoNativePermissionStatus, TempivoNativeQr, TempivoNativeTelemetry, TempivoSensorBeaconDevice, } from './native-types.js';
27
+ export { checkProductionDefaults } from './production-defaults.js';
28
+ export type { ProductionCheckLine, ProductionCheckResult } from './production-defaults.js';
package/dist/index.js CHANGED
@@ -7,6 +7,12 @@ export { TempivoSensorError } from './errors.js';
7
7
  export { DEFAULT_SENSOR_MODEL, assertSupportedSensorModel, bluetoothMacFromSerial, normalizeSensorSerial, encodeSensorPinPayload, parseSensorQrJson, sessionTypeFromModel, } from './qr.js';
8
8
  export { isMissingAdvertisementFirmware, sensorModelFromFirmware, } from './model.js';
9
9
  export { buildSensorQrConnectJson, draftSensorQrConnectJsonFromScanDevice, formatSensorReadingLine, mergeScanTargetIntoSensorQr, normalizeSensorStickerModel, parseSensorQrForConnect, pinFromSensorQrRecord, resolveSensorBleConnectCredentialsFromPin, sensorQrConnectJsonFromLoose, sensorQrSerialMatchesSelected, serialHexFromSensorQrScan, tryParseSensorQrJson, } from './connect-helpers.js';
10
- export { calibrationFromExtendedJson, decodeLaboratoryCalibrationTimestamp, } from './calibration.js';
10
+ export { calibrationFromExtendedJson, decodeLaboratoryCalibrationTimestamp, formatLaboratoryCalibration, normalizeLaboratoryCalibration, } from './calibration.js';
11
11
  export { BLE_MAX_MEASUREMENT_INTERVAL_MINUTES, BLE_MAX_TRANSMISSION_INTERVAL_SECONDS, BLE_MIN_MEASUREMENT_INTERVAL_MINUTES, BLE_MIN_TRANSMISSION_INTERVAL_SECONDS, DEFAULT_ALERT_HYSTERESIS_C, compileSensorProfileToBleJson, configurationFromApiProfile, parseSensorConfiguration, parseSensorConfigurationFromDevice, parseSensorConfigurationFromDeviceJson, parseSensorConfigurationJson, pickPartnerConfigurationJson, } from './profile.js';
12
- export { addDeviceFoundListener, connect, disconnect, getCalibration, getConfiguration, getProtoConfiguration, isNativeSensorBeaconAvailable, isScanning, requestPermissions, setConfigurationJson, startScan, stopScan, triggerTransmission, } from './native-bridge.js';
12
+ export { PROBE_CHANNEL_INDEX_0, alertsRequireProbe, assertProbeAlertsAllowed, channelTypesFromProtoConfig, deviceSupportsProbeFromBeaconReadings, deviceSupportsProbeFromChannelTypes, isTemperatureReadingType, resolveProbeSupported, } from './channel-capability.js';
13
+ export { configurationErrorsFromProtoConfig, decodeConfigurationError, decodeConfigurationErrorCodes, formatConfigurationErrorTimestamp, } from './config-errors.js';
14
+ export { addDeviceFoundListener, connect, clearMeasurementQueue, deleteMeasurements, disconnect, getCalibration, getCellularNetworkStatus, getCellularNetworkStatusJson, getConfiguration, getConfigurationErrors, getProtoConfiguration, isNativeSensorBeaconAvailable, isScanning, requestPermissions, setConfigurationJson, setOperator, startScan, stopScan, triggerTransmission, } from './native-bridge.js';
15
+ export { cellularNetworkStatusViewFromJson, formatCellularNetworkStatusLabel, formatCellularNetworkStatusLabelWithOperator, formatCellularNetworkStatusSummary, formatCellularServerDetailLabel, isCellularConnectionFullyOk, isCellularRegistrationOk, isCellularServerCommunicationOk, isSimCardOk, isValidCellularIpAddress, normalizeCellularSignalStrengthDbm, parseCellularNetworkStatusJson, parseCellularNetworkStatusObject, resolveCellularStatusTone, } from './cellular-network-status.js';
16
+ export { PREFERRED_PLMN_BY_COUNTRY_ISO2, PREFERRED_PLMN_FALLBACK, normalizeCountryIso2, resolveSetOperator, } from './plmn-by-country.js';
17
+ export { getOperatorNameFromPlmn, PLMN_OPERATOR_LABELS } from './plmn-labels.js';
18
+ export { checkProductionDefaults } from './production-defaults.js';
@@ -1,3 +1,4 @@
1
+ import { type TempivoCellularNetworkStatusView, type TempivoCellularStatusLocale } from './cellular-network-status.js';
1
2
  import type { TempivoDeviceFoundSubscription, TempivoNativeCalibration, TempivoNativePermissionResponse, TempivoNativeQr, TempivoSensorBeaconDevice } from './native-types.js';
2
3
  import type { TempivoSensorConfiguration, TempivoSensorQr, TempivoTriggerTransmissionResult } from './session-types.js';
3
4
  export declare function isNativeSensorBeaconAvailable(): Promise<boolean>;
@@ -11,5 +12,19 @@ export declare function disconnect(): Promise<void>;
11
12
  export declare function getConfiguration(): Promise<TempivoSensorConfiguration>;
12
13
  export declare function setConfigurationJson(config: string | TempivoSensorConfiguration): Promise<void>;
13
14
  export declare function triggerTransmission(): Promise<TempivoTriggerTransmissionResult>;
15
+ export declare function clearMeasurementQueue(): Promise<{
16
+ ok: boolean;
17
+ }>;
18
+ /** Alias for {@link clearMeasurementQueue}. */
19
+ export declare function deleteMeasurements(): Promise<{
20
+ ok: boolean;
21
+ }>;
22
+ export declare function getCellularNetworkStatusJson(): Promise<string>;
23
+ export declare function getCellularNetworkStatus(opts?: {
24
+ locale?: TempivoCellularStatusLocale;
25
+ afterServerTrigger?: boolean;
26
+ }): Promise<TempivoCellularNetworkStatusView>;
27
+ export declare function setOperator(countryOrAuto: string): Promise<void>;
14
28
  export declare function getCalibration(): Promise<TempivoNativeCalibration>;
15
29
  export declare function getProtoConfiguration(): Promise<Record<string, unknown>>;
30
+ export declare function getConfigurationErrors(): Promise<import("./config-errors.js").TempivoConfigurationErrors>;
@@ -1,3 +1,4 @@
1
+ import { cellularNetworkStatusViewFromJson, } from './cellular-network-status.js';
1
2
  import { TempivoSensorError } from './errors.js';
2
3
  async function loadNative() {
3
4
  try {
@@ -46,9 +47,29 @@ export async function setConfigurationJson(config) {
46
47
  export async function triggerTransmission() {
47
48
  return (await loadNative()).triggerTransmission();
48
49
  }
50
+ export async function clearMeasurementQueue() {
51
+ return (await loadNative()).clearMeasurementQueue();
52
+ }
53
+ /** Alias for {@link clearMeasurementQueue}. */
54
+ export async function deleteMeasurements() {
55
+ return (await loadNative()).deleteMeasurements();
56
+ }
57
+ export async function getCellularNetworkStatusJson() {
58
+ return (await loadNative()).getCellularNetworkStatusJson();
59
+ }
60
+ export async function getCellularNetworkStatus(opts) {
61
+ const json = await getCellularNetworkStatusJson();
62
+ return cellularNetworkStatusViewFromJson(json, opts);
63
+ }
64
+ export async function setOperator(countryOrAuto) {
65
+ return (await loadNative()).setOperator(countryOrAuto);
66
+ }
49
67
  export async function getCalibration() {
50
68
  return (await loadNative()).getCalibration();
51
69
  }
52
70
  export async function getProtoConfiguration() {
53
71
  return (await loadNative()).getProtoConfiguration();
54
72
  }
73
+ export async function getConfigurationErrors() {
74
+ return (await loadNative()).getConfigurationErrors();
75
+ }
@@ -1,6 +1,8 @@
1
+ import { type TempivoConfigurationErrors } from './config-errors.js';
1
2
  import type { TempivoDeviceFoundSubscription, TempivoNativeCalibration, TempivoNativePermissionResponse, TempivoNativeQr, TempivoSensorBeaconDevice } from './native-types.js';
2
3
  import { type ProtoConfigObject } from './proto/decode-proto-config.js';
3
4
  import type { TempivoSensorConfiguration, TempivoSensorQr, TempivoTriggerTransmissionResult } from './session-types.js';
5
+ export type { TempivoConfigurationErrors };
4
6
  export declare function isNativeSensorBeaconAvailable(): boolean;
5
7
  export declare function requestPermissions(): Promise<TempivoNativePermissionResponse>;
6
8
  export declare function startScan(): Promise<void>;
@@ -22,6 +24,30 @@ export declare function getConfiguration(): Promise<TempivoSensorConfiguration>;
22
24
  */
23
25
  export declare function setConfigurationJson(config: string | TempivoSensorConfiguration): Promise<void>;
24
26
  export declare function triggerTransmission(): Promise<TempivoTriggerTransmissionResult>;
27
+ /**
28
+ * Clear on-device measurement / uplink queue (memory reset).
29
+ * Manual recovery only: not used by setConfigurationJson.
30
+ */
31
+ export declare function clearMeasurementQueue(): Promise<{
32
+ ok: boolean;
33
+ }>;
34
+ /** Alias for {@link clearMeasurementQueue} (delete stored measurements on the sensor). */
35
+ export declare function deleteMeasurements(): Promise<{
36
+ ok: boolean;
37
+ }>;
38
+ /** GATT cellular network status JSON (registration, IP, PLMN). Same family as Tempivo app. */
39
+ export declare function getCellularNetworkStatusJson(): Promise<string>;
40
+ /**
41
+ * Set PLMN AUTO or preferred operator for a country ISO (`SE`, `NO`, …).
42
+ * Native merges PLMN into getConfiguration (sanitized). Avoids raw ProtoConfig write.
43
+ */
44
+ export declare function setOperator(countryOrAuto: string): Promise<void>;
25
45
  /** Decoded device ProtoConfig (server, APN, intervals). Requires an open BLE session. */
26
46
  export declare function getProtoConfiguration(): Promise<ProtoConfigObject>;
47
+ /**
48
+ * Read `configurationErrors` from the device ProtoConfig over BLE.
49
+ * Same GATT get as {@link getProtoConfiguration}; decoded for partner apps.
50
+ * Empty `errors` means the device reports none (or firmware cleared them).
51
+ */
52
+ export declare function getConfigurationErrors(): Promise<TempivoConfigurationErrors>;
27
53
  export declare function getCalibration(): Promise<TempivoNativeCalibration>;
@@ -1,4 +1,6 @@
1
1
  import { requireOptionalNativeModule } from 'expo-modules-core';
2
+ import { normalizeLaboratoryCalibration, } from './calibration.js';
3
+ import { configurationErrorsFromProtoConfig, } from './config-errors.js';
2
4
  import { TempivoSensorError } from './errors.js';
3
5
  import { configToJson, mapNativeError, qrToConnectJson } from './native-helpers.js';
4
6
  import { decodeProtoConfigBase64 } from './proto/decode-proto-config.js';
@@ -37,9 +39,25 @@ async function ensureBlePermission() {
37
39
  return;
38
40
  throw new TempivoSensorError('runtimeUnavailable', 'Bluetooth permission is required.');
39
41
  }
42
+ /**
43
+ * Drop a leftover native GATT session once per JS runtime (Metro reload / app relaunch
44
+ * often keeps the Android/iOS module alive while wiping JS). Without this, the sensor
45
+ * stops advertising until someone calls {@link disconnect}.
46
+ */
47
+ let releaseStaleGattOnceStarted = false;
48
+ function releaseStaleGattSessionOnce() {
49
+ if (releaseStaleGattOnceStarted)
50
+ return;
51
+ if (!isNativeSensorBeaconAvailable())
52
+ return;
53
+ releaseStaleGattOnceStarted = true;
54
+ void disconnect().catch(() => undefined);
55
+ }
40
56
  export async function startScan() {
41
57
  await ensureBlePermission();
42
58
  try {
59
+ // Own GATT blocks advertising for that sensor; release before scan.
60
+ await disconnect().catch(() => undefined);
43
61
  await getNative().startScan();
44
62
  }
45
63
  catch (error) {
@@ -62,6 +80,7 @@ export function addDeviceFoundListener(listener) {
62
80
  export async function connect(qr) {
63
81
  await ensureBlePermission();
64
82
  try {
83
+ releaseStaleGattSessionOnce();
65
84
  return await getNative().connect(qrToConnectJson(qr));
66
85
  }
67
86
  catch (error) {
@@ -142,6 +161,92 @@ export async function triggerTransmission() {
142
161
  throw mapNativeError(error);
143
162
  }
144
163
  }
164
+ /**
165
+ * Clear on-device measurement / uplink queue (memory reset).
166
+ * Manual recovery only: not used by setConfigurationJson.
167
+ */
168
+ export async function clearMeasurementQueue() {
169
+ try {
170
+ const native = getNative();
171
+ if (typeof native.clearMeasurementQueue !== 'function') {
172
+ throw new TempivoSensorError('runtimeUnavailable', 'Native clearMeasurementQueue is unavailable in this build.');
173
+ }
174
+ return await withTimeout(native.clearMeasurementQueue(), 45_000, 'clearMeasurementQueue');
175
+ }
176
+ catch (error) {
177
+ throw mapNativeError(error);
178
+ }
179
+ }
180
+ /** Alias for {@link clearMeasurementQueue} (delete stored measurements on the sensor). */
181
+ export async function deleteMeasurements() {
182
+ return clearMeasurementQueue();
183
+ }
184
+ /** GATT cellular network status JSON (registration, IP, PLMN). Same family as Tempivo app. */
185
+ export async function getCellularNetworkStatusJson() {
186
+ try {
187
+ const native = getNative();
188
+ if (typeof native.getCellularNetworkStatusJson !== 'function') {
189
+ throw new TempivoSensorError('runtimeUnavailable', 'Native getCellularNetworkStatusJson is unavailable in this build.');
190
+ }
191
+ return await native.getCellularNetworkStatusJson();
192
+ }
193
+ catch (error) {
194
+ throw mapNativeError(error);
195
+ }
196
+ }
197
+ /** Preferred PLMN map (keep in sync with `plmn-by-country.ts`). Inlined for Metro file: links. */
198
+ const PREFERRED_PLMN_BY_COUNTRY_ISO2 = {
199
+ SE: 24001,
200
+ NO: 24201,
201
+ DK: 23820,
202
+ FI: 24491,
203
+ GB: 23415,
204
+ UK: 23415,
205
+ DE: 26201,
206
+ FR: 20801,
207
+ IT: 22201,
208
+ ES: 21401,
209
+ };
210
+ /**
211
+ * Set PLMN AUTO or preferred operator for a country ISO (`SE`, `NO`, …).
212
+ * Native merges PLMN into getConfiguration (sanitized). Avoids raw ProtoConfig write.
213
+ */
214
+ export async function setOperator(countryOrAuto) {
215
+ const raw = String(countryOrAuto ?? '').trim();
216
+ const automatic = !raw || raw.toUpperCase() === 'AUTO';
217
+ let plmn = null;
218
+ if (!automatic) {
219
+ const iso = raw.toUpperCase();
220
+ if (!/^[A-Z]{2}$/.test(iso)) {
221
+ throw new TempivoSensorError('unknown', `Invalid country ISO: ${raw}`);
222
+ }
223
+ plmn = PREFERRED_PLMN_BY_COUNTRY_ISO2[iso] ?? 24001;
224
+ }
225
+ try {
226
+ const native = getNative();
227
+ if (typeof native.setOperator !== 'function') {
228
+ throw new TempivoSensorError('runtimeUnavailable', 'Native setOperator is unavailable in this build.');
229
+ }
230
+ await withTimeout(native.setOperator(automatic, plmn), 45_000, 'setOperator');
231
+ }
232
+ catch (error) {
233
+ throw mapNativeError(error);
234
+ }
235
+ }
236
+ function withTimeout(promise, ms, label) {
237
+ return new Promise((resolve, reject) => {
238
+ const id = setTimeout(() => {
239
+ reject(new TempivoSensorError('unknown', `${label} timed out after ${ms}ms.`));
240
+ }, ms);
241
+ promise.then((v) => {
242
+ clearTimeout(id);
243
+ resolve(v);
244
+ }, (e) => {
245
+ clearTimeout(id);
246
+ reject(e);
247
+ });
248
+ });
249
+ }
145
250
  /** Decoded device ProtoConfig (server, APN, intervals). Requires an open BLE session. */
146
251
  export async function getProtoConfiguration() {
147
252
  try {
@@ -158,11 +263,37 @@ export async function getProtoConfiguration() {
158
263
  throw mapNativeError(error);
159
264
  }
160
265
  }
266
+ /**
267
+ * Read `configurationErrors` from the device ProtoConfig over BLE.
268
+ * Same GATT get as {@link getProtoConfiguration}; decoded for partner apps.
269
+ * Empty `errors` means the device reports none (or firmware cleared them).
270
+ */
271
+ export async function getConfigurationErrors() {
272
+ const proto = await getProtoConfiguration();
273
+ return configurationErrorsFromProtoConfig(proto);
274
+ }
161
275
  export async function getCalibration() {
162
276
  try {
163
- return await getNative().getCalibration();
277
+ const raw = await getNative().getCalibration();
278
+ const payload = raw && typeof raw === 'object' ? raw : null;
279
+ try {
280
+ return normalizeLaboratoryCalibration(payload);
281
+ }
282
+ catch {
283
+ // Keep BLE session usable even if formatting helpers misbehave.
284
+ return {
285
+ laboratoryCalibrationDate: typeof payload?.laboratoryCalibrationDate === 'string'
286
+ ? payload.laboratoryCalibrationDate
287
+ : null,
288
+ laboratoryCalibrationTimestamp: typeof payload?.laboratoryCalibrationTimestamp === 'number'
289
+ ? payload.laboratoryCalibrationTimestamp
290
+ : null,
291
+ };
292
+ }
164
293
  }
165
294
  catch (error) {
166
295
  throw mapNativeError(error);
167
296
  }
168
297
  }
298
+ // Metro / process reuse: release any leftover GATT as soon as this module loads.
299
+ releaseStaleGattSessionOnce();
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Preferred NB-IoT PLMN per ISO 3166-1 alpha-2 (hardcoded for partner SDK).
3
+ * Keep in sync with app `src/lib/default-plmn-by-country.ts`.
4
+ */
5
+ export declare const PREFERRED_PLMN_BY_COUNTRY_ISO2: Record<string, number>;
6
+ export declare const PREFERRED_PLMN_FALLBACK = 24001;
7
+ export declare function normalizeCountryIso2(code: string | null | undefined): string | null;
8
+ /** `AUTO` or ISO2 → PLMN mode for BLE. */
9
+ export type SetOperatorArg = 'AUTO' | string;
10
+ export declare function resolveSetOperator(arg: SetOperatorArg): {
11
+ mode: 'AUTO' | 'VALUE';
12
+ plmn?: number;
13
+ countryIso?: string;
14
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Preferred NB-IoT PLMN per ISO 3166-1 alpha-2 (hardcoded for partner SDK).
3
+ * Keep in sync with app `src/lib/default-plmn-by-country.ts`.
4
+ */
5
+ export const PREFERRED_PLMN_BY_COUNTRY_ISO2 = {
6
+ SE: 24001,
7
+ NO: 24201,
8
+ DK: 23820,
9
+ FI: 24491,
10
+ GB: 23415,
11
+ UK: 23415,
12
+ DE: 26201,
13
+ FR: 20801,
14
+ IT: 22201,
15
+ ES: 21401,
16
+ };
17
+ export const PREFERRED_PLMN_FALLBACK = 24001;
18
+ export function normalizeCountryIso2(code) {
19
+ if (code == null || typeof code !== 'string')
20
+ return null;
21
+ const t = code.trim().toUpperCase();
22
+ if (!/^[A-Z]{2}$/.test(t))
23
+ return null;
24
+ return t;
25
+ }
26
+ export function resolveSetOperator(arg) {
27
+ const raw = String(arg ?? '').trim();
28
+ if (!raw || raw.toUpperCase() === 'AUTO')
29
+ return { mode: 'AUTO' };
30
+ const iso = normalizeCountryIso2(raw);
31
+ if (!iso)
32
+ throw new Error(`Invalid country ISO: ${raw}`);
33
+ const plmn = PREFERRED_PLMN_BY_COUNTRY_ISO2[iso] ?? PREFERRED_PLMN_FALLBACK;
34
+ return { mode: 'VALUE', plmn, countryIso: iso };
35
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * NB-IoT PLMN → operator label. Keep in sync with app `src/lib/plmn-options.ts`.
3
+ */
4
+ export type TempivoPlmnOperatorOption = {
5
+ plmn: number;
6
+ label: string;
7
+ };
8
+ /** Known operators (same labels as Tempivo settings / BLE strip). */
9
+ export declare const PLMN_OPERATOR_LABELS: TempivoPlmnOperatorOption[];
10
+ /**
11
+ * Operator name from PLMN. Known codes return labels (e.g. Tele2 Sweden).
12
+ * Unknown but valid codes return `PLMN {code}` so partners still see something.
13
+ */
14
+ export declare function getOperatorNameFromPlmn(plmn: number | null | undefined): string | null;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * NB-IoT PLMN → operator label. Keep in sync with app `src/lib/plmn-options.ts`.
3
+ */
4
+ /** Known operators (same labels as Tempivo settings / BLE strip). */
5
+ export const PLMN_OPERATOR_LABELS = [
6
+ { plmn: 24001, label: 'Telia Sweden' },
7
+ { plmn: 24007, label: 'Tele2 Sweden' },
8
+ { plmn: 24008, label: 'Telenor Sweden' },
9
+ { plmn: 24002, label: '3 Sweden' },
10
+ { plmn: 24201, label: 'Telenor Norway' },
11
+ { plmn: 24202, label: 'Telia Norway' },
12
+ { plmn: 23801, label: 'TDC Denmark' },
13
+ { plmn: 23820, label: 'Telia Denmark' },
14
+ { plmn: 24491, label: 'Telia Finland' },
15
+ { plmn: 24405, label: 'Elisa Finland' },
16
+ { plmn: 23420, label: '3 UK' },
17
+ { plmn: 23410, label: 'O2 UK' },
18
+ { plmn: 23415, label: 'Vodafone UK' },
19
+ { plmn: 26201, label: 'T-Mobile Germany' },
20
+ { plmn: 26202, label: 'Vodafone Germany' },
21
+ { plmn: 26207, label: 'O2 Germany' },
22
+ { plmn: 20801, label: 'Orange France' },
23
+ { plmn: 20810, label: 'SFR France' },
24
+ { plmn: 22201, label: 'TIM Italy' },
25
+ { plmn: 21401, label: 'Vodafone Spain' },
26
+ ];
27
+ const PLMN_TO_LABEL = Object.fromEntries(PLMN_OPERATOR_LABELS.map((o) => [o.plmn, o.label]));
28
+ /**
29
+ * Operator name from PLMN. Known codes return labels (e.g. Tele2 Sweden).
30
+ * Unknown but valid codes return `PLMN {code}` so partners still see something.
31
+ */
32
+ export function getOperatorNameFromPlmn(plmn) {
33
+ const n = plmn != null ? Number(plmn) : NaN;
34
+ if (!Number.isFinite(n) || n < 10_000 || n > 999_999)
35
+ return null;
36
+ return PLMN_TO_LABEL[n] ?? `PLMN ${Math.trunc(n)}`;
37
+ }