homebridge-lovesac-stealthtech 0.0.0-development

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.
@@ -0,0 +1,227 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.LovesacDevice = void 0;
37
+ const responses_1 = require("./responses");
38
+ const constants_1 = require("./constants");
39
+ const commands = __importStar(require("./commands"));
40
+ const settings_1 = require("../settings");
41
+ const CODE_NAMES = {
42
+ [constants_1.ResponseCode.Volume]: 'Volume',
43
+ [constants_1.ResponseCode.CenterVolume]: 'Center',
44
+ [constants_1.ResponseCode.Treble]: 'Treble',
45
+ [constants_1.ResponseCode.Bass]: 'Bass',
46
+ [constants_1.ResponseCode.Mute]: 'Mute',
47
+ [constants_1.ResponseCode.QuietMode]: 'Quiet Mode',
48
+ [constants_1.ResponseCode.Balance]: 'Balance',
49
+ [constants_1.ResponseCode.Layout]: 'Layout',
50
+ [constants_1.ResponseCode.Source]: 'Source',
51
+ [constants_1.ResponseCode.Power]: 'Power',
52
+ [constants_1.ResponseCode.Preset]: 'Preset',
53
+ [constants_1.ResponseCode.Covering]: 'Covering',
54
+ [constants_1.ResponseCode.ArmType]: 'Arm Type',
55
+ [constants_1.ResponseCode.Subwoofer]: 'Subwoofer',
56
+ [constants_1.ResponseCode.RearVolume]: 'Rear',
57
+ };
58
+ function formatStateValue(code, value) {
59
+ switch (code) {
60
+ case constants_1.ResponseCode.Power:
61
+ return value === 0 ? 'On' : 'Off';
62
+ case constants_1.ResponseCode.Mute:
63
+ case constants_1.ResponseCode.QuietMode:
64
+ case constants_1.ResponseCode.Subwoofer:
65
+ return value === 1 ? 'Yes' : 'No';
66
+ case constants_1.ResponseCode.Source:
67
+ return constants_1.SOURCE_NAMES[value] ?? String(value);
68
+ case constants_1.ResponseCode.Preset:
69
+ return constants_1.PRESET_NAMES[value] ?? String(value);
70
+ default:
71
+ return String(value);
72
+ }
73
+ }
74
+ class LovesacDevice {
75
+ connectionManager;
76
+ log;
77
+ state;
78
+ stateListeners = [];
79
+ stateInitialized = false;
80
+ mcuVersion = '';
81
+ versionListeners = [];
82
+ pollTimer = null;
83
+ constructor(connectionManager, log) {
84
+ this.connectionManager = connectionManager;
85
+ this.log = log;
86
+ this.state = (0, responses_1.createDefaultState)();
87
+ this.connectionManager.setNotificationHandler((data) => {
88
+ this.handleNotification(data);
89
+ });
90
+ this.connectionManager.onReconnect(() => {
91
+ this.requestStateRefresh().catch(err => {
92
+ this.log.warn('Reconnect state refresh failed: %s', (0, settings_1.errorMessage)(err));
93
+ });
94
+ });
95
+ }
96
+ onStateChange(listener) {
97
+ this.stateListeners.push(listener);
98
+ }
99
+ isStateInitialized() {
100
+ return this.stateInitialized;
101
+ }
102
+ onVersionResolved(callback) {
103
+ this.versionListeners.push(callback);
104
+ }
105
+ async requestStateRefresh() {
106
+ await this.connectionManager.enqueue(commands.requestDeviceInfo());
107
+ await this.connectionManager.enqueue(commands.requestVersionInfo());
108
+ }
109
+ startPolling(intervalSeconds) {
110
+ if (intervalSeconds <= 0) {
111
+ this.log.info('Background polling disabled');
112
+ return;
113
+ }
114
+ this.log.info('Starting background poll every %ds', intervalSeconds);
115
+ // Immediate initial fetch — onReconnect will also fire on first connect
116
+ this.requestStateRefresh().catch(err => {
117
+ this.log.warn('Initial state refresh failed (will retry on next poll): %s', (0, settings_1.errorMessage)(err));
118
+ });
119
+ this.pollTimer = setInterval(() => {
120
+ this.requestStateRefresh().catch(err => {
121
+ this.log.warn('Background poll failed: %s', (0, settings_1.errorMessage)(err));
122
+ });
123
+ }, intervalSeconds * 1000);
124
+ }
125
+ stopPolling() {
126
+ if (this.pollTimer) {
127
+ clearInterval(this.pollTimer);
128
+ this.pollTimer = null;
129
+ }
130
+ }
131
+ getResolvedAddress() {
132
+ return this.connectionManager.getResolvedAddress();
133
+ }
134
+ // --- Power ---
135
+ async setPower(on) {
136
+ this.log.info('Setting power %s', on ? 'ON' : 'OFF');
137
+ await this.connectionManager.enqueue(commands.setPower(on));
138
+ }
139
+ // --- Volume ---
140
+ async setVolume(volume) {
141
+ this.log.info('Setting volume to %d', volume);
142
+ await this.connectionManager.enqueue(commands.setVolume(volume));
143
+ }
144
+ volumeToPercent(volume) {
145
+ return Math.round((volume / settings_1.MAX_VOLUME) * 100);
146
+ }
147
+ percentToVolume(percent) {
148
+ return Math.round((percent / 100) * settings_1.MAX_VOLUME);
149
+ }
150
+ async volumeUp(step) {
151
+ const newVol = Math.min(settings_1.MAX_VOLUME, this.state.volume + step);
152
+ await this.setVolume(newVol);
153
+ }
154
+ async volumeDown(step) {
155
+ const newVol = Math.max(0, this.state.volume - step);
156
+ await this.setVolume(newVol);
157
+ }
158
+ // --- Mute ---
159
+ async setMute(muted) {
160
+ this.log.info('Setting mute %s', muted ? 'ON' : 'OFF');
161
+ await this.connectionManager.enqueue(commands.setMute(muted));
162
+ }
163
+ // --- Quiet Mode ---
164
+ async setQuietMode(on) {
165
+ this.log.info('Setting quiet mode %s', on ? 'ON' : 'OFF');
166
+ await this.connectionManager.enqueue(commands.setQuietMode(on));
167
+ }
168
+ // --- Source ---
169
+ async setSource(source) {
170
+ this.log.info('Setting source to %d', source);
171
+ await this.connectionManager.enqueue(commands.setSource(source));
172
+ }
173
+ // --- Preset ---
174
+ async setPreset(preset) {
175
+ this.log.info('Setting preset to %d', preset);
176
+ // Optimistically update cached state so switches respond immediately
177
+ const readVal = (0, constants_1.presetWriteToRead)(preset);
178
+ if (readVal !== undefined) {
179
+ this.state.preset = readVal;
180
+ }
181
+ await this.connectionManager.enqueue(commands.setPreset(preset));
182
+ }
183
+ isPresetActive(readValue) {
184
+ return this.state.preset === readValue;
185
+ }
186
+ // --- Internal ---
187
+ handleNotification(data) {
188
+ // Check for version response: CC 05/06 AA 01 03 <type> <major> <minor>
189
+ if (data.length >= 8 && data[2] === 0xAA && data[3] === 0x01 && data[4] === 0x03) {
190
+ const type = data[5]; // 01=MCU, 02=DSP, 03=EQ
191
+ const major = data[6];
192
+ const minor = data[7];
193
+ const ver = `${major}.${minor}`;
194
+ if (type === 0x01 && !this.mcuVersion) {
195
+ this.mcuVersion = ver;
196
+ this.log.info('Firmware version: %s', ver);
197
+ for (const listener of this.versionListeners) {
198
+ listener();
199
+ }
200
+ }
201
+ return;
202
+ }
203
+ const parsed = (0, responses_1.parseNotification)(data);
204
+ if (!parsed) {
205
+ this.log.debug('Ignoring non-status notification: %s', data.toString('hex'));
206
+ return;
207
+ }
208
+ const changed = (0, responses_1.applyResponse)(this.state, parsed);
209
+ if (changed === responses_1.OUT_OF_RANGE) {
210
+ this.log.warn('Out-of-range value for %s: %d (ignored)', CODE_NAMES[parsed.code] ?? `0x${parsed.code.toString(16)}`, parsed.value);
211
+ return;
212
+ }
213
+ this.stateInitialized = true;
214
+ if (changed) {
215
+ this.log.debug('State: %s = %s', CODE_NAMES[parsed.code] ?? `0x${parsed.code.toString(16)}`, formatStateValue(parsed.code, parsed.value));
216
+ for (const listener of this.stateListeners) {
217
+ try {
218
+ listener(parsed.code, parsed.value);
219
+ }
220
+ catch (err) {
221
+ this.log.error('State listener error: %s', (0, settings_1.errorMessage)(err));
222
+ }
223
+ }
224
+ }
225
+ }
226
+ }
227
+ exports.LovesacDevice = LovesacDevice;
@@ -0,0 +1,20 @@
1
+ import { PresetWriteValue, SourceValue } from './constants';
2
+ export interface BleCommand {
3
+ characteristicUuid: string;
4
+ data: Buffer;
5
+ }
6
+ export declare function setVolume(volume: number): BleCommand;
7
+ export declare function setBass(bass: number): BleCommand;
8
+ export declare function setTreble(treble: number): BleCommand;
9
+ export declare function setCenterVolume(center: number): BleCommand;
10
+ export declare function setRearVolume(rear: number): BleCommand;
11
+ export declare function setMute(muted: boolean): BleCommand;
12
+ export declare function setQuietMode(on: boolean): BleCommand;
13
+ export declare function setBalance(balance: number): BleCommand;
14
+ export declare function setPower(on: boolean): BleCommand;
15
+ export declare function setPreset(preset: PresetWriteValue): BleCommand;
16
+ export declare function setSource(source: SourceValue): BleCommand;
17
+ export declare function requestDeviceInfo(): BleCommand;
18
+ export declare function requestVersionInfo(): BleCommand;
19
+ export declare function setPlayPause(value: number): BleCommand;
20
+ export declare function setSkip(value: number): BleCommand;
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setVolume = setVolume;
4
+ exports.setBass = setBass;
5
+ exports.setTreble = setTreble;
6
+ exports.setCenterVolume = setCenterVolume;
7
+ exports.setRearVolume = setRearVolume;
8
+ exports.setMute = setMute;
9
+ exports.setQuietMode = setQuietMode;
10
+ exports.setBalance = setBalance;
11
+ exports.setPower = setPower;
12
+ exports.setPreset = setPreset;
13
+ exports.setSource = setSource;
14
+ exports.requestDeviceInfo = requestDeviceInfo;
15
+ exports.requestVersionInfo = requestVersionInfo;
16
+ exports.setPlayPause = setPlayPause;
17
+ exports.setSkip = setSkip;
18
+ const settings_1 = require("../settings");
19
+ // Format A: AA <cmdId> <subCmdId> 01 <value>
20
+ function formatA(cmdId, subCmdId, value) {
21
+ return Buffer.from([0xAA, cmdId, subCmdId, 0x01, value]);
22
+ }
23
+ // Format B: AA <cmdId> <value> 00
24
+ function formatB(cmdId, value) {
25
+ return Buffer.from([0xAA, cmdId, value, 0x00]);
26
+ }
27
+ function eqCommand(subCmdId, value) {
28
+ return { characteristicUuid: settings_1.CharUUID.EqControl, data: formatA(0x03, subCmdId, value) };
29
+ }
30
+ // Volume: 0-36
31
+ function setVolume(volume) {
32
+ return eqCommand(0x02, Math.max(0, Math.min(36, volume)));
33
+ }
34
+ // Bass: 0-20
35
+ function setBass(bass) {
36
+ return eqCommand(0x01, Math.max(0, Math.min(20, bass)));
37
+ }
38
+ // Treble: 0-20
39
+ function setTreble(treble) {
40
+ return eqCommand(0x00, Math.max(0, Math.min(20, treble)));
41
+ }
42
+ // Center volume: 0-30
43
+ function setCenterVolume(center) {
44
+ return eqCommand(0x03, Math.max(0, Math.min(30, center)));
45
+ }
46
+ // Rear volume: 0-30
47
+ function setRearVolume(rear) {
48
+ return eqCommand(0x0A, Math.max(0, Math.min(30, rear)));
49
+ }
50
+ // Mute: true=muted, false=unmuted
51
+ function setMute(muted) {
52
+ return eqCommand(0x09, muted ? 1 : 0);
53
+ }
54
+ // Quiet mode (night mode): true=on, false=off
55
+ function setQuietMode(on) {
56
+ return eqCommand(0x04, on ? 1 : 0);
57
+ }
58
+ // Balance: 0-100 (50=center)
59
+ function setBalance(balance) {
60
+ return {
61
+ characteristicUuid: settings_1.CharUUID.AudioPath,
62
+ data: formatA(0x04, 0x00, Math.max(0, Math.min(100, balance))),
63
+ };
64
+ }
65
+ // Power: true=on, false=off (standby)
66
+ function setPower(on) {
67
+ return {
68
+ characteristicUuid: settings_1.CharUUID.AudioPath,
69
+ data: formatA(0x04, 0x01, on ? 1 : 0),
70
+ };
71
+ }
72
+ // Preset (sound mode)
73
+ function setPreset(preset) {
74
+ return {
75
+ characteristicUuid: settings_1.CharUUID.EqControl,
76
+ data: formatB(0x03, preset),
77
+ };
78
+ }
79
+ // Input source
80
+ function setSource(source) {
81
+ return {
82
+ characteristicUuid: settings_1.CharUUID.Source,
83
+ data: formatB(0x07, source),
84
+ };
85
+ }
86
+ // Request full device state dump
87
+ function requestDeviceInfo() {
88
+ return {
89
+ characteristicUuid: settings_1.CharUUID.DeviceInfo,
90
+ data: formatB(0x01, 0x01),
91
+ };
92
+ }
93
+ // Request version info (AA 01 01 01 — last byte 01 distinguishes from state request)
94
+ function requestVersionInfo() {
95
+ return {
96
+ characteristicUuid: settings_1.CharUUID.DeviceInfo,
97
+ data: Buffer.from([0xAA, 0x01, 0x01, 0x01]),
98
+ };
99
+ }
100
+ // Play/Pause (Bluetooth source)
101
+ function setPlayPause(value) {
102
+ return {
103
+ characteristicUuid: settings_1.CharUUID.PlayerControl,
104
+ data: formatA(0x05, 0x00, value),
105
+ };
106
+ }
107
+ // Skip forward/backward (Bluetooth source)
108
+ function setSkip(value) {
109
+ return {
110
+ characteristicUuid: settings_1.CharUUID.PlayerControl,
111
+ data: formatA(0x05, 0x01, value),
112
+ };
113
+ }
@@ -0,0 +1,40 @@
1
+ export declare enum ResponseCode {
2
+ Volume = 1,
3
+ CenterVolume = 2,
4
+ Treble = 3,
5
+ Bass = 4,
6
+ Mute = 5,
7
+ QuietMode = 6,
8
+ Balance = 7,
9
+ Layout = 8,
10
+ Source = 9,
11
+ Power = 10,
12
+ Preset = 11,
13
+ Covering = 12,
14
+ ArmType = 13,
15
+ Subwoofer = 14,
16
+ RearVolume = 15
17
+ }
18
+ export declare enum PresetWriteValue {
19
+ TV = 5,
20
+ News = 6,
21
+ Movies = 7,
22
+ Music = 8,
23
+ Manual = 9
24
+ }
25
+ export declare enum PresetReadValue {
26
+ Movies = 0,
27
+ Music = 1,
28
+ TV = 2,
29
+ News = 3
30
+ }
31
+ export declare enum SourceValue {
32
+ HDMI = 0,
33
+ Bluetooth = 1,
34
+ AUX = 2,
35
+ Optical = 3
36
+ }
37
+ export declare function presetReadToWrite(readVal: PresetReadValue): PresetWriteValue;
38
+ export declare function presetWriteToRead(writeVal: PresetWriteValue): PresetReadValue | undefined;
39
+ export declare const PRESET_NAMES: Record<PresetReadValue, string>;
40
+ export declare const SOURCE_NAMES: Record<SourceValue, string>;
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SOURCE_NAMES = exports.PRESET_NAMES = exports.SourceValue = exports.PresetReadValue = exports.PresetWriteValue = exports.ResponseCode = void 0;
4
+ exports.presetReadToWrite = presetReadToWrite;
5
+ exports.presetWriteToRead = presetWriteToRead;
6
+ // Response codes — last 2 bytes of UpStream notifications: <code> <value>
7
+ var ResponseCode;
8
+ (function (ResponseCode) {
9
+ ResponseCode[ResponseCode["Volume"] = 1] = "Volume";
10
+ ResponseCode[ResponseCode["CenterVolume"] = 2] = "CenterVolume";
11
+ ResponseCode[ResponseCode["Treble"] = 3] = "Treble";
12
+ ResponseCode[ResponseCode["Bass"] = 4] = "Bass";
13
+ ResponseCode[ResponseCode["Mute"] = 5] = "Mute";
14
+ ResponseCode[ResponseCode["QuietMode"] = 6] = "QuietMode";
15
+ ResponseCode[ResponseCode["Balance"] = 7] = "Balance";
16
+ ResponseCode[ResponseCode["Layout"] = 8] = "Layout";
17
+ ResponseCode[ResponseCode["Source"] = 9] = "Source";
18
+ ResponseCode[ResponseCode["Power"] = 10] = "Power";
19
+ ResponseCode[ResponseCode["Preset"] = 11] = "Preset";
20
+ ResponseCode[ResponseCode["Covering"] = 12] = "Covering";
21
+ ResponseCode[ResponseCode["ArmType"] = 13] = "ArmType";
22
+ ResponseCode[ResponseCode["Subwoofer"] = 14] = "Subwoofer";
23
+ ResponseCode[ResponseCode["RearVolume"] = 15] = "RearVolume";
24
+ })(ResponseCode || (exports.ResponseCode = ResponseCode = {}));
25
+ // Preset WRITE values (sent to device)
26
+ var PresetWriteValue;
27
+ (function (PresetWriteValue) {
28
+ PresetWriteValue[PresetWriteValue["TV"] = 5] = "TV";
29
+ PresetWriteValue[PresetWriteValue["News"] = 6] = "News";
30
+ PresetWriteValue[PresetWriteValue["Movies"] = 7] = "Movies";
31
+ PresetWriteValue[PresetWriteValue["Music"] = 8] = "Music";
32
+ PresetWriteValue[PresetWriteValue["Manual"] = 9] = "Manual";
33
+ })(PresetWriteValue || (exports.PresetWriteValue = PresetWriteValue = {}));
34
+ // Preset READ values (received in notifications)
35
+ var PresetReadValue;
36
+ (function (PresetReadValue) {
37
+ PresetReadValue[PresetReadValue["Movies"] = 0] = "Movies";
38
+ PresetReadValue[PresetReadValue["Music"] = 1] = "Music";
39
+ PresetReadValue[PresetReadValue["TV"] = 2] = "TV";
40
+ PresetReadValue[PresetReadValue["News"] = 3] = "News";
41
+ })(PresetReadValue || (exports.PresetReadValue = PresetReadValue = {}));
42
+ // Source values (same for read and write)
43
+ var SourceValue;
44
+ (function (SourceValue) {
45
+ SourceValue[SourceValue["HDMI"] = 0] = "HDMI";
46
+ SourceValue[SourceValue["Bluetooth"] = 1] = "Bluetooth";
47
+ SourceValue[SourceValue["AUX"] = 2] = "AUX";
48
+ SourceValue[SourceValue["Optical"] = 3] = "Optical";
49
+ })(SourceValue || (exports.SourceValue = SourceValue = {}));
50
+ // Maps a preset read value to the corresponding write value
51
+ function presetReadToWrite(readVal) {
52
+ switch (readVal) {
53
+ case PresetReadValue.Movies: return PresetWriteValue.Movies;
54
+ case PresetReadValue.Music: return PresetWriteValue.Music;
55
+ case PresetReadValue.TV: return PresetWriteValue.TV;
56
+ case PresetReadValue.News: return PresetWriteValue.News;
57
+ default: throw new Error(`Unknown preset read value: ${readVal}`);
58
+ }
59
+ }
60
+ // Maps a preset write value to the corresponding read value
61
+ function presetWriteToRead(writeVal) {
62
+ switch (writeVal) {
63
+ case PresetWriteValue.Movies: return PresetReadValue.Movies;
64
+ case PresetWriteValue.Music: return PresetReadValue.Music;
65
+ case PresetWriteValue.TV: return PresetReadValue.TV;
66
+ case PresetWriteValue.News: return PresetReadValue.News;
67
+ default: return undefined;
68
+ }
69
+ }
70
+ exports.PRESET_NAMES = {
71
+ [PresetReadValue.Movies]: 'Movies',
72
+ [PresetReadValue.Music]: 'Music',
73
+ [PresetReadValue.TV]: 'TV',
74
+ [PresetReadValue.News]: 'News',
75
+ };
76
+ exports.SOURCE_NAMES = {
77
+ [SourceValue.HDMI]: 'HDMI-ARC',
78
+ [SourceValue.Bluetooth]: 'Bluetooth',
79
+ [SourceValue.AUX]: 'AUX',
80
+ [SourceValue.Optical]: 'Optical',
81
+ };
@@ -0,0 +1,36 @@
1
+ import { ResponseCode, PresetReadValue, SourceValue } from './constants';
2
+ export interface DeviceState {
3
+ power: boolean;
4
+ volume: number;
5
+ mute: boolean;
6
+ source: SourceValue;
7
+ preset: PresetReadValue;
8
+ quietMode: boolean;
9
+ bass: number;
10
+ treble: number;
11
+ centerVolume: number;
12
+ rearVolume: number;
13
+ balance: number;
14
+ subwooferConnected: boolean;
15
+ }
16
+ export declare function createDefaultState(): DeviceState;
17
+ export interface ParsedResponse {
18
+ code: ResponseCode;
19
+ value: number;
20
+ }
21
+ /**
22
+ * Parse a BLE notification from the UpStream characteristic.
23
+ * Returns the response code and value, or undefined if not a standard status response.
24
+ *
25
+ * Notifications have the format: CC 05/06 AA ... <code> <value>
26
+ * The last 2 bytes are always code + value for standard status responses.
27
+ * Version and OTA responses are ignored (handled separately if needed).
28
+ */
29
+ export declare function parseNotification(data: Buffer): ParsedResponse | undefined;
30
+ /**
31
+ * Apply a parsed response to the device state. Returns true if state changed.
32
+ * Out-of-range values are logged and ignored to guard against firmware bugs.
33
+ */
34
+ export declare function applyResponse(state: DeviceState, response: ParsedResponse): boolean;
35
+ /** Sentinel: applyResponse returns this for out-of-range values so the caller can log a warning. */
36
+ export declare const OUT_OF_RANGE: boolean;