@matter-server/ws-client 1.3.1 → 1.3.2-alpha.0-20260725-35ee6c7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,4 +14,5 @@ export * from "./models/model.js";
14
14
  export * from "./models/node.js";
15
15
  export * from "./topology/topology-types.js";
16
16
  export * from "./topology/topology-utils.js";
17
+ export * from "./wire-naming.js";
17
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AAGH,cAAc,aAAa,CAAC;AAG5B,cAAc,iBAAiB,CAAC;AAGhC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AAGH,cAAc,aAAa,CAAC;AAG5B,cAAc,iBAAiB,CAAC;AAGhC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,iBAAiB,CAAC;AAGhC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AAGjC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAG7C,cAAc,kBAAkB,CAAC"}
package/dist/esm/index.js CHANGED
@@ -11,4 +11,5 @@ export * from "./models/model.js";
11
11
  export * from "./models/node.js";
12
12
  export * from "./topology/topology-types.js";
13
13
  export * from "./topology/topology-utils.js";
14
+ export * from "./wire-naming.js";
14
15
  //# sourceMappingURL=index.js.map
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.ts"],
4
- "mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAWA,cAAc;AAGd,cAAc;AAGd,cAAc;AAGd,cAAc;AAGd,cAAc;AACd,cAAc;AAGd,cAAc;AACd,cAAc;",
4
+ "mappings": "AAAA;AAAA;AAAA;AAAA;AAAA;AAWA,cAAc;AAGd,cAAc;AAGd,cAAc;AAGd,cAAc;AAGd,cAAc;AACd,cAAc;AAGd,cAAc;AACd,cAAc;AAGd,cAAc;",
5
5
  "names": []
6
6
  }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025-2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ /**
7
+ * Field-name transform shared by the Python client generator and the WebSocket
8
+ * server, so generated Python labels and wire keys come from one function.
9
+ * Pure strings — no matter.js dependency.
10
+ */
11
+ /** Well-known acronyms chip-clusters preserves as uppercase.
12
+ * ORDER MATTERS: entries are checked left-to-right via replace(). An acronym that is a
13
+ * suffix of another must come AFTER the longer one, or the short form matches first and
14
+ * leaves the remainder un-expanded. Critical ordering constraints:
15
+ * SNTP before NTP — "Ntp" is a suffix of "Sntp"
16
+ * UTC before TC — "Tc" is a suffix of "Utc"
17
+ * PIN before PI — "Pi" is a suffix of "Pin"
18
+ */
19
+ export declare const ACRONYMS: readonly ["SNTPNTS", "NTPNTS", "BLEUWB", "HVAC", "ICAC", "DAC", "MAC", "EVSE", "RFID", "PIR", "IPV", "ANSI", "BDX", "BLE", "CEC", "CO", "CSR", "DNS", "DST", "ESA", "EV", "GHG", "ICD", "IEC", "IP", "LED", "MLE", "NFC", "NOC", "SNTP", "NTP", "OTA", "PAI", "PHY", "PIN", "PI", "PV", "PAKE", "IPK", "LQI", "URI", "RF", "RMS", "UTC", "TC", "URL", "UWB", "VID", "AC", "ID"];
20
+ /** Key: matter.js PascalCase model name (pre-toChipName transform), optionally cluster-qualified
21
+ * as "ClusterName.Name". Value: expected wire/Python field name.
22
+ *
23
+ * Cluster-qualified keys ("Cluster.Field") resolve against the cluster the value is
24
+ * observed under. For a struct shared across clusters the WebSocket converter keys on the
25
+ * top-level cluster while the Python generator keys on the defining cluster — so a qualified
26
+ * override on a shared/global struct field would make the two diverge. Only add qualified
27
+ * overrides for fields owned by a single cluster. */
28
+ export declare const FIELD_NAME_OVERRIDES: Record<string, string>;
29
+ /** Convert a PascalCase matter.js name to chip-clusters PascalCase (acronym-preserving). */
30
+ export declare function toChipName(name: string): string;
31
+ /** Convert a matter.js struct-member name to its wire field name (acronym-preserving camelCase).
32
+ * Checks cluster-qualified then per-name overrides before the generic transform. */
33
+ export declare function matterNameToWireField(name: string, clusterName?: string): string;
34
+ //# sourceMappingURL=wire-naming.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire-naming.d.ts","sourceRoot":"","sources":["../../src/wire-naming.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;GAIG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,YAEjB,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,MAAM,EACN,KAAK,EACL,KAAK,EACL,MAAM,EACN,MAAM,EACN,KAAK,EACL,KAAK,EACL,MAAM,EAGN,KAAK,EACL,KAAK,EACL,KAAK,EACL,IAAI,EACJ,KAAK,EACL,KAAK,EACL,KAAK,EACL,KAAK,EACL,IAAI,EACJ,KAAK,EACL,KAAK,EACL,KAAK,EACL,IAAI,EACJ,KAAK,EACL,KAAK,EACL,KAAK,EACL,KAAK,EACL,MAAM,EACN,KAAK,EACL,KAAK,EACL,KAAK,EAEL,KAAK,EACL,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,KAAK,EACL,KAAK,EAGL,KAAK,EACL,IAAI,EACJ,KAAK,EACL,KAAK,EACL,IAAI,EACJ,KAAK,EACL,KAAK,EACL,KAAK,EACL,IAAI,EACJ,IAAI,CACE,CAAC;AAEX;;;;;;;qDAOqD;AACrD,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAkEvD,CAAC;AAIF,4FAA4F;AAC5F,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAW/C;AAED;qFACqF;AACrF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAShF"}
@@ -0,0 +1,174 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025-2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ const ACRONYMS = [
7
+ // Compound acronyms first as a precaution
8
+ "SNTPNTS",
9
+ // before SNTP and NTP
10
+ "NTPNTS",
11
+ // before NTP
12
+ "BLEUWB",
13
+ // before BLE and UWB
14
+ "HVAC",
15
+ // before AC
16
+ "ICAC",
17
+ // before AC
18
+ "DAC",
19
+ // before AC
20
+ "MAC",
21
+ // before AC
22
+ "EVSE",
23
+ // before EV
24
+ "RFID",
25
+ // before RF
26
+ "PIR",
27
+ // before PI
28
+ "IPV",
29
+ // before IP
30
+ "ANSI",
31
+ // "ARL" intentionally omitted — old pkg has both CommissioningARL (needs ARL) and Arl attr (must stay Arl)
32
+ // "ACL" intentionally omitted — chip SDK uses Acl for the AccessControl attribute (not ACL)
33
+ "BDX",
34
+ "BLE",
35
+ "CEC",
36
+ "CO",
37
+ "CSR",
38
+ "DNS",
39
+ "DST",
40
+ "ESA",
41
+ "EV",
42
+ "GHG",
43
+ "ICD",
44
+ "IEC",
45
+ "IP",
46
+ "LED",
47
+ "MLE",
48
+ "NFC",
49
+ "NOC",
50
+ "SNTP",
51
+ // before NTP (NTP is a suffix of SNTP)
52
+ "NTP",
53
+ "OTA",
54
+ "PAI",
55
+ // "PAN" intentionally omitted — causes regressions for PanId attrs in ThreadNetworkDiagnostics
56
+ "PHY",
57
+ "PIN",
58
+ "PI",
59
+ "PV",
60
+ "PAKE",
61
+ "IPK",
62
+ "LQI",
63
+ // BX/BY/GX/GY/RX/RY intentionally omitted — too broad (would turn "ByNumber" → "BYNumber").
64
+ // ColorPoint coordinate attributes are handled via FIELD_NAME_OVERRIDES instead.
65
+ "URI",
66
+ "RF",
67
+ "RMS",
68
+ "UTC",
69
+ // before TC (TC is a suffix of UTC)
70
+ "TC",
71
+ "URL",
72
+ "UWB",
73
+ "VID",
74
+ "AC",
75
+ // after HVAC, ICAC, DAC, MAC
76
+ "ID"
77
+ ];
78
+ const FIELD_NAME_OVERRIDES = {
79
+ // --- ID suffix: old chip SDK kept lowercase "Id" (not "ID") for these ---
80
+ Id: "id",
81
+ // Note: GroupId intentionally NOT overridden — most places use "groupID" (with uppercase ID).
82
+ // Only GroupKeyManagement structs use "groupId" which is a minor inconsistency.
83
+ PanId: "panId",
84
+ ExtendedPanId: "extendedPanId",
85
+ LeaderRouterId: "leaderRouterId",
86
+ PartitionId: "partitionId",
87
+ PartitionIdChangeCount: "partitionIdChangeCount",
88
+ RouterId: "routerId",
89
+ ExtendedPanIdPresent: "extendedPanIdPresent",
90
+ PanIdPresent: "panIdPresent",
91
+ AdminVendorId: "adminVendorId",
92
+ // --- NOC struct: old chip SDK used fully lowercase (no acronym expansion) ---
93
+ Icac: "icac",
94
+ Noc: "noc",
95
+ // --- MLE: old chip SDK kept mle lowercase in mid-word position ---
96
+ MleFrameCounter: "mleFrameCounter",
97
+ // --- URL vs Url: old chip SDK kept "Url" (not "URL") for these specific fields ---
98
+ DvbiUrl: "dvbiUrl",
99
+ PosterArtUrl: "posterArtUrl",
100
+ ThumbnailUrl: "thumbnailUrl",
101
+ // --- ARL: intentionally not in ACRONYMS (breaks Arl attribute) ---
102
+ CommissioningArl: "commissioningARL",
103
+ // Note: chip SDK uppercases URL here (unlike dvbiUrl/posterArtUrl which keep "Url")
104
+ ArlRequestFlowUrl: "ARLRequestFlowUrl",
105
+ // --- LQI: CHIP SDK keeps lowercase "lqi" in struct fields ---
106
+ Lqi: "lqi",
107
+ // --- CA: not in ACRONYMS (would conflict with ICAC/DAC/MAC handling) ---
108
+ RootCaCertificate: "rootCACertificate",
109
+ // --- Watermark: matter.js spells as one word, chip SDK as two ---
110
+ Watermark: "waterMark",
111
+ // --- NOCSR: not a standalone acronym in ACRONYMS ---
112
+ NocsrElements: "NOCSRElements",
113
+ // --- DraftElectricalMeasurementCluster: custom cluster uses "ac"/"rms" not "AC"/"RMS" ---
114
+ // Ac* are safe as global overrides (only this cluster has them).
115
+ // Rms* must be cluster-qualified because standard clusters use "RMS" (uppercase).
116
+ AcVoltageMultiplier: "acVoltageMultiplier",
117
+ AcVoltageDivisor: "acVoltageDivisor",
118
+ AcCurrentMultiplier: "acCurrentMultiplier",
119
+ AcCurrentDivisor: "acCurrentDivisor",
120
+ AcPowerMultiplier: "acPowerMultiplier",
121
+ AcPowerDivisor: "acPowerDivisor",
122
+ "DraftElectricalMeasurementCluster.RmsVoltage": "rmsVoltage",
123
+ "DraftElectricalMeasurementCluster.RmsCurrent": "rmsCurrent",
124
+ // --- DoorLock quirks ---
125
+ // Note: chip SDK lowercases "for" here (requirePINforRemoteOperation — not a typo)
126
+ // Key must match the Matter.js model name (RequirePinForRemoteOperation), not the
127
+ // post-toChipName version (RequirePINForRemoteOperation).
128
+ RequirePinForRemoteOperation: "requirePINforRemoteOperation",
129
+ // Note: chip SDK lowercases the "f" in "format" here (ACCapacityformat — chip SDK quirk)
130
+ AcCapacityFormat: "ACCapacityformat",
131
+ // --- IPv4/IPv6: old chip SDK used "IPv4"/"IPv6" (capital I, lowercase v) ---
132
+ // Key must match the Matter.js model name (IPv4Addresses), not post-toChipName
133
+ IPv4Addresses: "IPv4Addresses",
134
+ IPv6Addresses: "IPv6Addresses",
135
+ OffPremiseServicesReachableIPv4: "offPremiseServicesReachableIPv4",
136
+ OffPremiseServicesReachableIPv6: "offPremiseServicesReachableIPv6",
137
+ // --- ColorControl chromaticity coordinates: old chip SDK kept uppercase XY suffix ---
138
+ ColorPointBx: "colorPointBX",
139
+ ColorPointBy: "colorPointBY",
140
+ ColorPointGx: "colorPointGX",
141
+ ColorPointGy: "colorPointGY",
142
+ ColorPointRx: "colorPointRX",
143
+ ColorPointRy: "colorPointRY"
144
+ };
145
+ const chipNameCache = /* @__PURE__ */ new Map();
146
+ function toChipName(name) {
147
+ const cached = chipNameCache.get(name);
148
+ if (cached !== void 0) return cached;
149
+ let result = name;
150
+ for (const acr of ACRONYMS) {
151
+ const titleCase = acr.charAt(0) + acr.slice(1).toLowerCase();
152
+ const regex = new RegExp(`${titleCase}(?=[A-Z]|$|[^a-zA-Z]|s(?=[A-Z]|$|[^a-zA-Z]))`, "g");
153
+ result = result.replace(regex, acr);
154
+ }
155
+ chipNameCache.set(name, result);
156
+ return result;
157
+ }
158
+ function matterNameToWireField(name, clusterName) {
159
+ if (clusterName) {
160
+ const qualifiedKey = `${clusterName}.${name}`;
161
+ if (qualifiedKey in FIELD_NAME_OVERRIDES) return FIELD_NAME_OVERRIDES[qualifiedKey];
162
+ }
163
+ if (name in FIELD_NAME_OVERRIDES) return FIELD_NAME_OVERRIDES[name];
164
+ const chipName = toChipName(name);
165
+ if (/^[A-Z]{2}/.test(chipName)) return chipName;
166
+ return chipName.charAt(0).toLowerCase() + chipName.slice(1);
167
+ }
168
+ export {
169
+ ACRONYMS,
170
+ FIELD_NAME_OVERRIDES,
171
+ matterNameToWireField,
172
+ toChipName
173
+ };
174
+ //# sourceMappingURL=wire-naming.js.map
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/wire-naming.ts"],
4
+ "mappings": "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",
5
+ "names": []
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matter-server/ws-client",
3
- "version": "1.3.1",
3
+ "version": "1.3.2-alpha.0-20260725-35ee6c7",
4
4
  "description": "WebSocket client library for Matter server",
5
5
  "homepage": "https://github.com/matter-js/matterjs-server",
6
6
  "bugs": {
@@ -34,7 +34,8 @@
34
34
  "scripts": {
35
35
  "clean": "nacho-build clean",
36
36
  "build": "nacho-build",
37
- "build-clean": "nacho-build --clean"
37
+ "build-clean": "nacho-build --clean",
38
+ "test": "matter-test --force-exit"
38
39
  },
39
40
  "dependencies": {
40
41
  "@matter/thread-br-client": "0.17.7-alpha.0-20260723-8a6b27aed"
package/src/index.ts CHANGED
@@ -27,3 +27,6 @@ export * from "./models/node.js";
27
27
  // Export network topology derivation (shared by dashboard + server)
28
28
  export * from "./topology/topology-types.js";
29
29
  export * from "./topology/topology-utils.js";
30
+
31
+ // Export wire field-name transform (shared with the Python client generator)
32
+ export * from "./wire-naming.js";
@@ -0,0 +1,183 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025-2026 Open Home Foundation
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ /**
8
+ * Field-name transform shared by the Python client generator and the WebSocket
9
+ * server, so generated Python labels and wire keys come from one function.
10
+ * Pure strings — no matter.js dependency.
11
+ */
12
+
13
+ /** Well-known acronyms chip-clusters preserves as uppercase.
14
+ * ORDER MATTERS: entries are checked left-to-right via replace(). An acronym that is a
15
+ * suffix of another must come AFTER the longer one, or the short form matches first and
16
+ * leaves the remainder un-expanded. Critical ordering constraints:
17
+ * SNTP before NTP — "Ntp" is a suffix of "Sntp"
18
+ * UTC before TC — "Tc" is a suffix of "Utc"
19
+ * PIN before PI — "Pi" is a suffix of "Pin"
20
+ */
21
+ export const ACRONYMS = [
22
+ // Compound acronyms first as a precaution
23
+ "SNTPNTS", // before SNTP and NTP
24
+ "NTPNTS", // before NTP
25
+ "BLEUWB", // before BLE and UWB
26
+ "HVAC", // before AC
27
+ "ICAC", // before AC
28
+ "DAC", // before AC
29
+ "MAC", // before AC
30
+ "EVSE", // before EV
31
+ "RFID", // before RF
32
+ "PIR", // before PI
33
+ "IPV", // before IP
34
+ "ANSI",
35
+ // "ARL" intentionally omitted — old pkg has both CommissioningARL (needs ARL) and Arl attr (must stay Arl)
36
+ // "ACL" intentionally omitted — chip SDK uses Acl for the AccessControl attribute (not ACL)
37
+ "BDX",
38
+ "BLE",
39
+ "CEC",
40
+ "CO",
41
+ "CSR",
42
+ "DNS",
43
+ "DST",
44
+ "ESA",
45
+ "EV",
46
+ "GHG",
47
+ "ICD",
48
+ "IEC",
49
+ "IP",
50
+ "LED",
51
+ "MLE",
52
+ "NFC",
53
+ "NOC",
54
+ "SNTP", // before NTP (NTP is a suffix of SNTP)
55
+ "NTP",
56
+ "OTA",
57
+ "PAI",
58
+ // "PAN" intentionally omitted — causes regressions for PanId attrs in ThreadNetworkDiagnostics
59
+ "PHY",
60
+ "PIN",
61
+ "PI",
62
+ "PV",
63
+ "PAKE",
64
+ "IPK",
65
+ "LQI",
66
+ // BX/BY/GX/GY/RX/RY intentionally omitted — too broad (would turn "ByNumber" → "BYNumber").
67
+ // ColorPoint coordinate attributes are handled via FIELD_NAME_OVERRIDES instead.
68
+ "URI",
69
+ "RF",
70
+ "RMS",
71
+ "UTC", // before TC (TC is a suffix of UTC)
72
+ "TC",
73
+ "URL",
74
+ "UWB",
75
+ "VID",
76
+ "AC", // after HVAC, ICAC, DAC, MAC
77
+ "ID",
78
+ ] as const;
79
+
80
+ /** Key: matter.js PascalCase model name (pre-toChipName transform), optionally cluster-qualified
81
+ * as "ClusterName.Name". Value: expected wire/Python field name.
82
+ *
83
+ * Cluster-qualified keys ("Cluster.Field") resolve against the cluster the value is
84
+ * observed under. For a struct shared across clusters the WebSocket converter keys on the
85
+ * top-level cluster while the Python generator keys on the defining cluster — so a qualified
86
+ * override on a shared/global struct field would make the two diverge. Only add qualified
87
+ * overrides for fields owned by a single cluster. */
88
+ export const FIELD_NAME_OVERRIDES: Record<string, string> = {
89
+ // --- ID suffix: old chip SDK kept lowercase "Id" (not "ID") for these ---
90
+ Id: "id",
91
+ // Note: GroupId intentionally NOT overridden — most places use "groupID" (with uppercase ID).
92
+ // Only GroupKeyManagement structs use "groupId" which is a minor inconsistency.
93
+ PanId: "panId",
94
+ ExtendedPanId: "extendedPanId",
95
+ LeaderRouterId: "leaderRouterId",
96
+ PartitionId: "partitionId",
97
+ PartitionIdChangeCount: "partitionIdChangeCount",
98
+ RouterId: "routerId",
99
+ ExtendedPanIdPresent: "extendedPanIdPresent",
100
+ PanIdPresent: "panIdPresent",
101
+ AdminVendorId: "adminVendorId",
102
+ // --- NOC struct: old chip SDK used fully lowercase (no acronym expansion) ---
103
+ Icac: "icac",
104
+ Noc: "noc",
105
+ // --- MLE: old chip SDK kept mle lowercase in mid-word position ---
106
+ MleFrameCounter: "mleFrameCounter",
107
+ // --- URL vs Url: old chip SDK kept "Url" (not "URL") for these specific fields ---
108
+ DvbiUrl: "dvbiUrl",
109
+ PosterArtUrl: "posterArtUrl",
110
+ ThumbnailUrl: "thumbnailUrl",
111
+ // --- ARL: intentionally not in ACRONYMS (breaks Arl attribute) ---
112
+ CommissioningArl: "commissioningARL",
113
+ // Note: chip SDK uppercases URL here (unlike dvbiUrl/posterArtUrl which keep "Url")
114
+ ArlRequestFlowUrl: "ARLRequestFlowUrl",
115
+ // --- LQI: CHIP SDK keeps lowercase "lqi" in struct fields ---
116
+ Lqi: "lqi",
117
+ // --- CA: not in ACRONYMS (would conflict with ICAC/DAC/MAC handling) ---
118
+ RootCaCertificate: "rootCACertificate",
119
+ // --- Watermark: matter.js spells as one word, chip SDK as two ---
120
+ Watermark: "waterMark",
121
+ // --- NOCSR: not a standalone acronym in ACRONYMS ---
122
+ NocsrElements: "NOCSRElements",
123
+ // --- DraftElectricalMeasurementCluster: custom cluster uses "ac"/"rms" not "AC"/"RMS" ---
124
+ // Ac* are safe as global overrides (only this cluster has them).
125
+ // Rms* must be cluster-qualified because standard clusters use "RMS" (uppercase).
126
+ AcVoltageMultiplier: "acVoltageMultiplier",
127
+ AcVoltageDivisor: "acVoltageDivisor",
128
+ AcCurrentMultiplier: "acCurrentMultiplier",
129
+ AcCurrentDivisor: "acCurrentDivisor",
130
+ AcPowerMultiplier: "acPowerMultiplier",
131
+ AcPowerDivisor: "acPowerDivisor",
132
+ "DraftElectricalMeasurementCluster.RmsVoltage": "rmsVoltage",
133
+ "DraftElectricalMeasurementCluster.RmsCurrent": "rmsCurrent",
134
+ // --- DoorLock quirks ---
135
+ // Note: chip SDK lowercases "for" here (requirePINforRemoteOperation — not a typo)
136
+ // Key must match the Matter.js model name (RequirePinForRemoteOperation), not the
137
+ // post-toChipName version (RequirePINForRemoteOperation).
138
+ RequirePinForRemoteOperation: "requirePINforRemoteOperation",
139
+ // Note: chip SDK lowercases the "f" in "format" here (ACCapacityformat — chip SDK quirk)
140
+ AcCapacityFormat: "ACCapacityformat",
141
+ // --- IPv4/IPv6: old chip SDK used "IPv4"/"IPv6" (capital I, lowercase v) ---
142
+ // Key must match the Matter.js model name (IPv4Addresses), not post-toChipName
143
+ IPv4Addresses: "IPv4Addresses",
144
+ IPv6Addresses: "IPv6Addresses",
145
+ OffPremiseServicesReachableIPv4: "offPremiseServicesReachableIPv4",
146
+ OffPremiseServicesReachableIPv6: "offPremiseServicesReachableIPv6",
147
+ // --- ColorControl chromaticity coordinates: old chip SDK kept uppercase XY suffix ---
148
+ ColorPointBx: "colorPointBX",
149
+ ColorPointBy: "colorPointBY",
150
+ ColorPointGx: "colorPointGX",
151
+ ColorPointGy: "colorPointGY",
152
+ ColorPointRx: "colorPointRX",
153
+ ColorPointRy: "colorPointRY",
154
+ };
155
+
156
+ const chipNameCache = new Map<string, string>();
157
+
158
+ /** Convert a PascalCase matter.js name to chip-clusters PascalCase (acronym-preserving). */
159
+ export function toChipName(name: string): string {
160
+ const cached = chipNameCache.get(name);
161
+ if (cached !== undefined) return cached;
162
+ let result = name;
163
+ for (const acr of ACRONYMS) {
164
+ const titleCase = acr.charAt(0) + acr.slice(1).toLowerCase();
165
+ const regex = new RegExp(`${titleCase}(?=[A-Z]|$|[^a-zA-Z]|s(?=[A-Z]|$|[^a-zA-Z]))`, "g");
166
+ result = result.replace(regex, acr);
167
+ }
168
+ chipNameCache.set(name, result);
169
+ return result;
170
+ }
171
+
172
+ /** Convert a matter.js struct-member name to its wire field name (acronym-preserving camelCase).
173
+ * Checks cluster-qualified then per-name overrides before the generic transform. */
174
+ export function matterNameToWireField(name: string, clusterName?: string): string {
175
+ if (clusterName) {
176
+ const qualifiedKey = `${clusterName}.${name}`;
177
+ if (qualifiedKey in FIELD_NAME_OVERRIDES) return FIELD_NAME_OVERRIDES[qualifiedKey];
178
+ }
179
+ if (name in FIELD_NAME_OVERRIDES) return FIELD_NAME_OVERRIDES[name];
180
+ const chipName = toChipName(name);
181
+ if (/^[A-Z]{2}/.test(chipName)) return chipName;
182
+ return chipName.charAt(0).toLowerCase() + chipName.slice(1);
183
+ }