Remove chip-clusters dependency and auto generate API compatible classes #303
Remove chip-clusters dependency and auto generate API compatible classes #303
Conversation
…js model Generate Python cluster definitions directly from MatterModel.standard, eliminating the dependency on home-assistant-chip-clusters and aenum. Changes: - Add python_client/chip/ package with hand-written infrastructure: - ChipUtility.py: classproperty descriptor - tlv/: uint, float32, TLVWriter, TLVReader - clusters/ClusterObjects.py: base classes + ALL_* registration dicts - clusters/Types.py: Nullable, NullValue - clusters/enum.py: MatterIntEnum (stdlib IntEnum, no aenum) - Add generator script python_client/scripts/generate-python-clusters.ts - Reads MatterModel.standard and generates 121 cluster files - One file per cluster in chip/clusters/objects/ - Objects.py re-exports all clusters for API compatibility - Also generates device_types.py from Matter.deviceTypes - Remove home-assistant-chip-clusters and aenum from pyproject.toml - Add dacite as explicit dependency (was transitive via chip-clusters) - Include chip* in setuptools package discovery - Add python:generate npm script All 12 Python unit tests pass. All 228 JS tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Resolve cluster inheritance: derived clusters (ModeBase, OperationalState, ConcentrationMeasurement, ResourceMonitoring, AlarmBase, Label) now inherit all base cluster members (datatypes, commands, attributes, events) - Handle command direction override: when derived cluster overrides a base command without specifying direction, use the base command's metadata - Preserve chip-clusters acronym casing: OTA, DST, UTC, NTP, CO, EV, CEC, URL, PIN, ACL, ICAC, CSR, NOC, TC, VID, LED, RFID are kept uppercase (e.g., AddNOC not AddNoc, SetDSTOffset not SetDstOffset) - Register inherited datatypes in the datatype registry for cross-references Result: 0 differences across all 121 common clusters when compared with the original home-assistant-chip-clusters package. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Clusters referencing global types (e.g., Globals.Enums.enum8) failed at runtime with NameError because the generator excluded 'Globals' from cross-cluster imports. Remove the Globals exclusion so it is imported like any other cross-cluster reference. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mark auto-generated cluster files, Objects.py, and device_types.py as linguist-generated=true to collapse them in GitHub PR diffs for easier review. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Each generated Python file now ends with exactly one newline (PEP 8). Previously, loops added blank lines after every element including the last one, resulting in two trailing newlines. Changed all element loops to only add blank lines between elements, not after the last one. Affected: cluster files (Enums, Bitmaps, Structs, Commands, Attributes, Events sections) and device_types.py. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Created tsconfig.json for python_client/scripts/ with proper module resolution (node16) and type definitions. This enables IDE support and type checking for the code generator script. Also cleaned up unused imports and parameters in the generator to pass strict TypeScript checks (noUnusedLocals, noUnusedParameters). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Removed '!' assertion on dt.id since TypeScript already knows it's defined after filtering for !== undefined. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Added 'ID' to the acronym list to ensure field names like vendorId, fabricId, nodeId get properly transformed to vendorID, fabricID, nodeID (matching chip-clusters behavior). Also updated toCamelCase() to apply acronym preservation before lowercasing the first character, ensuring camelCase field names like vendorID (not vendorId) match the original chip-clusters API. Reordered ACRONYMS list to process longer acronyms first to avoid over-matching (e.g., VID before ID). All 75 Python tests now pass including full integration tests against real Matter.js server. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add ruff, mypy, pylint configuration matching python-matter-server standards - Configure comprehensive ruff rule set with appropriate ignores for generated code - Add mypy strict type checking with overrides for generated files and tests - Fix field name casing bugs: iPv4Addresses/iPv6Addresses, LastNetworkId - Fix exception handling in connection.py to avoid type pollution - Add npm scripts for python:lint, python:lint-fix, python:typecheck - Update generator to add __all__ export list for type checkers - Update generator to fix docstring formatting (PEP 257) - Update generator to add type hint for __init_subclass__ - Generated code properly excluded from all linting/type checks - All 75 Python tests passing - Ruff: 0 errors (218 auto-fixes applied) - Mypy: 5 errors (all in tests, matching python-matter-server pattern) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add new 'lint' job that runs before unit and integration tests - Run ruff linter on python_client (excluding generated chip/ directory) - Run mypy type checker on python_client - Both unit-tests and integration-tests now depend on lint job passing Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Run ruff and mypy from within python_client directory so they pick up pyproject.toml configuration correctly. This ensures generated code in chip/ is properly excluded. Fixes: Found 3701 errors → 5 errors (only in tests) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add None check for process.stdout in test helper - Add None check for dev_info before accessing deviceType - Add assertion for client.server_info before accessing schema_version - Add return type annotation for _onoff_command helper - Add TYPE_CHECKING import for Clusters type hint - Add type ignore for aiohttp ClientWSTimeout deprecation Result: All mypy checks passing (0 errors in 157 files) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1. Add missing comparison methods to Nullable class (__le__, __gt__, __ge__) to complete ordering protocol. Maintains consistent behavior where Nullable compares as less-than-or-equal to everything. 2. Remove unnecessary pass statements from generated command classes with no fields. The descriptor method is already present, so pass is redundant. All tests passing. Addresses github-code-quality bot feedback. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
TheJulianJES
left a comment
There was a problem hiding this comment.
As of now, this isn't a drop-in-replacement, as there seem to be a lot of subtle differences in the cluster definitions. Starting HA always causes an error, and even if fixed, you just get another one afterwards. Repeat that and maybe you get it running eventually, but that'll take some time.
There also seem to be some unrelated changes in the "Matter server (client)" part, which maybe should be moved to a dedicated PR, but it's not that big of a deal.
In general, it's a huge PR and basically impossible to review diff-wise (especially comparing the old and new cluster definitions).
For this to proceed, I wonder if it makes sense to do the following – just an idea:
- Create another PR first which splits out the huge
chip.clusters.Objectsmodule into different ones, which are all imported inObjectsat the end, like done in this PR, but still with the original CHIP cluster definitions.- That diff will be huge, but it doesn't matter there, as the code will be basically the same. Just moved to different locations.
- Optional intermediate step: Move only custom clusters to be generated from Matter.js (server) definitions.
- That way, we could already continue HA development for entities based on newer custom clusters (without needing to manually define them both in Matter.js server TypeScript code and the Python custom cluster definitions).
- After the first step's PR is merged, this PR can be rebased. The diff should shrink substantially, as the cluster definitions modules/files now all match in name. So, we'd see a pure diff of old CHIP and new Matter.js-based cluster definitions.
- We are then able to check for any potential issues with the new auto-generated ones and spot all the differences, and make changes to the auto-generation. So, we can fix the issues we have now, e.g.:
IdvsIDandkRollerShadevskRollershade. - The diff between old and new definitions should also be much easier to review by AI tools, since you no longer have to try and let it read a 3 MB chip clusters file, just a (small) diff now.
- As an initial solution to those differences, I'd honestly consider just creating mappings in another file for the auto-generation, so that if the auto-generation would name something differently, we explicitly override that, FOR NOW.
- After that initial PR is merged, we can later just rename all attributes/clusters/... and update HA (or keep backwards compatible definitions as well).
- We are then able to check for any potential issues with the new auto-generated ones and spot all the differences, and make changes to the auto-generation. So, we can fix the issues we have now, e.g.:
Alternatively or additionally to the above, you could create a Python script that dumps all the cluster + attributes + types from the CHIP ones and tries to access the same ones via the Matter.js definitions instead. If it fails, it's logged and the Matter.js auto-generation can be adjusted to be more similar to the old CHIP ones.
That way, we can be 100% sure the old and new ones are fully compatible, for now.
(AI alone probably can't (properly) compare the old and new cluster definitions, since the CHIP ones are like 3 MB with 60k lines. But it should be possible for it to come up with a script like mentioned above very easily and even adjust the auto-generation accordingly then.)
- Revert classproperty caching (PR reviewer request) - Add uint, NullValue, Nullable, float32 re-exports to chip.clusters.Objects - Expand ACRONYMS list in generator (~30 new entries, correct ordering) - Fix toKName() to apply acronym expansion for compound k-values (kProgrammingPIN etc.) - Add K_VALUE_OVERRIDES map for k-values chip-clusters keeps in TitleCase (kPin, kRfid, etc.) - Add EventList (0xFFFA) as a generated global attribute on all clusters - Remove spurious pass statements from generateStruct and generateEvent - Strip Enum suffix from enum class names only in registry lookup (not class names) - Add meaningful type assertions in test_imports.py for re-exported primitives - Reduce naming mismatches vs home-assistant-chip-clusters from 581 to ~349 (remaining are Matter spec version differences between 1.4 and 1.2/1.3) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
python_client/chip/clusters/cluster_defs/ActivatedCarbonFilterMonitoring.py
Fixed
Show fixed
Hide fixed
python_client/chip/clusters/cluster_defs/ActivatedCarbonFilterMonitoring.py
Fixed
Show fixed
Hide fixed
- ACRONYMS comment now accurately states that ordering IS required (SNTP before NTP, UTC before TC, PIN before PI) because shorter forms are suffixes of longer ones - Add omission comment for ACL alongside ARL explanation - stripEnumSuffix comment now clarifies it is for registry aliases only; old chip-clusters (≤2024.11.4) KEEPS the Enum suffix in class names Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
matter.js models these as one-word "rollershade*" but chip SDK treats it as two words "RollerShade*". Add 4 K_VALUE_OVERRIDES entries to produce the correct kRollerShade, kRollerShade2Motor, kRollerShadeExterior, kRollerShadeExterior2Motor. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- toCamelCase now preserves leading acronyms (ESAType, ACCapacity, UTCTime, etc.) - Add FIELD_NAME_OVERRIDES for chip SDK inconsistencies (panId vs panID, etc.) - Add PAKE, IPK, LQI, URI to ACRONYMS list - BX/BY/GX/GY/RX/RY omitted from ACRONYMS (too broad — "ByNumber" regression) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…t casing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Commands with access.timed=true in the Matter.js model now emit the @ChipUtility.classproperty must_use_timed_invoke → True property, matching the old chip-clusters SDK convention. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ModeOptionStruct in mode clusters (DishwasherMode, OvenMode, RvcCleanMode, etc.) defines zero direct children but inherits label/mode/modeTags from the global base struct. Switching from model.children to model.members picks up these inherited fields and generates correct Python dataclasses. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When an early integration test fails (e.g. commissioning), subsequent tests that depend on node_id/test_node_id would fail with confusing errors instead of cleanly skipping. This helper surfaces the real failure without masking it. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The loop over (DishwasherMode, OvenMode) caused mypy to infer cls as type[Cluster], which has no Structs attribute. Replace with direct calls to a helper function, keeping type information concrete. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
struct: type caused attr-defined on __dataclass_fields__ since mypy doesn't know plain type objects carry dataclass metadata. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: improve CHIP SDK compatibility in Python cluster generator Systematically align generated output with CHIP SDK's chip.clusters.Objects: - Remove eventList global attribute (not in CHIP SDK) - Use uint for bitmap-typed fields (CHIP SDK convention) - Fix list element types to not wrap in Optional/Nullable - Fix kUnknownEnumValue to use first unused value from 0 - Use model.members for command/event fields (includes inherited) - Deduplicate and sort attributes by tag ID - Fix multi-bit bitmap masks for range constraints - Add CLASS_NAME_OVERRIDES + toChipClassName() for naming edge cases - Add K_VALUE_OVERRIDES for bitmap member name mismatches - Remove incorrect GroupId field-name override Also adds comparison utility scripts (compare_clusters.py, compare_summary.py) for verifying CHIP SDK compatibility. * feat: Add comparison utility scripts Adds compare_clusters.py and compare_summary.py for verifying CHIP SDK compatibility * chore: regenerate Python cluster definitions Output of running: npx tsx python_client/scripts/generate-python-clusters.ts * fix: remove redundant quotes from type annotations The generated files use `from __future__ import annotations` which already makes all annotations strings, so explicit quoting is redundant. Matches CHIP SDK style: `value: uint = 0` instead of `value: 'uint' = 0`. * chore: regenerate Python cluster definitions Output of running: npx tsx python_client/scripts/generate-python-clusters.ts * revert: keep eventList attribute and kUnknownEnumValue after-max logic eventList is part of the Matter spec and newer CHIP SDK versions include it. kUnknownEnumValue as first-unused-after-max avoids assigning semantically meaningful low values like 0. * chore: regenerate Python cluster definitions Output of running: npx tsx python_client/scripts/generate-python-clusters.ts * revert: keep bitmap types instead of replacing with uint Using the actual bitmap type (e.g., OptionsBitmap) is more type-safe than plain uint, even though the older CHIP SDK uses uint. * chore: regenerate Python cluster definitions Output of running: npx tsx python_client/scripts/generate-python-clusters.ts * fix: strip Enum suffix from class names where CHIP SDK omits it The CHIP SDK omits the "Enum" suffix for some enum class names (e.g., WindowCovering.Enums.Type not TypeEnum). Add CLASS_NAME_OVERRIDES for Type, EndProductType, ModeTag, StatusCode, SelectAreasStatus, and SkipAreaStatus. Fixes HA crash: AttributeError: type object 'Enums' has no attribute 'Type' * chore: regenerate Python cluster definitions Output of running: npx tsx python_client/scripts/generate-python-clusters.ts * fix: cluster-aware class name overrides for Enum/Bitmap suffix stripping Make toChipClassName() accept optional cluster context to support cluster-qualified overrides (e.g., "AdministratorCommissioning.StatusCodeEnum" → "StatusCode" while ValveConfigurationAndControl keeps "StatusCodeEnum"). Also strip Bitmap suffix for WindowCovering bitmaps (Mode, ConfigStatus, OperationalStatus, SafetyStatus) to match CHIP SDK. Improve compare_summary.py to detect Enum/Bitmap suffix mismatches. * chore: regenerate Python cluster definitions Output of running: npx tsx python_client/scripts/generate-python-clusters.ts * fix: rename TiltBlindLift to TiltBlindLiftAndTilt for CHIP SDK compat Matter.js uses TiltBlindLift but the CHIP SDK (and HA) expects kTiltBlindLiftAndTilt for WindowCovering.Enums.Type value 8. * chore: regenerate Python cluster definitions Output of running: npx tsx python_client/scripts/generate-python-clusters.ts * fix: use legacy DoorLock enum names (DlLockState, DlLockType, DlStatus) The CHIP SDK uses legacy "Dl" prefixed names for three DoorLock enums that differ from the Matter.js model names (LockStateEnum, LockTypeEnum, StatusCodeEnum). HA references these legacy names. * chore: regenerate Python cluster definitions Output of running: npx tsx python_client/scripts/generate-python-clusters.ts * fix: don't rename DoorLock.StatusCodeEnum to DlStatus The Matter.js model only has 2 members (Duplicate, Occupied) while the CHIP SDK's DlStatus has 7 (including kSuccess, kFailure, etc.). Renaming would expose an incomplete enum that crashes HA. * chore: regenerate Python cluster definitions Output of running: npx tsx python_client/scripts/generate-python-clusters.ts * fix: add missing DlStatus enum members via EXTRA_ENUM_MEMBERS The Matter.js model only defines 2 members for DoorLock.StatusCodeEnum (Duplicate, Occupied) but the CHIP SDK's DlStatus has 7 including kSuccess, kFailure, kInvalidField, kResourceExhausted, and kNotFound. Add EXTRA_ENUM_MEMBERS mechanism to inject additional enum members that are present in the CHIP SDK but missing from the Matter.js model. * chore: regenerate Python cluster definitions Output of running: npx tsx python_client/scripts/generate-python-clusters.ts * fix: prevent AC acronym expansion for DraftElectricalMeasurementCluster Custom cluster uses "Ac" not "AC" in attribute names (e.g., AcPowerDivisor not ACPowerDivisor). Add CLASS_NAME_OVERRIDES and FIELD_NAME_OVERRIDES. * chore: regenerate Python cluster definitions Output of running: npx tsx python_client/scripts/generate-python-clusters.ts * fix: prevent RMS acronym expansion for DraftElectricalMeasurementCluster Custom cluster uses "Rms" not "RMS" (e.g., RmsVoltage not RMSVoltage). * chore: regenerate Python cluster definitions Output of running: npx tsx python_client/scripts/generate-python-clusters.ts * fix: make Rms field/class overrides cluster-specific The DraftElectricalMeasurementCluster custom cluster uses "Rms"/"rms" while standard clusters like ElectricalPowerMeasurement use "RMS". Use cluster-qualified keys in both FIELD_NAME_OVERRIDES and CLASS_NAME_OVERRIDES, and add clusterName parameter to toCamelCase(). * chore: regenerate Python cluster definitions Output of running: npx tsx python_client/scripts/generate-python-clusters.ts * fix: add HeatingCoolingUnit device type missing from Matter.js model The original hand-maintained device_types.py included HeatingCoolingUnit (0x0300) which HA references, but it's not in the Matter.js model. Add it as an extra device type in the generator. * fix: rename kUnbolting to kUnbolt for DoorLock Feature bitmap Matter.js uses "Unbolting" but the CHIP SDK (and HA) expects "kUnbolt". * chore: regenerate Python cluster definitions Output of running: npx tsx python_client/scripts/generate-python-clusters.ts * fix: use base cluster attribute type when local override is an empty stub Derived clusters (e.g., DishwasherMode from ModeBase) can have local attribute stubs with no type or children. These stubs overrode the base cluster's full definition, causing supportedModes to resolve as List[uint] instead of List[ModeOptionStruct]. Now falls back to the base attribute when the local one has no type. * chore: regenerate Python cluster definitions Output of running: npx tsx python_client/scripts/generate-python-clusters.ts * fix: add ColorCapabilitiesBitmap missing from Matter.js model The CHIP SDK has ColorControl.Bitmaps.ColorCapabilitiesBitmap but it's not in the Matter.js model. Add EXTRA_BITMAPS mechanism to inject bitmap definitions and use it for ColorCapabilitiesBitmap. * chore: regenerate Python cluster definitions Output of running: npx tsx python_client/scripts/generate-python-clusters.ts * fix: use metatype as fallback when attribute type is undefined Attributes with no explicit type (type=undefined) but a valid metatype (e.g., float, boolean, string) were all resolving to uint. Now checks metatype before falling back to uint. Fixes EveCluster pressure/voltage/watt/altitude typed as uint instead of float32, and BridgedDeviceBasicInformation fields typed as uint instead of str/bool. * chore: regenerate Python cluster definitions Output of running: npx tsx python_client/scripts/generate-python-clusters.ts * fix: use explicit assignments in Objects.py for pylint compatibility Pylint cannot resolve names re-exported via 'from ... import' and reports E0611 (no-name-in-module) for ClusterCommand, NullValue, uint, etc. Use explicit assignments (e.g., Cluster = _co.Cluster) so pylint sees them as concrete module-level names. * fix: move base class exports into objects/__init__.py for pylint compat On macOS (case-insensitive FS), chip/clusters/Objects and chip/clusters/objects resolve to the same path, so pylint/astroid reads objects/__init__.py instead of Objects.py. Move base class and primitive type assignments (Cluster, ClusterCommand, NullValue, uint, etc.) into objects/__init__.py so they're visible to pylint on all platforms. Objects.py becomes a thin re-export for case-sensitive filesystems. * fix: rename objects/ to _cluster_defs/ to avoid macOS case collision On macOS (case-insensitive FS), chip/clusters/objects/ and chip/clusters/Objects.py resolve to the same path, causing pylint to read __init__.py instead of Objects.py. Renaming the directory to _cluster_defs/ eliminates the collision entirely. Objects.py is now a thin re-export from chip.clusters._cluster_defs. _cluster_defs/__init__.py contains base classes, primitives, and all cluster imports. HA imports are unaffected (they use chip.clusters.Objects). * refactor: simplify Objects.py and _cluster_defs/__init__.py Now that the objects/ → _cluster_defs/ rename eliminates the macOS case collision, the explicit assignment workaround in __init__.py is unnecessary. Restore simple structure: - _cluster_defs/__init__.py: just cluster imports - Objects.py: re-exports clusters + base classes + primitives * refactor: use explicit imports in Objects.py, empty __init__.py Replace wildcard import with explicit per-cluster imports in Objects.py. This eliminates all indirection — Objects.py is the single source of truth for all exports (clusters, base classes, primitives). The _cluster_defs/__init__.py is now empty (only needed for relative imports in per-cluster files). No wildcard imports means better static analysis. * Revert "refactor: use explicit imports in Objects.py, empty __init__.py" This reverts commit aaf807e. * refactor: rename _cluster_defs to cluster_defs Drop the underscore prefix — the package is imported directly by custom_clusters.py and tests, so it's not truly private. * fix: use signed int for temperature, power, energy, voltage types The generator mapped all integer types to uint, but semantic type aliases like temperature (int16), power-mW (int64), energy-mWh (int64), etc. are signed. Add these aliases to resolvePrimitiveByName and delegate the metatype "integer" case to it so both code paths resolve correctly. Fixes Thermostat setpoints, TemperatureMeasurement, DeviceEnergyManagement power values, and all other signed integer attributes. * chore: regenerate Python cluster definitions Output of running: npx tsx python_client/scripts/generate-python-clusters.ts * fix: correct FIELD_NAME_OVERRIDES keys to match Matter.js model names Override keys must use the raw Matter.js model name, not the post-toChipName version. Fixes: - RequirePinForRemoteOperation (was RequirePINForRemoteOperation) - IPv4Addresses (was Ipv4Addresses) - OffPremiseServicesReachableIPv4/IPv6 (was ...IPV4/IPV6) - ExtendedPanIdPresent/PanIdPresent (was ...PanIDPresent) * chore: regenerate Python cluster definitions Output of running: npx tsx python_client/scripts/generate-python-clusters.ts * fix: Thermostat signed types, lqi field name, kLedFeedback casing - Add SignedTemperature and TemperatureDifference to signed int aliases (fixes localTemperatureCalibration, minSetpointDeadBand, setpointChangeAmount) - Add FIELD_NAME_OVERRIDE Lqi → lqi (CHIP SDK uses lowercase) - Add K_VALUE_OVERRIDE LedFeedback → LedFeedback (prevent LED expansion) * chore: regenerate Python cluster definitions Output of running: npx tsx python_client/scripts/generate-python-clusters.ts * refactor: resolve signed int types from Matter.js model, not hardcoded Build SIGNED_INT_TYPES set by walking the Matter.js datatype hierarchy (global + cluster-level) instead of hardcoding type aliases. Any type whose base resolves to int8/int16/.../int64 is automatically recognized. * fix: pass clusterName to all generators, remove dead code - Pass clusterName to toCamelCase in generateStruct, generateCommand, generateEvent (fixes cluster-qualified FIELD_NAME_OVERRIDES not matching) - Pass clusterName to toChipClassName in generateStruct, generateCommand, generateEvent, generateBitmap (fixes cluster-qualified CLASS_NAME_OVERRIDES) - Pass "Globals" to generateEnum in generateGlobalsFile for consistency - Remove identical if/else branches in resolvePythonType and attribute field generation * chore: remove comparison scripts from tracked files The compare_clusters.py and compare_summary.py scripts were developer tools used during this work. Removed from git but kept locally. * fix: add __all__ to Objects.py for mypy export resolution mypy cannot resolve names re-exported via wildcard imports. Add a complete __all__ listing all cluster names plus base classes and primitive types so mypy sees them as explicit exports. Fixes 11 mypy errors (name-defined, attr-defined) in node.py, test_imports.py, and test_integration.py. * fix: remove unreachable None check for deviceTypeList elements deviceTypeList is typed as List[DeviceTypeStruct] (not Optional), so elements are never None. mypy correctly flags the check as unreachable. * chore: remove HeatingCoolingUnit extra device type No longer needed — removed the hardcoded HeatingCoolingUnit (0x0300) device type and the extra device types injection mechanism. * fix: use correct field names IPv4Addresses/IPv6Addresses in client The generated GeneralDiagnostics cluster uses IPv4Addresses/IPv6Addresses (matching CHIP SDK), not iPv4Addresses/iPv6Addresses. * fix: use correct attribute name LastNetworkID in client The generated NetworkCommissioning cluster uses LastNetworkID (uppercase ID from acronym expansion), not LastNetworkId. * style: inline error messages in process_helpers.py * style: apply ruff auto-fixes (FURB110, PLC0207) - client.py: simplify `f.label if f.label else None` → `f.label or None` - node.py: add maxsplit=1 to split() when only first element is used
The @command decorator doesn't set direction on custom cluster commands, causing them to be silently dropped by the generator's request/response filter. Treat undefined direction as "request" and use effectiveAccess for timed invoke check. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
As a first step now that the matter-python-client exists we also want to get rid of the chip-clusters dependency and use the matter.js model to auto generate the classes. The other benefit is that we get rid of the single huge Objects.py file and can split the classes up in own files. This is the first step to even more simplify this later, but for now we keep API compatible with the old library.
Replace home-assistant-chip-clusters with Generated Code from Matter.js Model
Overview
This PR replaces the external
home-assistant-chip-clustersPython dependency with code generated directly from the Matter.js model (MatterModel.standard). This eliminates the external dependency, ensures API compatibility, and keeps the Python client in sync with the Matter.js implementation.Motivation
home-assistant-chip-clusters(maintained by separate project)@matter-server/custom-clustersChanges
New Files
Generator Script:
python_client/scripts/generate-python-clusters.ts(~1272 lines)Hand-written Infrastructure:
python_client/chip/ChipUtility.py-classpropertydescriptorpython_client/chip/tlv/__init__.py- TLV serialization (uint,float32,TLVWriter,TLVReader)python_client/chip/tlv/tlvlist.py-TLVListclasspython_client/chip/clusters/ClusterObjects.py- Base classes and registration dictspython_client/chip/clusters/Types.py-Nullableclass andNullValuesingletonpython_client/chip/clusters/enum.py-MatterIntEnumreimplementation (replacesaenum)Generated Files:
python_client/chip/clusters/objects/*.py- 121 cluster files +Globals.pypython_client/chip/clusters/Objects.py- Re-export for backward compatibilitypython_client/matter_server/client/models/device_types.py- 72 device types (regenerated)Modified Files
python_client/pyproject.toml:home-assistant-chip-clusters,aenumdacite(for dataclass support)chip*python_client/matter_server/client/models/node.py:ip_addressesproperty (correctly spelled alias for backward compat).gitattributes:linguist-generated=truefor GitHub UIpackage.json:python:generatenpm scriptRemoved Dependencies
home-assistant-chip-clusters==2024.12.2- External chip SDK Python bindingsaenum- Enum library (replaced with stdlibIntEnum+ dynamic member injection)Compatibility with
chip-clusters==2024.11.4The comparison reference is
home-assistant-chip-clusters==2024.11.4(generated from Matter spec ~1.2/1.3). Our generator targets Matter.js which implements spec 1.4. Differences fall into intentional categories:Fixes implemented
toCamelCaseleading-acronym fix +FIELD_NAME_OVERRIDES):ESAType,ACCapacity,UTCTimeetc. were incorrectly lowercased. Fixed by preserving leading acronyms. AFIELD_NAME_OVERRIDESmap handles chip SDK inconsistencies (panIdnotpanID,icac,ACCapacityformat, IPv4/IPv6 casing, etc.)K_VALUE_OVERRIDES):kRollerShadeand related WindowCovering TypeEnum values were incorrectly cased.must_use_timed_invoke: Commands withaccess.timed=truenow emit@ChipUtility.classproperty must_use_timed_invoke → True. AffectsAdministratorCommissioning(3 commands),DoorLock(17 commands), and others.DishwasherMode,OvenMode,RvcCleanMode,RvcRunMode, and 5 other mode clusters had emptyModeOptionStructbecausemodel.childrendoesn't include inherited fields. Fixed by switching tomodel.members.Accepted differences (intentional, not bugs)
Cat A — clusters absent from our output (not in Matter.js 1.4 standard model or SDK-internal test clusters):
BallastConfigurationChimeDemandResponseLoadControlFaultInjectionProxyConfigurationProxyDiscoveryProxyValidPulseWidthModulationSampleMeiTimerUnitTestingWebRTCTransportProviderCat B — clusters in our output not in old SDK (new Matter 1.4 + custom vendor extensions):
JointFabricAdministrator,JointFabricDatastore,WaterTankLevelMonitoring, and 6 custom vendor clusters (EveCluster,HeimanCluster,InovelliCluster,NeoCluster,ThirdRealityMeteringCluster,DraftElectricalMeasurementCluster)Cat C1 — renamed/removed in spec 1.3/1.4 (documented in
docs/SPEC_DIFFERENCES.md):Dl-prefixed bitmaps/enums removed, 13 deprecated commands removed (ClearAllPINCodes,GetPINCode,Toggle, etc.) — all removed in spec 1.3Enums.Type→Enums.TypeEnum,Bitmaps.ConfigStatus→Bitmaps.ConfigStatusBitmapetc. — spec 1.4 suffix standardisationStatusCode→StatusCodeEnuminAdministratorCommissioning,RvcCleanMode,RvcRunMode,TimeSynchronization; PowerSource fault-change struct reclassificationCat C3 — new in spec 1.4 (attributes/commands/events not in old SDK, present in our output):
BasicInformation.configurationVersion,GeneralCommissioningTC* attributes,NetworkCommissioningQueryIdentity + PDC feature,OperationalCredentialsVID verification commands,onMode/startUpModein mode clusters, etc.Verification numbers
members missingmembers extraRemaining
members missing(~72) are all Cat C1/C3 spec-version differences fully documented indocs/SPEC_DIFFERENCES.md. None of these are generator bugs.Technical Details
Generator Features
Cluster Inheritance Resolution
ModeBase,OperationalState,ConcentrationMeasurement,ResourceMonitoring,AlarmBase,LabelAcronym Preservation
AnnounceOtaProvider→AnnounceOTAProvider,vendorId→vendorIDFIELD_NAME_OVERRIDESmap handles chip SDK naming inconsistenciesCross-Cluster Type References
GeneralDiagnostics.InterfaceTypeEnum)Globalstypes (e.g.,Globals.Enums.enum8)File Organization
chip/clusters/objects/chip/clusters/Objects.pyfor backward compatibilityMatterIntEnum Reimplementation
Eliminated
aenumdependency by reimplementingMatterIntEnumusing stdlibIntEnum:_missing_method returnskUnknownEnumValuefor unknown valuesextend_enum_if_value_doesnt_exist()for dynamic enum member injectionTesting
Test Coverage
✅ JavaScript Tests: 228/228
✅ Python Unit Tests: 12/12
✅ Python Integration Tests: 63/63 (real Matter.js server with device commissioning)
Migration Guide
For users: No changes required. The API is compatible.
For developers:
# Regenerate clusters after model changes npm run python:generateBreaking Changes
None. This is a drop-in replacement with compatible API.