import { z } from 'zod';
import { DeviceConfig } from './device-config.js';
import type { IDeviceRuntimeState } from './device-runtime-state.js';
import type { DeviceType, DeviceFeature, DeviceRole } from './device-type.js';
import type { DeviceContext } from './device-context.js';
import type { IDevice } from './device.js';
import type { ConfigUISchemaWithValues } from '../interfaces/config-ui.js';
import type { CapabilityDefinition, InferRuntimeState } from '../capabilities/capability-definition.js';
import { type DeviceLocalState } from '../generated/device-local-state.js';
export declare abstract class BaseDevice<T extends z.ZodObject<z.core.$ZodLooseShape> = z.ZodObject<z.ZodRawShape>> implements IDevice {
    readonly id: number;
    readonly stableId: string;
    readonly type: DeviceType;
    readonly name: string;
    readonly parentDeviceId: number | null;
    readonly role?: DeviceRole;
    /**
     * Cap-keyed runtime-state slice is the single source of truth for
     * `online`. Both getter and setter proxy to the slice — drivers can
     * write `this.online = true` ergonomically, and the cap event fires
     * automatically through the runtime-state writer. `markOnline()` is
     * kept as the explicit method form mandated by `IDevice`.
     */
    get online(): boolean;
    set online(value: boolean);
    /**
     * Generic per-cap runtime-state namespace. One entry per cap with
     * `runtimeState:` declared, auto-generated by codegen — see
     * `device-local-state.ts`. Drivers access via:
     *
     *   `this.state.battery.sleeping = true`        // patches the battery slice
     *   `const pct = this.state.battery.percentage` // reads the battery slice
     *   `this.state.deviceStatus.online = true`     // mirrors `markOnline(true)`
     *
     * Adding a new cap with `runtimeState:` automatically extends this
     * namespace — drivers don't have to declare proxies. Reads return
     * `undefined` when the slice hasn't been seeded; writes patch via
     * `runtimeState.patchCapState` and validate against the cap's schema
     * (so partial writes need the slice to be seeded with the required
     * fields first — drivers do this on cap registration).
     *
     * For caps not exposed in `DeviceLocalState`, drivers can build their
     * own typed proxy via `this.sliceProxy(cap)`.
     */
    get state(): DeviceLocalState;
    private _stateProxyCache?;
    abstract readonly features: readonly DeviceFeature[];
    readonly config: DeviceConfig<T>;
    /**
     * Per-device runtime state, cap-keyed. Always installed — slices
     * for individual caps materialise as those caps register their
     * native providers (`ctx.registerNativeCap`). The cap's own
     * `runtimeState` schema is the source of truth for the slice
     * shape; drivers don't redeclare it, they just write through.
     *
     * Read: `this.runtimeState.getCapState('battery')` →
     *   `{percentage, charging, sleeping, lastUpdated}` for any
     *   provider that registers `batteryCapability`.
     * Write: `this.runtimeState.setCapState('battery', { … })`.
     *
     * Cross-process consumers reach this state through the
     * `deviceState` cap router (or via cap-specific events the driver
     * emits — e.g. `battery.onStatusChanged`). The local handle is
     * accessed in-process by the driver to avoid roundtrips.
     */
    readonly runtimeState: IDeviceRuntimeState;
    readonly ctx: DeviceContext;
    /**
     * Operator-organisational location label (room / area / zone).
     * Read from `ctx.deviceMeta.location`; mutated via
     * `kernel.devices.setLocation(id, value)`. Free-text — providers
     * don't interpret it; the UI groups devices by this for filters
     * like "show me all cameras in Kitchen". `null` when unset.
     */
    readonly location: string | null;
    /**
     * Soft-disabled flag. When `true`, the device class is still
     * instantiated and visible in the UI (so the operator can flip
     * back on without re-adding) but lifecycle hooks (publishToBroker,
     * alarm-stream subscribe, …) MUST be gated by the driver to skip
     * work. The `BaseDevice` enforces this by exposing the flag here;
     * it does NOT mutate cap behaviour automatically — drivers consult
     * `this.disabled` at the top of their lifecycle methods. Read from
     * `ctx.deviceMeta.disabled`; mutated via
     * `kernel.devices.setDisabled(id, value)`.
     */
    readonly disabled: boolean;
    constructor(ctx: DeviceContext, schema: T, options: {
        type: DeviceType;
        /** Optional semantic role within parent — see `DeviceRole`. */
        role?: DeviceRole;
    });
    removeDevice(): Promise<void>;
    /**
     * Set the device's online flag. Called by `BaseDeviceProvider` after
     * aggregating per-profile stream-broker health, or directly by drivers
     * that have provider-side liveness signals (e.g. ONVIF heartbeats,
     * Reolink Baichuan firmware push events). Mirrors the new value into
     * the `device-status` runtime-state slice so cross-process consumers
     * pick it up via the standard cap-state channel. Subclasses can
     * override to gate side effects on the transition.
     */
    markOnline(online: boolean): void;
    /**
     * Re-publish the device's current `features` array to the persisted
     * meta blob. Drivers call this after a probe finishes when the live
     * `features` getter has gained new flags (e.g. `hasIntercom` flips
     * to true → `DeviceFeature.TwoWayAudio` joins the list).
     *
     * Without this, only the construction-time snapshot is written —
     * `deviceManager.registerDevice` is invoked once per boot, so probe-
     * driven additions don't reach the persisted index until the next
     * server restart, and `getDevice` / `listAll` keep returning the
     * stale list for forked-worker devices (whose live IDevice instance
     * is invisible to the hub registry).
     *
     * Idempotent: re-calling with the same features just no-ops on the
     * persisted meta. Best-effort: lookup or write failures are logged
     * at debug and swallowed — the live `device.features` getter is
     * still authoritative within this process, so callers never block
     * device boot on a meta refresh.
     */
    protected refreshFeatures(): Promise<void>;
    /**
     * Typed read-through to a cap-keyed runtime-state slice. Drivers
     * call `this.getCapSlice(batteryCapability)` and the return type
     * is inferred from the cap's `runtimeState` Zod schema — no string
     * key, no manual generic. Returns `null` when the slice hasn't
     * been written yet (e.g. driver hasn't seeded battery yet).
     */
    protected getCapSlice<TCap extends CapabilityDefinition>(cap: TCap): InferRuntimeState<TCap> | null;
    /**
     * Typed writer to a cap-keyed runtime-state slice. Routes through
     * the runtime-state writer (validate → persist → emit cap event).
     * Equivalent to `this.runtimeState.setCapState(cap.name, value)`
     * but with the cap's `runtimeState` schema enforcing the value
     * shape at compile time. Mirrors the symmetry of
     * `getCapSlice` / `setCapSlice` for cross-cap consistency.
     */
    protected setCapSlice<TCap extends CapabilityDefinition>(cap: TCap, value: InferRuntimeState<TCap> & Record<string, unknown>): void;
    /**
     * Field-level read/write proxy over a cap's runtime-state slice.
     * Drivers that want ergonomic per-field access declare:
     *
     * ```ts
     * protected battery = this.sliceProxy(batteryCapability)
     * // …
     * this.battery.sleeping = true        // patches the slice
     * const charging = this.battery.charging  // reads the slice
     * ```
     *
     * Reads return `undefined` when the slice hasn't been seeded yet
     * (cap not registered, or seeded but the field is absent). Writes
     * route through `runtimeState.patchCapState` so the cap's `runtimeState`
     * schema validates the merged result and the cap event fires.
     *
     * Pattern is generic — same shape works for `battery`, `device-status`,
     * `motion`, `doorbell`, anything with a `runtimeState:` schema. Drivers
     * declare one proxy per cap they read/write directly.
     */
    protected sliceProxy<TCap extends CapabilityDefinition>(cap: TCap): InferRuntimeState<TCap>;
    /**
     * Default empty settings UI. Drivers override this to expose an
     * editable form in the device-details page. Returning an empty sections
     * array signals "nothing to contribute" — the aggregator drops the
     * contribution entirely rather than rendering a blank panel.
     */
    getSettingsUISchema(): ConfigUISchemaWithValues;
    /**
     * Default write path: forward the flat patch directly to storage.
     * Drivers that project a UI shape different from storage (e.g. `RtspCamera`
     * exposing `mainStreamUrl`/`subStreamUrl` over `streams[]`) override this
     * to reshape before `config.setAll`.
     */
    applySettingsPatch(patch: Record<string, unknown>): Promise<void>;
    /**
     * Phase 3 — populate device-scoped state needed by downstream phases
     * (accessory reconciliation, public `features` array, optional cap
     * registration). Called ONCE per construction, after register but
     * before `getAccessoryChildren()`.
     *
     * Drivers write the `feature-probe` runtime-state slice via
     * `this.runtimeState.setCapState('feature-probe', {...})` — flag bag
     * is open (Reolink writes `hasPtz/hasIntercom`, Hikvision writes
     * `hasSupplementalLight/hasAlarmIo`, etc).
     *
     * Default: no-op (driver had no probe to run).
     */
    onProbe(): Promise<void>;
    /**
     * Phase 5 — fired after the device + its accessories are registered.
     * Drivers publish streams to the broker, kick off background tasks,
     * or subscribe to lib events that need a fully-registered device id.
     *
     * Default: no-op.
     *
     * RENAMED FROM `onCreated` (which still exists for back-compat in this
     * pass). The new name reflects the post-probe, post-accessory contract.
     */
    onActivate(): Promise<void>;
    /**
     * Re-run the probe + reconcile accessories + refresh features meta.
     * Drivers call this when device-side state changes (battery cam wakes,
     * firmware update, manual operator trigger).
     *
     * The kernel injects `_kernelReprobe` on registration so this method
     * delegates to the same orchestrator that runs the boot-time phase
     * 3 + 4 sequence. Drivers should NOT override this — they override
     * `onProbe()` instead.
     */
    reprobe(): Promise<void>;
    /**
     * Kernel-injected callback that runs the full post-probe orchestration
     * (onProbe → registerDevice meta refresh → accessory reconciliation).
     * Set by `device-cap-proxy.register()`. Drivers should not touch this
     * directly — call `reprobe()` instead.
     */
    _kernelReprobe?: () => Promise<void>;
    /**
     * Declare accessory child devices the kernel should auto-spawn
     * after `onProbe()` resolves. Each spec fully describes one child
     * — stableId suffix (deterministic per kind for restore-safety),
     * meta (type / name / location), config (initial blob the child
     * self-hydrates), and a factory that constructs the concrete
     * class with whatever closure-captured refs it needs (typically
     * `this` for the parent reference).
     *
     * The kernel handles the rest: allocateDeviceId, persistInitialConfig
     * (skipped on restore when the row already exists),
     * persistInitialMeta, createContext, factory invocation, register,
     * and recursive lifecycle (probe + accessories + activate).
     *
     * Implementations should derive children from
     * `this.runtimeState.getCapState('feature-probe')` (post-probe truth).
     * Drivers can use the `getProbeFlags()` helper to read the flag bag
     * with a typed cast.
     *
     * Default: no children.
     */
    getAccessoryChildren(): readonly AccessoryChildSpec[];
    /**
     * Read the current feature-probe flag bag with a typed cast. Helper
     * for `getAccessoryChildren()` and `features` getters that derive
     * outputs from the probe results.
     */
    protected getProbeFlags<F extends Record<string, unknown> = Record<string, unknown>>(): F;
    /**
     * Returns true once `onProbe` has completed at least once
     * (`lastProbedAt > 0`). Drivers gate `getAccessoryChildren()` on this
     * to avoid spawning stale accessories on a fresh device whose probe
     * hasn't landed yet.
     */
    protected hasProbed(): boolean;
}
/**
 * Specification for one accessory child a parent device wants the
 * kernel to auto-spawn. Returned from `BaseDevice.getAccessoryChildren()`.
 */
export interface AccessoryChildSpec {
    /**
     * Stable suffix appended to the parent's stableId to derive the
     * child's stableId (e.g. `siren` → `${parent.stableId}-siren`).
     * Must be deterministic per (parent, accessory kind) so restore
     * resolves the same numeric id and re-instantiates without
     * orphaning the persisted config.
     */
    readonly stableIdSuffix: string;
    /** Operator-organisational meta passed to `kernel.devices.create()`'s
     *  `initialMeta` arg. The kernel pre-writes the meta row before
     *  the child class constructor runs. */
    readonly meta: import('./device-management.js').InitialDeviceMeta;
    /** Initial config blob the child self-hydrates from on first
     *  create. Empty `{}` on restore (DB row carries the canonical
     *  values) — the kernel detects the existing row and skips the
     *  pre-persist step. */
    readonly config: Record<string, unknown>;
    /**
     * Constructor closure — invoked with the kernel-built context
     * after meta + config are persisted. Closures over the parent
     * reference for child classes that need `parent` as a constructor
     * arg (e.g. accessories that share the parent's connection). The
     * returned IDevice is registered + `onCreated`-fired by the
     * kernel.
     */
    readonly factory: (ctx: DeviceContext) => IDevice;
}
//# sourceMappingURL=base-device.d.ts.map