Repository navigation
Release 0.18.0 on 20.10.2026 #4818
Apollon77
announced in
Announcements
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
matter.js 0.18.0 — Matter 1.6.1, New Controller API, and More Compliant Than Ever
We're excited to announce matter.js 0.18.0. It adds support for the Matter 1.6.1 specification, finalizes the new Controller API, extends out-of-the-box compliance with the Matter specification, and brings many other improvements to the JavaScript Matter SDK.
As always, huge thanks go to all the community contributors who keep making matter.js better with every release.
What's New in Version 0.18.0
Matter 1.6.1: This release adds support for all new device types, clusters, and data types introduced in the Matter 1.6.1 specification, so you can build Matter 1.6.1 devices, bridges, and controllers. All the required logic is implemented internally, so Groupcast and the Access Control features it needs are now enabled by default. If you don't want this, you can disable them explicitly.
New Controller API finalized: This version finalizes the new
ClientNode-based Controller API. It adds all the features that were still missing to allow a full migration from the legacyPairedNode-based Controller API. Both APIs use the same storage format, so you can migrate without re-commissioning your nodes. The documentation includes a migration guide. The legacy API, and with it the@project-chip/matter.jspackage, is planned for removal in v0.19.Device type validation: We improved validation and verification, especially for building devices and bridges. matter.js now checks every endpoint against its device type definition, including required clusters, features, conformance conditions, and the required number of elements. By default, findings are logged as warnings. A strict mode throws errors instead, so you can ensure full compliance while you develop. We also updated several default cluster implementations to behave as the Matter specification requires.
Automated controller testing: We already had certification test flows for devices and bridges. Now we also run automated controller testing. As usual, this runs not only against matter.js itself but also against
chip-tool, to ensure interoperability across the Matter ecosystem.Beyond these new features, this version includes many compliance, stability, and reliability improvements across the Matter protocol stack, mDNS, commissioning, and BLE, along with further improvements to the developer tooling.
Looking ahead, we'll focus on further improving specification compliance, adding support for the remaining Matter protocol features, and adding a node management layer for multi-node tasks. Stay tuned, there's much more to come!
Breaking Changes
Most changes affect details or internal APIs. If you have been using the documented public APIs in the "right way", you should see few or no breaking changes. Notable points:
with().WARNentries from the new device type validation. They point to non-compliant device structures you should fix. Enable strict mode to throw instead of logging. This is handy during development. For production you can disable the validation, because it costs some performance on larger structures. Ideally your structure is fully compliant anyway.Get Involved
Update to matter.js 0.18.0 on npm and share your feedback, experiences, and questions with us on GitHub or Discord — we're here to support you!
Changelog
Breaking: Matter 1.6.1 brings specification changes that may require code adjustments
@matter/examples
--numasks for (default 2)@matter/general
FinalizationErrorCryptocreates, signs and verifies ML-DSA-44 and ML-DSA-65 (FIPS 204) keys, natively on Node.js where available and via@noble/post-quantumotherwiseDiagnostic.Message.origin), shown in brackets after the facilityTransportClosedErrorfor an operation on a transport connection that is already closedStorageDriver.writeCoalescingIntervalnextEmitOf()waits for the next emit of anObservablewithout missing one that comes right after a state checklog.formatorLogger.formatto choose anotherStandardCryptowhere Node.js-style crypto lacks SHA-256 oraes-128-ccm, and no longer breaks the package importStandardCryptosupports computation of SHA3-256, SHA-512/224 and SHA-512/256 on every runtimedriver.jsonnames an unregistered storage driver throwsNoProviderErrorFormattedTextlist nesting, list diff markers andLifetime.detailsSymbol.metadatapolyfill no longer conflicts withlib.esnext.decoratorsin the published declarationsIpServicereports a change when a newer TXT record drops a keyWebSocketClientkeeps a message sent right after the connection opens, andconnect()rejects when the socket closes firstCache/AsyncCache.get()afterclose()throwsClosedErrorinstead of caching a value nothing closes, so a late mDNS send no longer leaves a socket bound@matter/model
FieldModel.isEnumRange)Z(obsolete) is supported (Conformance.Flag.Obsolete,isObsolete)Matter.clusters(id)drops from about 180 µs to under 1 µsValidateModelreports errors only in its result;Model.errors,Model.valid,Model.error()andResource.errorsare deprecatedpercentandpercent100thsvalues are bounded to 0–100 and 0–10000 as the specification definesOccupancySensing.HoldTime)Primary1..6attributes are optional whereNumberOfPrimariesdoes not require themMinMeasuredValueandMaxMeasuredValuedefault tonullAnnounceOtaProvideris fabric-scoped0is false, fractional defaults keep their fraction,FieldValue.Noneremoves a default, and quality definitions given as arrays load@matter/node
ColorControlServer,DoorLockServer,ElectricalEnergyMeasurementServer,LevelControlServer,ModeSelectServer,PowerSourceServer,PowerTopologyServer,SmokeCoAlarmServer,SwitchServer,ThermostatServerandWindowCoveringServerselect no features by default; select them with.with(...)typeorendProductTypeits Lift and Tilt features do not allow; with Tilt,endProductTypeis requiredendpoint.validation:off,warn(default) orstrict); see Device Type ValidationDeviceTypeConformanceError) on endpoints whose device types do not list itClusterBehavior.enable()to enable them - only allowed for developmentMinLevel1 andMaxLevel254 on eight device typesSupportedModesdescriptor.tagListschema, so subclass state is validated and persisted like the parent'sDoorLockServerenforces operating modes, wrong-code lockout and user types, and its lock command handlers areasync; hardware interaction logic belongs inhandleLockOperationLevelControlServerandColorControlServertransitionEndTimeMsis nowtransitionEndTime(aTimestamp)LevelControlServer.couple()takes the target level as third argument; overrides must pass itSoftwareUpdateManager.addUpdateConsent()andforceUpdate()take the update as an objectClientNodeattribute reads its default when mandatory andundefinedotherwise; options and environment variables no longer seed client stateClientNodeattribute always writes to the peer, even for an unchanged value; values the peer cannot express are declined locallyClientNodewrite set,stateChangeddoes not fire and$Changedfires when the transaction ends, possibly afteract()resolvesClientNodeapplies a written value only once the device accepts it; a declined write rolls back the whole transactiondiscoveryCapabilitiesmoved fromCommissioningClient.CommissioningOptionstoDiscovery.Options, declare such options asCommissioningDiscovery.Options;Discovery.Options.scannerFilteris removedundefinedDestroyedDependencyErrororCrashedDependencyError, and a concurrent attempt withFabricOperationInProgressErrorClientNodeis refused withIdentityConflictError, and is not listed inPartsListparts.delete()andparts.clear(), also onagent.parts, throwImplementationError; remove an endpoint with itsclose()ordelete()add()is closed instead of reset, so the same instance cannot be added againCommissioningClientstatesessionParametersis replaced byreportedSessionParameters,advertisedIntervalsandtcpUnsupported; stored values migrateWebRtcTransportRequestorServer.removeSession()takes a session key (id, fabric, peer node) instead of the idcodeof a status error is its id, such asnot-foundorconstraint-error+door-lock, matching the HTTPError-CodeheaderClientNode's default subscription and post-commissioning read emit peer events and change notifications; other reads return events in their result onlyRead/Subscribeused asdefaultSubscriptionis sent as given; without Basic Information and thefabricsattribute, offline and Leave detection degradeClientNodeInteraction.localStateFor()will be removed in 0.19ClientNodecontroller API (ServerNode.peers, enable/disable, connection state, split commissioning, commissioning windows); see Controller migration guideGroupcastServer), AccessControlAuxiliaryfeature, and group commands viaClientGroupendpoints and group bindingsServerNode.RootEndpointincludesGroupcastServerand AccessControlAuxiliaryby default; opt out withServerNode.RootEndpointWithoutGroupcastBehavior.reactorTimer()andBehavior.periodicReactorTimer()create timers that stop when the behavior closesBehavior.migrate(); the result is stored with its next writeBehavior.markChanged()reports an attribute a behavior computes on read to subscriptionsDoorLockServersupports schedule users,ExpiringUser,DisposableUserandForcedUser, and a lock implementation drives hardware viahandleLockOperationOtaSoftwareUpdateProviderServerstateapplyDelaysets theDelayedActionTimefor an applyDiscovery.Options.abortcancelslocate(),discover()andpase()likecancel()network.tcp.maxMessageSizesets the largest TCP message a node accepts and announces as its MAX_TCP_MESSAGE_SIZE (default 64000)locate(),commission()andpase()report a failed discovery as aDiscoveryErrorsubtype such asDeviceNotDiscoveredErrororNoDiscoveryTransportErrorMockSite,MockServerNode, …) is exported at@matter/node/testingEndpointLifecycle.isReadableandisGoneClientNode.openEnhancedCommissioningWindowalso returns the passcode, discriminator, vendor and product ID and timeoutcaseConnectionTimeout; commissioning state records the devicehostnameSoftwareUpdateManagersupportsmaxBdxBlockSizeandbdxAdditionalMrpDelay; OTA requestor query and apply delays are configurable (announcedUpdateQueryDelay,minimumQueryInterval,minimumApplyDelay)IcdClientemitscounterStart$Changedafter a registration or key refresh has committedWebRtcTransportRequestorServerreports refused signaling via itsrefusedeventstrictvalidation mode a cluster server refuses construction when a mandatory command throwsBehavior.unimplementedDclBehavior.offline(MATTER_DCL_OFFLINE) keeps the DCL services it creates off the networkCommissioningErrorfor a failedCommissioningCompletecarries the status ascauseEndpointReadFailurecarriesreceivedfor a status the device sentReceivedStatusResponseErrorfor a status the device sentincludeKnownVersionsonClientNodesubscriptions skips injecting cached data versions, also when a sustained subscription resubscribesEndpoint.act()accepts anabortsignal that ends the client commands its actor invokes, andEndpoint.getStateOf()accepts one for its readIdAssignedandNumberAssignedlifecycle changes are emitted beforeInstalledChangeNotificationService.Changehas a new kind"readable", emitted when an endpoint's state becomes readable; consumers that switch on kind must handle or ignore itBindingServer; set its state withadd()orsetStateOf(), notbehaviors.require()binding.established, and is ignored for the OTA Provider and WebRTC Transport clientsNetworkServer.portis unset (default Controller behavior)NetworkServer.State.clientCacheFlushIntervaldefaults to the storage driver'swriteCoalescingIntervaland allows to adjust the flush interval for all clients at onceIdentifyServeroffersTriggerEffectonly when the device type requires it or it is implemented or enabledZ) element; a client sends and decodes itClusterProtocol.readState()throwsImplementationErrorfor a remote session without a transactionIcdClientemitsregistered,unregisteredandkeyRefreshedafter storing the change;unregister()keeps the registration when a sleeping peer does not wakeINVALID_SUBSCRIPTIONClientNodeno longer emits$Changedwith an outdated value when a report changed an attribute during a transactionWebRtcTransportRequestorServerkeeps sessions of different peers with the same session id apartDclBehaviorboolean options set through environment variables are read as booleans.with()on a behavior subclass with own attributes or commands replaces the default behavior; an endpoint refuses two behaviors for one clusterRemoveFabricwhile another fabric's fail-safe is armed updates the fabric state, and the factory reset waits until no fail-safe is armedfabricIndexClientNodeno longer causes duplicate peers or unhandled errors; decommissioning also deletes its resumption recordpeers.commission()honours itsabortoptionDiscoveryErrordataVersionFilters, and add cached versions only for clusters the caller does not filter, under the cache's fabric filterClientNodecache forgets its data versions when the default subscription's fabric filter changes, also across restartsClientNoderead or skips the remaining events; the failure is loggedClientNodesubscription applies the caller's ownrefreshRequestwhen it resubscribesClientNoderuns at most one default subscription and reports it as not subscribed whenever it endsClientNodewith neither a known nor a discovered address reportsWaitingForDeviceDiscoveryinstead ofReconnectingPartsList, so bridged composed devices are structured correctlyAttributeListomits, records the peer'sClusterRevision, migrates values persisted under property names, and hides storage metadataaddon lists, validates peer writes, and keeps change streams across endpoint crashes and resets__proto__UNSUPPORTED_COMMANDClusterBehavior.with()rejects a feature the cluster does not define.set()variant keeps its ownStateandEvents, soCommissioningClient,IcdClientandScenesManagementServerkeep their customized nonvolatile fieldsHttpServer,MqttServerandWebSocketServerconvert environment values to their types, soMATTER_MQTT_ENABLED=falsedisables MQTTAccessControlServer.extensionEntryAccessCheckalso applies to fabrics commissioned after the node went onlineArmFailSafeover CASE answersBusyWithOtherAdminwhile any commissioning window is openConfigStatusThermostatServerfixes invalid configured setpoints and limits at startup, and accepts absolute limits narrower than the Matter defaultsPartsListreports replacements, keeps order, followsDeviceTypeListchanges, and lists every endpoint of a bridged nodeDescriptorServer.addTagsandaddDeviceTypesno longer add duplicatesStop,*WithOnOffandRemainingTimebehave as specified; level coupling no longer fails or stays blocked on locked transactionsThermostat.Presetswrites are validated, persisted and reported; atomic writes commit together, and an invalidBeginWritefails withINVALID_COMMANDDoorLockServercredential handling uses correct indices, status codes and user creationSwitchServerhandles positions, long press and multi-press as specified and no longer leaks a reactor per pressNotifyUpdateApplied, keepDelayedOnQuerywhile a provider is busy, recover fromapplyUpdatefailures, and skip updates without consentDelayedActionTimein seconds, wait for the restart only after it, and allow applying only the version the update token namesServiceAreaServerwithout the Maps feature no longer fails to initializeChangeNotificationServicereports nothing once its node is being destroyed, keeps working after a factory reset, and carriestimestampKindon eventsChangeNotificationServiceobserver is logged instead of skipping the other observers and failing the changeendpoints.sizeno longer double-counts the root endpointRemoteServerdeclarescertificateandkeyas PEM stringsImplementationErrorinstead of creating a cycleClientNodeEndpoints.require()on a runningClientNodeis reused when the peer later reports that endpointnumbera stored endpoint had gets it with a warning; the stored endpoint gets a new numberBasicInformationthroughPeerno longer leaves a state reference behind per accessVendorIdVerification.verify()returnsundefinedwhen the device rejects the signing requestsynchronous-transaction-conflictwhen another transaction holds itkeypadLockouton startup instead of resetting it toNoLockoutfabricsChanged@matter/nodejs
FileStorageDriver's constructor no longer accepts aclearargumentSqliteStorageDriverandJsonFileStorageDriverreport awriteCoalescingIntervalofInstantlog.format(MATTER_LOG_FORMAT) to force a formataes-128-ccm--storage-clear/MATTER_STORAGE_CLEARclears the storage on start againFileStorageDriver.contexts()lists only contexts that hold keysNodeJsUdpSocket.create()closes the socket when it cannot configure it@matter/nodejs-ble
NobleBleClientreports its actual scan time asBleScannerClient.listeningTime@matter/nodejs-shell
--cleanup-legacy-storageto remove leftover pre-0.16 storage artifacts@matter/protocol
NoUsableGroupKeyError(GroupKeySetMissingErrororGroupKeyNotStartedError)DclClientmaps DCL records to their schema types (empty values becomeundefined,otaFileSizeis abigint) and ignores records with unsupported schema versionsPeerDescriptor.sessionParametersis replaced byreportedSessionParameters, which holds only what the peer reported;Peer.sessionParametersprovides the effective valuesfinalizeCommissioningno longer carries the PASE session intervals; they come as the new third argumentsessionParametersSubscriptionimplementations providecloseForShutdown();Subscription.close()andSession.closeSubscriptions()end subscriptions for good (isTerminated), onlySession.closeSubscriptionsForShutdown()keeps them for re-establishmentDecodedDataReporttypes andnormalize*helpers,Invoke.LegacyCommandRequest,Specifier.ClusterTypeCommandandSessionManager.ownerwill be removed in 0.19DclClient.fetchComplianceInfo()reads a software version's compliance record, used by OTA update checksPasscodeMismatchErroron a wrong passcode to allow better user guidancePathError,ReceivedPathError,AggregatePathErrorandExpandedStatusare exportedreceivedwhen they were decoded from the peerResultStatus.errorOf()turns a status entry into the matching local or received errorWriteResult.assertSuccess()and the commissioning commands raise aReceivedStatusResponseErrorfor a status the peer sentClientRequest.abortClientRequest.largeMessagerequires a TCP-backed session for any interactionRebootResubscribeArmer.returnTimeout()returns how long an armed peer has to return once it was allowed to applyDclCertificateService,DclVendorInfoServiceandDclOtaUpdateServicecan runofflinefrom data seeds;DclCertificateServiceaccepts extra revocations (installRevocations())BleScannerClientmay reportlisteningTimeandisPeripheralReachable(), so stale or unreachable peripherals are not offered for commissioningbdxAdditionalMrpDelayNodeSession.reportedParametersholds the session parameters the peer sent, without fallbacksPeerAckMissingError, aPeerUnresponsiveErrorsubtype with its own message textMessageTooLargeErrorinstead ofNetworkErroreventFiltersarefreshRequestreturnsDclVendorInfois a deprecated alias ofVendorDclSchemaDclVendorInfoServicealways provides the built-in test vendors and no longer stores themReceived error status NotFound (code 139)D,CM,VP,DT,PH,TandICDvaluesINVALID_COMMAND, and answers handler errors withFAILUREper commandUpdateFabricLabelaccepts an empty labelSessionManagerrefuses a localmaxPathsPerInvokebelow onemaxIntervalCeilingis sent as requestedNoc.verify(),FailsafeContext.buildFabric()andFabricAuthority.createFabric()validate node ID, CAT version and vendor IDConstraintErrornames the violated constraintSustainedSubscriptionalways ends not live, whatever phase the close interrupts and even when anactive/inactiveobserver throwsFabricAuthority.createFabric()resolves once the new fabric is persisted, rejects when persisting fails, and no longer reports the new fabric as replacedInvalidNOC)deletinganddeletedonceINVALID_SUBSCRIPTION, so the device drops ittimingkick overrides take effect, and a kick can restart a connection to a last known address that discovery confirms@matter/react-native
ReactNativeBle.scannerreturns the sameBleScanneron every accessstorage.clearclears the storage on start@matter/testing
certTest()) runs controller-side tests with matter.js, chip-tool and matterjs-server controllers; see Certification controller testsMockTime.stepWallClock()steps the wall clock without moving the monotonic clockMockTime.resolveholds virtual time duringBlobreads and stops after a test timeout@matter/types
tariffComponentIDstotariffComponentIds(eight properties)Enum | numbercoupleColorTempToLevelMinMiredsandstartUpColorTemperatureMiredsare mandatory with theColorTemperaturefeatureSemtagundefinedwhere not statedClusterType()compat layer and the generatedCluster,Completeand<Name>Clusteraliases will be removed in 0.19ClusterLookupnamespace for cluster element name/ID resolutionStatusResponseError.received,StatusResponseError.idOf(),ReceivedStatusResponseandReceivedStatusResponseError.create(); eachReceived<Status>class extends its local classVendorId.isOperational(),CaseAuthenticatedTag.isValid()andNodeId.isValidCaseAuthenticatedTag()ClusterType()cluster enforces the bounds of 64-bit attributesidis always its status id, such asconstraint-errorinstead ofconstraint, orunknown-<code>for an unknown codeis()on aStatusResponseErrorsubclass matches only that subclass, soReceivedStatusResponseError.is()no longer matches a local statusstatusfield is typedStatus | <Cluster>.StatusCodeUnexpectedDataErrorCluster.with()rejects unknown features and returns one frozen namespace per selectionStatusResponseError.create()returns anUnknownStatusResponseErrorfor an unknown code instead of throwing it, and its message names the code@project-chip/matter.js
ServerNode.peers/ClientNode(see Controller migration guide)CommissioningControlleracceptsclientCacheFlushIntervalInteractionClientinvoke raises aReceivedStatusResponseErrorwith the messageReceived non-success result (code <status>)InteractionClientoptions take anabortsignalInteractionClientsends a timed write only when requested or required, so group writes workPairedNodestarts no interaction, and closing nodes or the controller completes even when a step failsCommissioningController.commissionNode()no longer waits forever for a connected node that finished initializing before it started waitingisCommissioned()skip peers whose state is not readable, such as a peer being deleted, and peers that have left the fabricAbout matter.js
matter.js is an open-source JavaScript SDK that implements the Matter IoT standard, developed by the Connectivity Standards Alliance and its members. It simplifies building Matter-compatible devices, bridges, and controllers.
matter.js is built on Node.js and runs on any operating system Node.js supports. It also works with other compatible JavaScript runtimes and platforms such as Bun, Electron, and React Native. The included shell application lets developers explore and interact with Matter devices during development and testing.
matter.js is still working toward full specification coverage, but it already supports most of the relevant functionality. Some devices built with it have been officially Matter-certified, and more certifications are underway.
Tens of thousands of users already run matter.js in open-source (though uncertified) bridges, devices, and smart home projects, demonstrating interoperability with major ecosystems such as Amazon, Apple, Aqara, Google, SmartThings, and Home Assistant.
As the heart of the new Open Home Foundation Matter Server, matter.js serves as the controller for over a million Home Assistant users.
matter.js is an open-source project maintained by the Open Home Foundation.
All reactions