import { Buffer } from 'node:buffer'; /** * Encoding of Govee light "scenes" into the on-the-wire command frames that both * the AWS IoT `ptReal` command and the BLE control characteristic accept. * * The Govee app fetches a scene library per device (see `govee-content.ts`). Each * scene carries a base64 `scenceParam` blob — the raw effect definition — which the * app splits into 20-byte BLE frames before sending. Applying a scene is two steps: * * 1. transfer the effect payload as an `0xA3` multi-packet sequence, then * 2. activate it with a single `0x33 0x05 0x04 ` "set sub-mode" frame. * * The framing below is a direct port of the app's `MultipleControllerCommV1 * .makeSendBytesV2()` so that the bytes we emit are identical to the ones the * official app writes. */ /** Multi-packet protocol byte used for scene/DIY payload transfer. */ export declare const PROTOCOL_MULTI = 163; /** Protocol byte for a single-frame write command. */ export declare const PROTOCOL_WRITE = 51; /** Command type for "set mode". */ export declare const COMMAND_MODE = 5; /** * Sub-mode identifiers, from the app's `BleProtocol` constants. Music has two * generations of protocol; which one a device speaks is a per-model property in the * app, so the plugin exposes it as a config choice. */ export declare const SUB_MODE_SCENE = 4; export declare const SUB_MODE_DIY = 10; export declare const SUB_MODE_COLOUR = 11; export declare const SUB_MODE_MUSIC_LEGACY = 12; export declare const SUB_MODE_MUSIC_MODERN = 19; /** * Scene payload kinds, matching `CategoryV1.LightEffect.sceneType` in the Govee app. */ export declare const SceneType: { readonly static: 0; readonly rgb: 1; readonly rgbic: 2; readonly graffiti: 3; readonly cube: 4; readonly diy: 5; readonly compose: 6; }; /** * XOR checksum ("BCC") over the first `length` bytes of a frame — the app's * `BleUtils.getBCC()`. */ export declare function bcc(frame: Buffer, length?: number): number; /** * Build a single 20-byte write frame: protocol, command type, data, then checksum. */ export declare function buildSingleFrame(protocol: number, commandType: number, data?: number[]): Buffer; /** * Split an arbitrary payload into the app's `0xA3` multi-packet frame sequence. * * The first frame carries a header (`0x01`, total frame count, then the caller's * header bytes) followed by as much payload as fits; continuation frames carry 17 * payload bytes each and are numbered from 1; the final frame is numbered `0xFF`. * * Port of `MultipleControllerCommV1.makeSendBytesV2()`. */ export declare function buildMultiPacketFrames(protocol: number, header: number[], payload: Buffer): Buffer[]; /** * Best-effort payload-kind detection for scenes that arrive without a `sceneType`. */ export declare function detectSceneType(payload: Buffer): number; export interface EncodeSceneOptions { /** Base64 `scenceParam` from the Govee scene library. */ scenceParam: string; /** Numeric scene code used by the activation frame. */ sceneCode: number; /** `LightEffect.sceneType`; detected from the payload when omitted. */ sceneType?: number; } /** * Turn a scene library entry into the ordered list of base64 frames that apply it. * * A `static` scene (`sceneType` 0) carries no effect payload — it is applied by the * activation frame alone. */ export declare function encodeScene(options: EncodeSceneOptions): string[]; /** * Encode a DIY effect. DIY uses the same multi-packet transfer as scenes but is * activated through the DIY sub-mode with the effect's own code. */ export declare function encodeDiy(effectStr: string, diyCode: number): string[]; /** * Which generation of the music protocol a device speaks. The app keys this off an * internal per-model table; the plugin exposes it as a per-device config choice. */ export type MusicProtocol = 'modern' | 'legacy'; /** * Music effect ids differ between the two protocol generations, so effects are named * here and resolved to the right byte at encode time. `rhythm` additionally supports a * "soft" variant, carried in the dynamic byte. */ export declare const MUSIC_EFFECTS: readonly ["energic", "rolling", "spectrum", "rhythm"]; export type MusicEffect = (typeof MUSIC_EFFECTS)[number]; export interface MusicModeOptions { effect: MusicEffect; /** 0-100. */ sensitivity: number; /** When true the device picks colours itself; when false `colour` is applied. */ autoColour: boolean; colour?: { r: number; g: number; b: number; }; /** Rhythm only: the "soft" rather than "power" variant. */ soft?: boolean; protocol?: MusicProtocol; } /** * Build the music-mode frame. * * Both generations share a shape: the sub-mode, the effect id, the sensitivity, then a * colour flag — 1 when an explicit colour follows, 0 when the device should choose. Only * rhythm carries the extra "dynamic" byte selecting the soft or power variant, and its * sense is inverted between the two generations. * * Modern (`OldMusic.l()` in the app, sub-mode 0x13): * rhythm -> [sub, effect, sensitivity, soft, manualColour, r, g, b] * otherwise -> [sub, effect, sensitivity, manualColour, r, g, b] * * Legacy (`SubModeMusic.getWriteBytes()`, sub-mode 0x0c): * energic -> [sub, effect, sensitivity] * rhythm -> [sub, effect, sensitivity, !soft, manualColour, r, g, b] * otherwise -> [sub, effect, sensitivity, manualColour, r, g, b] */ export declare function encodeMusicMode(options: MusicModeOptions): string[]; /** * Join encoded frames into the comma-separated form used by the plugin config and * by the AWS `ptReal` command payload. */ export declare function framesToCode(frames: string[]): string; /** * Split a stored config code back into individual frames, tolerating whitespace. */ export declare function codeToFrames(code: string): string[];