Skip to content

Remove chip-clusters dependency and auto generate API compatible classes #303

Draft
Apollon77 wants to merge 37 commits intomainfrom
generated-python-client
Draft

Remove chip-clusters dependency and auto generate API compatible classes #303
Apollon77 wants to merge 37 commits intomainfrom
generated-python-client

Conversation

@Apollon77
Copy link
Copy Markdown
Collaborator

@Apollon77 Apollon77 commented Feb 21, 2026

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-clusters Python 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

  • Eliminate external dependency: Remove dependency on home-assistant-chip-clusters (maintained by separate project)
  • Stay in sync: Generate cluster definitions from the same Matter.js model used by the server
  • Vendor cluster support: Include vendor-specific clusters (Eve, Heiman, Inovelli, Neo, ThirdReality) from @matter-server/custom-clusters
  • Simplified maintenance: Single source of truth for cluster definitions
  • API compatibility: Maintain compatibility with existing Python client code

Changes

New Files

Generator Script:

  • python_client/scripts/generate-python-clusters.ts (~1272 lines)
    • Reads Matter.js model and generates Python cluster definitions
    • Handles cluster inheritance (ModeBase, OperationalState, etc.)
    • Preserves chip-clusters acronym casing (OTA, DST, UTC, ID, VID, etc.)
    • Tracks cross-cluster type references
    • Generates one file per cluster for better code organization
    • Includes TypeScript config for IDE support

Hand-written Infrastructure:

  • python_client/chip/ChipUtility.py - classproperty descriptor
  • python_client/chip/tlv/__init__.py - TLV serialization (uint, float32, TLVWriter, TLVReader)
  • python_client/chip/tlv/tlvlist.py - TLVList class
  • python_client/chip/clusters/ClusterObjects.py - Base classes and registration dicts
  • python_client/chip/clusters/Types.py - Nullable class and NullValue singleton
  • python_client/chip/clusters/enum.py - MatterIntEnum reimplementation (replaces aenum)

Generated Files:

  • python_client/chip/clusters/objects/*.py - 121 cluster files + Globals.py
  • python_client/chip/clusters/Objects.py - Re-export for backward compatibility
  • python_client/matter_server/client/models/device_types.py - 72 device types (regenerated)

Modified Files

  • python_client/pyproject.toml:
    • Removed: home-assistant-chip-clusters, aenum
    • Added: dacite (for dataclass support)
    • Updated package discovery to include chip*
    • Added README and project URLs for PyPI
  • python_client/matter_server/client/models/node.py:
    • Added ip_addresses property (correctly spelled alias for backward compat)
  • .gitattributes:
    • Mark generated files as linguist-generated=true for GitHub UI
  • package.json:
    • Added python:generate npm script

Removed Dependencies

  • home-assistant-chip-clusters==2024.12.2 - External chip SDK Python bindings
  • aenum - Enum library (replaced with stdlib IntEnum + dynamic member injection)

Compatibility with chip-clusters==2024.11.4

The 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

  • Field-name casing (toCamelCase leading-acronym fix + FIELD_NAME_OVERRIDES): ESAType, ACCapacity, UTCTime etc. were incorrectly lowercased. Fixed by preserving leading acronyms. A FIELD_NAME_OVERRIDES map handles chip SDK inconsistencies (panId not panID, icac, ACCapacityformat, IPv4/IPv6 casing, etc.)
  • K-value casing (K_VALUE_OVERRIDES): kRollerShade and related WindowCovering TypeEnum values were incorrectly cased.
  • must_use_timed_invoke: Commands with access.timed=true now emit @ChipUtility.classproperty must_use_timed_invoke → True. Affects AdministratorCommissioning (3 commands), DoorLock (17 commands), and others.
  • ModeOptionStruct inherited fields: DishwasherMode, OvenMode, RvcCleanMode, RvcRunMode, and 5 other mode clusters had empty ModeOptionStruct because model.children doesn't include inherited fields. Fixed by switching to model.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):

Cluster Reason
BallastConfiguration Not in Matter.js 1.4 standard model
Chime Not in Matter.js 1.4 standard model
DemandResponseLoadControl Not in Matter.js 1.4 standard model
FaultInjection SDK-internal test cluster
ProxyConfiguration Not in Matter.js 1.4 standard model
ProxyDiscovery Not in Matter.js 1.4 standard model
ProxyValid Not in Matter.js 1.4 standard model
PulseWidthModulation Not in Matter.js 1.4 standard model
SampleMei SDK-internal test cluster
Timer Not in Matter.js 1.4 standard model
UnitTesting SDK-internal test cluster
WebRTCTransportProvider Not in Matter.js 1.4 standard model

Cat 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):

  • DoorLock: 20+ Dl-prefixed bitmaps/enums removed, 13 deprecated commands removed (ClearAllPINCodes, GetPINCode, Toggle, etc.) — all removed in spec 1.3
  • WindowCovering: Enums.TypeEnums.TypeEnum, Bitmaps.ConfigStatusBitmaps.ConfigStatusBitmap etc. — spec 1.4 suffix standardisation
  • Various: StatusCodeStatusCodeEnum in AdministratorCommissioning, RvcCleanMode, RvcRunMode, TimeSynchronization; PowerSource fault-change struct reclassification

Cat C3 — new in spec 1.4 (attributes/commands/events not in old SDK, present in our output):
BasicInformation.configurationVersion, GeneralCommissioning TC* attributes, NetworkCommissioning QueryIdentity + PDC feature, OperationalCredentials VID verification commands, onMode/startUpMode in mode clusters, etc.

Verification numbers

Metric Before this branch After all fixes
members missing 166 ~72
members extra 190 ~96

Remaining members missing (~72) are all Cat C1/C3 spec-version differences fully documented in docs/SPEC_DIFFERENCES.md. None of these are generator bugs.

Technical Details

Generator Features

  1. Cluster Inheritance Resolution

    • Merges base cluster members into derived clusters
    • Handles: ModeBase, OperationalState, ConcentrationMeasurement, ResourceMonitoring, AlarmBase, Label
    • Resolves command direction overrides
  2. Acronym Preservation

    • Preserves known acronyms at word boundaries: OTA, DST, UTC, NTP, ICAC, CSR, NOC, CEC, URL, PIN, ACL, VID, LED, RFID, TC, CO, EV, ID, PAKE, IPK, LQI, URI, and more
    • Examples: AnnounceOtaProviderAnnounceOTAProvider, vendorIdvendorID
    • FIELD_NAME_OVERRIDES map handles chip SDK naming inconsistencies
  3. Cross-Cluster Type References

    • Tracks and imports types from other clusters (e.g., GeneralDiagnostics.InterfaceTypeEnum)
    • Includes Globals types (e.g., Globals.Enums.enum8)
  4. File Organization

    • One Python file per cluster in chip/clusters/objects/
    • Thin re-export at chip/clusters/Objects.py for backward compatibility

MatterIntEnum Reimplementation

Eliminated aenum dependency by reimplementing MatterIntEnum using stdlib IntEnum:

  • _missing_ method returns kUnknownEnumValue for unknown values
  • extend_enum_if_value_doesnt_exist() for dynamic enum member injection
  • Fully compatible with existing code

Testing

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:generate

Breaking Changes

None. This is a drop-in replacement with compatible API.

Apollon77 and others added 8 commits February 21, 2026 13:04
…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>
Apollon77 and others added 6 commits February 23, 2026 20:48
- 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>
Copy link
Copy Markdown
Collaborator

@TheJulianJES TheJulianJES left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. Create another PR first which splits out the huge chip.clusters.Objects module into different ones, which are all imported in Objects at 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.
  2. 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).
  3. 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.: Id vs ID and kRollerShade vs kRollershade.
    • 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).

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.)

Apollon77 and others added 2 commits March 9, 2026 09:03
- 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>
Apollon77 and others added 4 commits March 11, 2026 19:44
- 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>
Apollon77 and others added 7 commits March 12, 2026 07:50
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>
TheJulianJES and others added 2 commits April 1, 2026 23:57
* 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
Apollon77 and others added 3 commits April 2, 2026 14:54
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants