/** * @license * Copyright 2022-2026 Matter.js Authors * SPDX-License-Identifier: Apache-2.0 */ import { Behavior } from "#behavior/Behavior.js"; import { OtaAnnouncements } from "#behavior/system/software-update/OtaAnnouncements.js"; import { Endpoint } from "#endpoint/Endpoint.js"; import type { ClientNode } from "#node/ClientNode.js"; import { Bytes, Duration, EventEmitter, Observable, ObserverGroup, Timer, Timestamp } from "@matter/general"; import { DclOtaUpdateService, FileDesignator, OtaUpdateSource, PeerAddress, PeerAddressMap, RebootResubscribeArmer, StorageScope } from "@matter/protocol"; import { VendorId } from "@matter/types"; import { OtaSoftwareUpdateProvider } from "@matter/types/clusters/ota-software-update-provider"; interface UpdateConsent extends SoftwareUpdateManager.UpdateTarget { peerAddress: PeerAddress; } interface UpdateQueueEntry extends UpdateConsent { endpoint: Endpoint; lastProgressUpdateTime?: Timestamp; lastProgressStatus?: OtaUpdateStatus; lastBdxTransferredBytes?: number; /** With `Applying`, the delay the device waits out before it applies; progress is not expected meanwhile. */ applyDelay?: Duration; } export interface OtaUpdateAvailableDetails { newSoftwareVersion: number; newSoftwareVersionString: string; fileDesignator: FileDesignator; httpsUri?: string; consentRequired: boolean; metadataForRequestor?: Bytes; } export declare enum OtaUpdateStatus { Unknown = 0, WaitForConsent = 1, Querying = 2, Downloading = 3, WaitForApply = 4, Applying = 5, Done = 6, Cancelled = 7 } export interface SoftwareUpdateInfo { /** VendorId of the updated device */ vendorId: VendorId; /** ProductId of the updated device */ productId: number; /** Software version of the updated device */ softwareVersion: number; /** Software Version String of the updated device */ softwareVersionString: string; /** ReleaseNotesUrl for the update */ releaseNotesUrl?: string; /** SpecificationVersion of the updated device */ specificationVersion?: number; /** Source of the update returned, whether it is a local file, downloaded from test or production DCL */ source: OtaUpdateSource; } export interface PendingUpdateInfo { peerAddress: PeerAddress; vendorId: VendorId; productId: number; targetSoftwareVersion: number; /** * - `"queued"` — waiting for its turn; no announcement has been sent yet. * - `"in-progress"` — the OTA provider has been announced to the node and we are waiting for it to complete. * - `"stalled"` — no status update and no BDX transfer activity within {@link OTA_PROGRESS_TIMEOUT}. The entry * will be automatically reset and retried. */ status: "queued" | "in-progress" | "stalled"; lastProgressStatus?: OtaUpdateStatus; lastProgressUpdateTime?: Timestamp; /** Bytes transferred by the BDX session of an in-progress update, undefined for any other status. */ bdxTransferredBytes?: number; /** Size of the transfer of an in-progress update, undefined for any other status or while it is unknown. */ bdxDataLength?: number; } /** * The Software Update Manager is the instance to bridge between the central OTA store and DCL service and manage the * updates for all peers of a node. It gets installed on the endpoint where the OtaSoftwareUpdateProvider behavior is * installed. It uses the generic DclOtaUpdateService, which exists globally, to request and get new updates. * * The following state allows configuring the behavior: * * {@link allowTestOtaImages}: When set to true, we also query the Test DCL additionally to the production DCL for update * and use this when a newer version is found. Default is false * * * {@link updateCheckInterval}: By default, we check the DCL for updates every 24h. This state value allows adjusting this. * * * {@link announceAsDefaultProvider}: By default, we announce ourselves as a default update provider to all nodes in the fabric. * Set to "false" if this is not wanted and updates are only pushed on availability */ export declare class SoftwareUpdateManager extends Behavior { #private; static readonly id = "softwareupdates"; readonly state: SoftwareUpdateManager.State; internal: SoftwareUpdateManager.Internal; readonly events: SoftwareUpdateManager.Events; initialize(): Promise; get storage(): import("@matter/protocol").ScopedStorage; /** * Returns a snapshot of the current update queue for introspection. */ get queuedUpdates(): PendingUpdateInfo[]; /** * Used to determine if an update is existing in our storage for a peer with a certain software version. * * It uses the already checked details and does not check again on-demand. It considers consents already given, * but validates the peer data. */ updateExistsFor(peerAddress: PeerAddress, { softwareVersion, vendorId, productId, requestorCanConsent }: OtaSoftwareUpdateProvider.QueryImageRequest): Promise; /** * Check all commissioned peers for available updates and purge stored updates that are no longer needed. * * Runs automatically on a periodic timer but may also be invoked directly to force an immediate check. */ checkForUpdates(): Promise; /** * Checks all nodes, or optionally a defined one, for available updates from the DCL OTA Update Service. * * Returns a list of peers for which updates are available along with the collected update info. * * If `includeStoredUpdates` is set to true, locally stored/imported OTA images are considered as update candidates * alongside the DCL. The DCL is always queried regardless, unless the OTA service is offline; across all candidates * the highest applicable and allowed version wins. */ queryUpdates(options?: { peerToCheck?: ClientNode; includeStoredUpdates?: boolean; }): Promise<{ peerAddress: PeerAddress; info: SoftwareUpdateInfo; }[]>; /** * Notify the application that consent is needed for the given update on the given peer */ protected requestConsentForUpdate(peerAddress: PeerAddress, updateDetails: SoftwareUpdateInfo): void; /** * Forces an OTA update for a specific node identified by its peer address, vendor ID, product ID, * and target software version. This method will override any ongoing updates by explicitly adding an update * consent for the specified node and processing the update immediately if applicable. * This can be used when an exact timing of the update is needed. When the update can be executed in a delayed/queued * manner, please use `addUpdateConsent()`. */ forceUpdate(peerAddress: PeerAddress, target: SoftwareUpdateManager.UpdateTarget): Promise; /** * How BDX should carry this peer's pending update: the options given with its consent, else the general defaults * in {@link State}. An absent value leaves the decision to the peer and its medium. * * A consent may exist before the update is queued, so both are consulted. */ bdxTransferOptionsFor(peerAddress: PeerAddress): { maxBlockSize: number | undefined; additionalMrpDelay: Duration | undefined; }; /** * Adds or updates a consent for a given peer address, vendor ID, product ID, and target software version. * Filters out existing consents for the given peer address and replaces them with the new one. * If the node associated with the peer address is applicable for an update, it schedules the update to happen with * the next queue slot, so potentially delayed. * This can be used when the update can be executed in a delayed/queued manner and it does not matter exactly when. */ addUpdateConsent(peerAddress: PeerAddress, target: SoftwareUpdateManager.UpdateTarget): Promise; /** * Checks if consent exists for the given peer address and optionally for a specific target software version. */ hasConsent(peerAddress: PeerAddress, targetSoftwareVersion?: number): boolean; /** * Checks for consent and removes it if present, also cancels if in progress. Use this to remove a formerly given * consent. */ removeConsent(peerAddress: PeerAddress, targetSoftwareVersion?: number): Promise; /** * Suppress the next startUp event from the given peer on the given session. * Called by OtaSoftwareUpdateProviderServer when a queryImage is detected as a post-reboot query * so the startUp event that follows is not treated as a new failure-after-apply. */ suppressNextStartUp(peerAddress: PeerAddress, sessionId: number): void; /** * Handles the status change of an OTA update for a given peer device. * * This method processes OTA update status notifications received from a specified device. * Based on the status, it updates the internal state of the update queue, logs relevant * messages, and triggers the necessary events. * * With `Applying`, `applyDelay` is the `DelayedActionTime` the provider allowed the apply with, which the device * waits out before it applies and restarts. */ onOtaStatusChange(peerAddress: PeerAddress, status: OtaUpdateStatus, toVersion?: number, applyDelay?: Duration): void; [Symbol.asyncDispose](): Promise; } export declare namespace SoftwareUpdateManager { class State { /** Set this to true to also allow updates from the Test DCL */ allowTestOtaImages: boolean; /** Default Update check Interval */ updateCheckInterval: Duration; /** Announce this controller as Update provider to all nodes */ announceAsDefaultProvider: boolean; /** Interval to Announces this controller as Update provider. Must not be lower than 24h, `Instant` disables. */ announcementInterval: Duration; /** * Caps the BDX block size offered for OTA transfers. Unset accepts whatever the peer requests, which is the * transport maximum. * * A smaller block multiplies round trips, and each of those is bounded by the peer's BDX response budget, so * lower this only for a peer that cannot handle full-size blocks. */ maxBdxBlockSize?: number; /** * Additive MRP retransmission margin for OTA transfers, overriding the margin the peer's medium implies. * * A BDX retransmission interval is capped so the whole schedule fits the peer's response budget, so values * above roughly 7s are inert. */ bdxAdditionalMrpDelay?: Duration; } /** Identifies the update a consent applies to, and how to transfer it. */ interface UpdateTarget { vendorId: VendorId; productId: number; targetSoftwareVersion: number; /** * Caps the BDX block size for this transfer, overriding {@link State.maxBdxBlockSize}. Unset falls back to * that default. */ maxBdxBlockSize?: number; /** * Additive MRP retransmission margin for this transfer, overriding {@link State.bdxAdditionalMrpDelay} and the * margin the peer's medium implies. Unset falls back to that default. */ bdxAdditionalMrpDelay?: Duration; } class Internal { /** Use this to pre-initialize consent to allow nodes to update automatically. The content will not be persisted! */ consents: UpdateConsent[]; otaService: DclOtaUpdateService; /** Scope of the OTA storage, captured so a lookup cannot depend on the service still being constructed. */ otaStorageScope?: StorageScope; checkForUpdateTimer: Timer; updateQueue: UpdateQueueEntry[]; updateQueueTimer?: Timer; announcements?: OtaAnnouncements; versionUpdateObservers: ObserverGroup; knownUpdates: PeerAddressMap; /** * Peer → session ID on which the reboot-triggering queryImage was received. * * Intentionally ephemeral (not persisted): the suppress window is sub-second — between the * queryImage response and the subsequent startUp event that follows on the same session. * Entries are consumed immediately when the matching startUp fires in #onSoftwareVersionChanged, * or cleaned up in #cancelUpdate when the peer is permanently removed from tracking. */ pendingStartUpSuppress: PeerAddressMap; rebootResubscribeArmer?: RebootResubscribeArmer; /** Raised whenever the node is not online, so update work cannot start before the first online transition. */ suppressUpdates: boolean; } class Events extends EventEmitter { /** Emitted when an update is available for a Peer and there is no consent stored and contains update details */ updateAvailable: Observable<[peer: PeerAddress, updateDetails: SoftwareUpdateInfo], void>; /** * Emitted when a Peer has asked to apply the image it downloaded and this provider allowed it. * * This is the last thing a Peer needs from its provider: the download is complete, the * `ApplyUpdateRequest` is answered, and what follows — applying and reporting back — is the * Peer's own. {@link updateDone} is further off, because it waits for the `NotifyUpdateApplied` * a device sends after it has restarted. */ updateApplying: Observable<[peer: PeerAddress], void>; /** Emitted when an update for a Peer is finished */ updateDone: Observable<[peer: PeerAddress], void>; /** Emitted when an update for a Peer has failed or was cancelled */ updateFailed: Observable<[peer: PeerAddress], void>; announceAsDefaultProvider$Changed: Observable<[announceAsDefaultProvider: boolean], void>; announcementInterval$Changed: Observable<[announcementInterval: Duration], void>; } } export {}; //# sourceMappingURL=SoftwareUpdateManager.d.ts.map