/** * AWN Realtime data source — connects to `rt2.ambientweather.net` over * socket.io and pushes sensor updates as they arrive from the station * (typically every ~30 seconds for indoor units, longer outdoors). This * is the opt-in alternative to the platform's default 2-minute REST * polling loop. * * The class only delivers VALUE updates by `uniqueId` (the stable * `${mac}-${sensorKey}` identifier). It deliberately doesn't know about * HomeKit, wrappers, or naming — the platform already has the wrappers * registered after the initial REST discovery, and the realtime path * is purely "push values into existing wrappers." * * Reconnect behavior: socket.io-client's built-in reconnection is * disabled in favor of our own bounded exponential backoff. The default * client gives up after a few attempts on certain errors; we want to * retry indefinitely (the network or AWN's endpoint could be down for * extended periods). */ import type { Logger } from 'homebridge'; export interface RealtimeUpdate { uniqueId: string; /** * A PRESENT raw sensor value, forwarded verbatim to the shared * row-aware coercion/decoder boundary (PR #67 review R2-F1). Numbers * and booleans decode normally; a present-invalid value on a boolean * STATE row faults there rather than being dropped at the transport; * a present-invalid value on a numeric row is dropped by the coercer * (legacy contract). The legacy flag-off distribute path guards * `typeof number`, so non-numbers never reach a legacy wrapper. */ value: unknown; /** * HomeKit-aligned low/normal flag for the sensor's physical probe. * undefined = no battery reported for this probe; true = low; * false = normal. Polarity is already inverted from AWN's * 0=low/1=good convention at the realtime layer so the platform * doesn't need to know. */ batteryLow?: boolean; } export type RealtimeUpdateHandler = (updates: RealtimeUpdate[]) => void; export interface RealtimeOptions { applicationKey: string; apiKey: string; log: Logger; onUpdates: RealtimeUpdateHandler; /** * Optional filter for which sensor keys the realtime source emits. * Defaults to "any numeric value" — but in practice the platform * wrappers Map will drop any uniqueId it doesn't know about, so a * broad filter here costs nothing. */ isSensorKey?: (key: string) => boolean; /** * Row-aware battery-field resolver injected by the platform (the * shared `resolveBatteryField(effectiveMap, mac, dp)` reader). * Defaults to the v1.6.0 static `batteryFieldForSensor` lookup when * absent, so a bare construction keeps legacy behavior. */ resolveBatteryField?: (stationMac: string, dataPoint: string) => string | null; /** * The config's adopted catalog version (§19.6), gating the * vendor-polarity battery decode. Absent = 1, the legacy uniform * decode — a bare construction keeps historical behavior. */ catalogAdopted?: number; } export declare class RealtimeSource { private readonly opts; private socket; private reconnectTimer; private heartbeatTimer; private currentBackoff; private stopped; private updatesSinceHeartbeat; private hasEverConnected; private lastDisconnectWasClean; constructor(opts: RealtimeOptions); start(): void; stop(): void; private startHeartbeat; private connect; private scheduleReconnect; /** * Normalize whatever the WS server sends into a flat list of * (uniqueId, value) updates. The realtime endpoint can deliver * either a single device object (the typical `data` payload) or an * array of them (the `subscribed` initial state). Both shapes are * accepted; everything else is silently dropped. */ private handleDevicePayload; private normalizeToDevices; } //# sourceMappingURL=realtimeSource.d.ts.map