homebridge
Version:
HomeKit support for the impatient
115 lines • 5.43 kB
TypeScript
/**
* Server Lifecycle Manager
*
* Handles start(), stop(), cleanup(), waitForServerReady(),
* runServer(), createServerNodeWithRecovery(), and storage setup.
*/
import type { ServerNode } from '@matter/main';
import type { MatterAccessoryCache } from '../accessoryCache.js';
import type { MatterServerConfig } from '../sharedTypes.js';
import type { CommissioningDeps, CommissioningManager } from './CommissioningManager.js';
import type { FabricManager } from './FabricManager.js';
import { Endpoint, ServerNode as MatterServerNode } from '@matter/main';
import { AggregatorEndpoint as AggregatorEndpointType } from '@matter/main/endpoints';
export interface ServerLifecycleDeps {
config: MatterServerConfig;
commissioningManager: CommissioningManager;
fabricManager: FabricManager;
getCommissioningDeps: () => CommissioningDeps;
getAccessoryCache: () => MatterAccessoryCache | null;
setAccessoryCache: (cache: MatterAccessoryCache) => void;
/**
* Rebuilds cached accessories into the aggregator. Called after the
* aggregator exists but BEFORE the server node goes online, so that a
* commissioned controller reconnecting during startup never observes a
* shrunken parts list (which makes controllers treat the missing endpoints
* as removed devices and e.g. discard their room assignments).
*/
restoreAccessoriesFromCache?: () => Promise<void>;
setServerNode: (node: ServerNode | null) => void;
getServerNode: () => ServerNode | null;
setAggregator: (agg: Endpoint<typeof AggregatorEndpointType> | null) => void;
getAggregator: () => Endpoint<typeof AggregatorEndpointType> | null;
setIsRunning: (running: boolean) => void;
getIsRunning: () => boolean;
cleanupHandlers: Array<() => void | Promise<void>>;
getShutdownHandler: () => (() => Promise<void>) | null;
setShutdownHandler: (handler: (() => Promise<void>) | null) => void;
onStop: () => Promise<void>;
}
export declare class ServerLifecycle {
matterStoragePath?: string;
/**
* Whether a ServerNode creation failure is a transient storage-lock contention
* (a previous instance still releasing the lock) rather than a hard failure.
*
* matter.js already reclaims stale locks left by a crashed owner, so only a
* live previous instance produces "locked by another process". The
* "already locked by this process" variant is a bug, not a race, so it is
* deliberately not treated as transient.
*/
private isTransientStorageLock;
/**
* Create a ServerNode, retrying on transient storage-lock contention.
*
* Without this, a quick restart where the previous process is still releasing
* the storage lock throws straight out of start(), which disables Matter for
* the entire process lifetime with no retry (a transient condition turned into
* a hard failure).
*/
private createServerNode;
/**
* Create ServerNode with automatic recovery from corrupted storage
*/
createServerNodeWithRecovery(nodeOptions: Parameters<typeof MatterServerNode.create>[0], sanitizedId: string): Promise<ServerNode>;
/**
* Set up and validate storage
*/
setupStorage(config: MatterServerConfig): Promise<MatterAccessoryCache>;
/**
* Start the Matter server
*/
start(deps: ServerLifecycleDeps): Promise<void>;
/**
* Run the server after devices have been added (for external accessory and
* deferred-online modes)
*/
runServer(deps: ServerLifecycleDeps): Promise<void>;
/**
* Start the server node, wait for it to be ready, load cache, and update commissioning info.
* Shared by both start() (non-external mode) and runServer() (deferred external mode).
*/
private startServerNode;
/**
* Wait for the server to be ready
*/
waitForServerReady(deps: ServerLifecycleDeps, maxWaitTime?: number): Promise<void>;
/**
* Stop the Matter server.
*
* External-accessory mode runs `start()` (which registers SIGINT/SIGTERM
* handlers and creates the ServerNode) but defers `runServer()` until
* after accessory registration — between those two steps `isRunning` is
* still false. A check of just `isRunning` would skip cleanup and leave
* the process handlers + half-initialised server node leaked when a
* publish failure called `stop()` in its catch block. Tear down any
* resources we actually allocated, regardless of `isRunning`.
*/
stop(deps: ServerLifecycleDeps, accessories: Map<string, any>): Promise<void>;
/**
* Cleanup resources.
*
* `preserveNodeReference` is set by stop() when `serverNode.close()` failed —
* matter.js may still be holding the port, and dropping the reference would
* leave no way to retry the close. In that case the SIGINT/SIGTERM shutdown
* handler is also kept registered: the sole caller
* (ExternalMatterAccessoryPublisher) never retries stop(), so removing the
* handler would leave the still-bound node with no graceful-shutdown hook
* for the rest of the process lifetime. The cleanupHandlers are always run
* because they are independent of the node reference.
*/
cleanup(deps: ServerLifecycleDeps, options?: {
preserveNodeReference?: boolean;
}): Promise<void>;
}
//# sourceMappingURL=ServerLifecycle.d.ts.map