[
  {
    "path": ".clang-format",
    "content": "Language:        Cpp\nAccessModifierOffset: -1\nAlignAfterOpenBracket: Align\nAlignConsecutiveAssignments: false\nAlignConsecutiveDeclarations: false\nAlignEscapedNewlines: DontAlign\nAlignOperands:   true\nAlignTrailingComments: true\nAllowAllParametersOfDeclarationOnNextLine: true\nAllowShortBlocksOnASingleLine: false\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortFunctionsOnASingleLine: All\nAllowShortIfStatementsOnASingleLine: false\nAllowShortLoopsOnASingleLine: false\nAlwaysBreakAfterReturnType: None\nAlwaysBreakBeforeMultilineStrings: false\nAlwaysBreakTemplateDeclarations: MultiLine\nBinPackArguments: true\nBinPackParameters: true\nBraceWrapping:\n  AfterClass:      false\n  AfterControlStatement: false\n  AfterEnum:       false\n  AfterFunction:   false\n  AfterNamespace:  false\n  AfterObjCDeclaration: false\n  AfterStruct:     false\n  AfterUnion:      false\n  AfterExternBlock: false\n  BeforeCatch:     false\n  BeforeElse:      false\n  IndentBraces:    false\n  SplitEmptyFunction: true\n  SplitEmptyRecord: true\n  SplitEmptyNamespace: true\nBreakBeforeBinaryOperators: None\nBreakBeforeBraces: Attach\nBreakBeforeInheritanceComma: false\nBreakInheritanceList: BeforeColon\nBreakBeforeTernaryOperators: true\nBreakConstructorInitializersBeforeComma: false\nBreakConstructorInitializers: BeforeColon\nBreakAfterJavaFieldAnnotations: false\nBreakStringLiterals: true\nColumnLimit:     120\nCommentPragmas:  '^ IWYU pragma:'\nCompactNamespaces: false\nConstructorInitializerAllOnOneLineOrOnePerLine: true\nConstructorInitializerIndentWidth: 4\nContinuationIndentWidth: 4\nCpp11BracedListStyle: true\nDerivePointerAlignment: false\nDisableFormat:   false\nExperimentalAutoDetectBinPacking: false\nFixNamespaceComments: true\nForEachMacros:\n  - foreach\n  - Q_FOREACH\n  - BOOST_FOREACH\nIncludeBlocks:   Preserve\nIncludeCategories:\n  - Regex:           '^<ext/.*\\.h>'\n    Priority:        2\n  - Regex:           '^<.*\\.h>'\n    Priority:        1\n  - Regex:           '^<.*'\n    Priority:        2\n  - Regex:           '.*'\n    Priority:        3\nIncludeIsMainRegex: '([-_](test|unittest))?$'\nIndentCaseLabels: true\nIndentPPDirectives: None\nIndentWidth:     2\nIndentWrappedFunctionNames: false\nKeepEmptyLinesAtTheStartOfBlocks: false\nMacroBlockBegin: ''\nMacroBlockEnd:   ''\nMaxEmptyLinesToKeep: 1\nNamespaceIndentation: None\nPenaltyBreakAssignment: 2\nPenaltyBreakBeforeFirstCallParameter: 1\nPenaltyBreakComment: 300\nPenaltyBreakFirstLessLess: 120\nPenaltyBreakString: 1000\nPenaltyBreakTemplateDeclaration: 10\nPenaltyExcessCharacter: 1000000\nPenaltyReturnTypeOnItsOwnLine: 2000\nPointerAlignment: Right\nRawStringFormats:\n  - Language:        Cpp\n    Delimiters:\n      - cc\n      - CC\n      - cpp\n      - Cpp\n      - CPP\n      - 'c++'\n      - 'C++'\n    CanonicalDelimiter: ''\n    BasedOnStyle:    google\n  - Language:        TextProto\n    Delimiters:\n      - pb\n      - PB\n      - proto\n      - PROTO\n    EnclosingFunctions:\n      - EqualsProto\n      - EquivToProto\n      - PARSE_PARTIAL_TEXT_PROTO\n      - PARSE_TEST_PROTO\n      - PARSE_TEXT_PROTO\n      - ParseTextOrDie\n      - ParseTextProtoOrDie\n    CanonicalDelimiter: ''\n    BasedOnStyle:    google\nReflowComments:  true\nSortIncludes:    false\nSortUsingDeclarations: false\nSpaceAfterCStyleCast: true\nSpaceAfterTemplateKeyword: false\nSpaceBeforeAssignmentOperators: true\nSpaceBeforeCpp11BracedList: false\nSpaceBeforeCtorInitializerColon: true\nSpaceBeforeInheritanceColon: true\nSpaceBeforeParens: ControlStatements\nSpaceBeforeRangeBasedForLoopColon: true\nSpaceInEmptyParentheses: false\nSpacesBeforeTrailingComments: 2\nSpacesInAngles:  false\nSpacesInContainerLiterals: false\nSpacesInCStyleCastParentheses: false\nSpacesInParentheses: false\nSpacesInSquareBrackets: false\nStandard:        Auto\nTabWidth:        2\nUseTab:          Never\n"
  },
  {
    "path": ".clang-tidy",
    "content": "---\nChecks: >-\n  *,\n  -abseil-*,\n  -altera-*,\n  -android-*,\n  -boost-*,\n  -bugprone-easily-swappable-parameters,\n  -bugprone-implicit-widening-of-multiplication-result,\n  -bugprone-narrowing-conversions,\n  -bugprone-signed-char-misuse,\n  -cert-dcl50-cpp,\n  -cert-err33-c,\n  -cert-err58-cpp,\n  -cert-oop57-cpp,\n  -cert-str34-c,\n  -clang-analyzer-optin.cplusplus.UninitializedObject,\n  -clang-analyzer-osx.*,\n  -clang-diagnostic-delete-abstract-non-virtual-dtor,\n  -clang-diagnostic-delete-non-abstract-non-virtual-dtor,\n  -clang-diagnostic-ignored-optimization-argument,\n  -clang-diagnostic-shadow-field,\n  -clang-diagnostic-unused-const-variable,\n  -clang-diagnostic-unused-parameter,\n  -concurrency-*,\n  -cppcoreguidelines-avoid-c-arrays,\n  -cppcoreguidelines-avoid-magic-numbers,\n  -cppcoreguidelines-init-variables,\n  -cppcoreguidelines-macro-usage,\n  -cppcoreguidelines-narrowing-conversions,\n  -cppcoreguidelines-non-private-member-variables-in-classes,\n  -cppcoreguidelines-prefer-member-initializer,\n  -cppcoreguidelines-pro-bounds-array-to-pointer-decay,\n  -cppcoreguidelines-pro-bounds-constant-array-index,\n  -cppcoreguidelines-pro-bounds-pointer-arithmetic,\n  -cppcoreguidelines-pro-type-const-cast,\n  -cppcoreguidelines-pro-type-cstyle-cast,\n  -cppcoreguidelines-pro-type-member-init,\n  -cppcoreguidelines-pro-type-reinterpret-cast,\n  -cppcoreguidelines-pro-type-static-cast-downcast,\n  -cppcoreguidelines-pro-type-union-access,\n  -cppcoreguidelines-pro-type-vararg,\n  -cppcoreguidelines-special-member-functions,\n  -cppcoreguidelines-virtual-class-destructor,\n  -fuchsia-multiple-inheritance,\n  -fuchsia-overloaded-operator,\n  -fuchsia-statically-constructed-objects,\n  -fuchsia-default-arguments-declarations,\n  -fuchsia-default-arguments-calls,\n  -google-build-using-namespace,\n  -google-explicit-constructor,\n  -google-readability-braces-around-statements,\n  -google-readability-casting,\n  -google-readability-namespace-comments,\n  -google-readability-todo,\n  -google-runtime-references,\n  -hicpp-*,\n  -llvm-else-after-return,\n  -llvm-header-guard,\n  -llvm-include-order,\n  -llvm-qualified-auto,\n  -llvmlibc-*,\n  -misc-non-private-member-variables-in-classes,\n  -misc-no-recursion,\n  -misc-unused-parameters,\n  -modernize-avoid-c-arrays,\n  -modernize-avoid-bind,\n  -modernize-concat-nested-namespaces,\n  -modernize-return-braced-init-list,\n  -modernize-use-auto,\n  -modernize-use-default-member-init,\n  -modernize-use-equals-default,\n  -modernize-use-trailing-return-type,\n  -modernize-use-nodiscard,\n  -mpi-*,\n  -objc-*,\n  -readability-container-data-pointer,\n  -readability-convert-member-functions-to-static,\n  -readability-else-after-return,\n  -readability-function-cognitive-complexity,\n  -readability-implicit-bool-conversion,\n  -readability-isolate-declaration,\n  -readability-magic-numbers,\n  -readability-make-member-function-const,\n  -readability-redundant-string-init,\n  -readability-uppercase-literal-suffix,\n  -readability-use-anyofallof,\nWarningsAsErrors: '*'\nAnalyzeTemporaryDtors: false\nFormatStyle:     google\nCheckOptions:\n  - key:             google-readability-function-size.StatementThreshold\n    value:           '800'\n  - key:             google-runtime-int.TypeSuffix\n    value:           '_t'\n  - key:             llvm-namespace-comment.ShortNamespaceLines\n    value:           '10'\n  - key:             llvm-namespace-comment.SpacesBeforeComments\n    value:           '2'\n  - key:             modernize-loop-convert.MaxCopySize\n    value:           '16'\n  - key:             modernize-loop-convert.MinConfidence\n    value:           reasonable\n  - key:             modernize-loop-convert.NamingStyle\n    value:           CamelCase\n  - key:             modernize-pass-by-value.IncludeStyle\n    value:           llvm\n  - key:             modernize-replace-auto-ptr.IncludeStyle\n    value:           llvm\n  - key:             modernize-use-nullptr.NullMacros\n    value:           'NULL'\n  - key:             modernize-make-unique.MakeSmartPtrFunction\n    value:           'make_unique'\n  - key:             modernize-make-unique.MakeSmartPtrFunctionHeader\n    value:           'esphome/core/helpers.h'\n  - key:             readability-braces-around-statements.ShortStatementLines\n    value:           2\n  - key:             readability-identifier-naming.LocalVariableCase\n    value:           'lower_case'\n  - key:             readability-identifier-naming.ClassCase\n    value:           'CamelCase'\n  - key:             readability-identifier-naming.StructCase\n    value:           'CamelCase'\n  - key:             readability-identifier-naming.EnumCase\n    value:           'CamelCase'\n  - key:             readability-identifier-naming.EnumConstantCase\n    value:           'UPPER_CASE'\n  - key:             readability-identifier-naming.StaticConstantCase\n    value:           'UPPER_CASE'\n  - key:             readability-identifier-naming.StaticVariableCase\n    value:           'lower_case'\n  - key:             readability-identifier-naming.GlobalConstantCase\n    value:           'UPPER_CASE'\n  - key:             readability-identifier-naming.ParameterCase\n    value:           'lower_case'\n  - key:             readability-identifier-naming.PrivateMemberCase\n    value:           'lower_case'\n  - key:             readability-identifier-naming.PrivateMemberSuffix\n    value:           '_'\n  - key:             readability-identifier-naming.PrivateMethodCase\n    value:           'lower_case'\n  - key:             readability-identifier-naming.PrivateMethodSuffix\n    value:           '_'\n  - key:             readability-identifier-naming.ClassMemberCase\n    value:           'lower_case'\n  - key:             readability-identifier-naming.ClassMemberCase\n    value:           'lower_case'\n  - key:             readability-identifier-naming.ProtectedMemberCase\n    value:           'lower_case'\n  - key:             readability-identifier-naming.ProtectedMemberSuffix\n    value:           '_'\n  - key:             readability-identifier-naming.FunctionCase\n    value:           'lower_case'\n  - key:             readability-identifier-naming.ClassMethodCase\n    value:           'lower_case'\n  - key:             readability-identifier-naming.ProtectedMethodCase\n    value:           'lower_case'\n  - key:             readability-identifier-naming.ProtectedMethodSuffix\n    value:           '_'\n  - key:             readability-identifier-naming.VirtualMethodCase\n    value:           'lower_case'\n  - key:             readability-identifier-naming.VirtualMethodSuffix\n    value:           ''\n  - key:             readability-qualified-auto.AddConstToQualified\n    value:           0\n  - key:             readability-identifier-length.MinimumVariableNameLength\n    value:           0\n  - key:             readability-identifier-length.MinimumParameterNameLength\n    value:           0\n  - key:             readability-identifier-length.MinimumLoopCounterNameLength\n    value:           0\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n# general\n[*]\nend_of_line = lf\ninsert_final_newline = true\ncharset = utf-8\n\n# python\n[*.py]\nindent_style = space\nindent_size = 4\n\n# C++\n[*.{cpp,h,tcc}]\nindent_style = space\nindent_size = 2\n\n# Web\n[*.{js,html,css}]\nindent_style = space\nindent_size = 2\n\n# YAML\n[*.{yaml,yml}]\nindent_style = space\nindent_size = 2\nquote_type = double\n\n# JSON\n[*.json]\nindent_style = space\nindent_size = 2\n"
  },
  {
    "path": ".flake8",
    "content": "[flake8]\nmax-line-length = 120\n# Following 4 for black compatibility\n# E501: line too long\n# W503: Line break occurred before a binary operator\n# E203: Whitespace before ':'\n# D202 No blank lines allowed after function docstring\n\n# TODO fix flake8\n# D100 Missing docstring in public module\n# D101 Missing docstring in public class\n# D102 Missing docstring in public method\n# D103 Missing docstring in public function\n# D104 Missing docstring in public package\n# D105 Missing docstring in magic method\n# D107 Missing docstring in __init__\n# D200 One-line docstring should fit on one line with quotes\n# D205 1 blank line required between summary line and description\n# D209 Multi-line docstring closing quotes should be on a separate line\n# D400 First line should end with a period\n# D401 First line should be in imperative mood\n\nignore =\n  E501,\n  W503,\n  E203,\n  D202,\n\n  D100,\n  D101,\n  D102,\n  D103,\n  D104,\n  D105,\n  D107,\n  D200,\n  D205,\n  D209,\n  D400,\n  D401,\n\nexclude = api_pb2.py\n"
  },
  {
    "path": ".gitattributes",
    "content": "# Normalize line endings to LF in the repository\n* text eol=lf\n*.png binary\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\n#github: luar123 # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\n#patreon: # Replace with a single Patreon username\n#open_collective: # Replace with a single Open Collective username\n#ko_fi: # Replace with a single Ko-fi username\n#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\n#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\n#liberapay: # Replace with a single Liberapay username\n#issuehunt: # Replace with a single IssueHunt username\n#lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\n#polar: # Replace with a single Polar username\nbuy_me_a_coffee: luar123\n#thanks_dev: # Replace with a single thanks.dev username\n#custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".gitignore",
    "content": "# Gitignore settings for ESPHome\n# This is an example and may include too much for your use-case.\n# You can modify this file to suit your needs.\n/.esphome/\n/secrets.yaml\n/.vscode/\n__pycache__/\n/components/zigbee/files_to_parse/*.c\n/components/zigbee/files_to_parse/*.h\n/components/zigbee/files_to_parse/zigbee_const.py\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "---\n# See https://pre-commit.com for more information\n# See https://pre-commit.com/hooks.html for more hooks\nrepos:\n  - repo: https://github.com/astral-sh/ruff-pre-commit\n    # Ruff version.\n    rev: v0.5.4\n    hooks:\n      # Run the linter.\n      - id: ruff\n        args: [--fix]\n      # Run the formatter.\n      - id: ruff-format\n  - repo: https://github.com/psf/black-pre-commit-mirror\n    rev: 24.4.2\n    hooks:\n      - id: black\n        args:\n          - --safe\n          - --quiet\n        files: ^((esphome|script|tests)/.+)?[^/]+\\.py$\n  - repo: https://github.com/PyCQA/flake8\n    rev: 6.1.0\n    hooks:\n      - id: flake8\n        additional_dependencies:\n          - flake8-docstrings==1.5.0\n          - pydocstyle==5.1.1\n        files: ^(esphome|tests)/.+\\.py$\n  - repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v3.4.0\n    hooks:\n      - id: no-commit-to-branch\n        args:\n          - --branch=dev\n          - --branch=release\n          - --branch=beta\n  - repo: https://github.com/asottile/pyupgrade\n    rev: v3.15.2\n    hooks:\n      - id: pyupgrade\n        args: [--py39-plus]\n  - repo: https://github.com/adrienverge/yamllint.git\n    rev: v1.35.1\n    hooks:\n      - id: yamllint\n  - repo: https://github.com/pre-commit/mirrors-clang-format\n    rev: v13.0.1\n    hooks:\n      - id: clang-format\n        types_or: [c, c++]\n  - repo: local\n    hooks:\n      - id: pylint\n        name: pylint\n        entry: pylint\n        language: system\n        types: [python]\n"
  },
  {
    "path": ".yamllint",
    "content": "---\nextends: default\n\nignore-from-file: .gitignore\n\nrules:\n  document-start: disable\n  empty-lines:\n    level: error\n    max: 1\n    max-start: 0\n    max-end: 1\n  indentation:\n    level: error\n    spaces: 2\n    indent-sequences: true\n    check-multi-line-strings: false\n  line-length: disable\n  truthy: disable\n"
  },
  {
    "path": "README.md",
    "content": "> [!TIP]\n> **New simple Mode! No more endpoint definitions needed.**\n>\n> I started to implement the automated endpoint definition generation, see basic mode section for details.\n\n> [!Important]\n> **Please help to collect working cluster definitions [here](https://github.com/luar123/zigbee_esphome/discussions/22).**\n>\n> **If something is not working please check the [troubleshooting](#troubleshooting) section first. Config validation is not complete. Always consult [Zigbee Cluster Library](https://csa-iot.org/wp-content/uploads/2022/01/07-5123-08-Zigbee-Cluster-Library-1.pdf) for cluster definitions**\n\n# ESPHome ZigBee external component\n\nExternal ZigBee component for ESPHome.\n\n## Features\n\n- Automated generation of zigbee definition for lights, switches, sensors and binary sensors (see basic mode)\n- Definition of endpoints, clusters and attributes supported by esp-zigbee-sdk 1.6\n- Set attributes action\n- Manual report action\n- Reset zigbee action\n- Join trigger\n- Attribute received trigger\n- Time sync with coordinator\n- Custom clusters and attributes\n- (normal, binary, text) sensors, switches and lights can be connected to attributes without need for lambdas/actions\n- Wifi co-existence on ESP32-C6 and ESP32-C5\n- Deep-sleep should work\n- Not tested: groups\n- Time sync with coordinator\n- Router\n\n## Limitations\n\n- No coordinator devices\n- Attribute set action works only with numeric types and character string\n- Attribute OnValue trigger works only with numeric types\n- Reporting can be enabled, but not configured\n- No control devices like switches ([workaround](https://github.com/luar123/zigbee_esphome/discussions/18#discussioncomment-11875376))\n- Needs esp-idf >=5.1.4\n- Needs esphome >=2025.7\n- scenes not implemented\n- Officially the zigbee stack supports only 10 endpoints. however, this is not enforced and at least for sensor endpoints more than 10 seem to work. More then 10 light endpoints will crash!\n- zigbee2mqtt: Only one light is supported without creating a custom converter/definition\n- zigbee2mqtt: Analog input cluster (used for sensors) is supported by 2025 October release, but ignores type and unit\n- ZHA: Analog input cluster (used for sensors) without unit/type is ignored\n- ZHA: Minimum reporting interval is set to high values (30s) for some sensors and can't be changed. Keep that in mind if reporting seems not to work properly.\n\n## ToDo List (Short-Mid term)\n\n- Light effects (through identify cluster commands)\n- more components to support basic mode\n\n## Not planned (feel free to submit a pull request)\n\n- [Zigbee ZCL OTA Upgrade Cluster](https://docs.espressif.com/projects/esp-zigbee-sdk/en/latest/esp32/user-guide/zcl_ota_upgrade.html) and related [OTA API for ESP Zigbee SDK](https://docs.espressif.com/projects/esp-zigbee-sdk/en/latest/esp32/api-reference/esp_zigbee_ota.html) to allow OTA (over-the-air) firmware updates via Zigbee\n- Coordinator devices\n- Binding config in yaml\n- Reporting config in yaml\n- Control device support like switches ([workaround](https://github.com/luar123/zigbee_esphome/discussions/18#discussioncomment-11875376))\n\n## Usage\n\nInclude external component:\n\n```\nexternal_components:\n  - source: github://luar123/zigbee_esphome\n    components: [zigbee]\n\nzigbee:\n  components: all # to add all supported components\n  ...\n```\n\n### Configuration variables\n\n- **id** (Optional, string): Manually specify the ID for code generation.\n- **name** (Optional, string): Zigbee Model Identifier in basic cluster. Used by coordinator to match custom converters. Defaults to `esphome.name`\n- **manufacturer** (Optional, string): Zigbee Manufacturer Name in basic cluster. Used by coordinator to match custom converters. Defaults to `\"esphome\"`\n- **date** (Optional, string): Date Code in basic cluster. Defaults to build time\n- **power_supply** (Optional, int): Zigbee Power Source in basic cluster. Defaults to `0` = unknown\n  - `1` = single phase mains (USB)\n  - `2` = three phase mains\n  - `3` = battery\n- **version** (Optional, int): Zigbee App Version in basic cluster. Defaults to `0`\n- **area** (Optional, int): Zigbee Physical Environment in basic cluster. See ZCL. Defaults to `0` = unknown\n- **router** (Optional, bool): Create a router device instead of an end device. Defaults to `false`\n- **device_version** (Optional, int): Set the Home Automation Profile device version. Custom values might be needed for compatibility with some vendors. Defaults to `0`\n- **trust_center_key** (Optional, bind_key): Set custom trust center key. 32 digits hex number.\n- **debug** (Optional, bool): Print zigbee stack debug messages. Defaults to `false`\n- **components** (Optional, string|list): `all`: add definitions for all supported components that have a name and are not marked as internal.\n  - None: Add no definitions (default).\n  - List of component ids: Add only those. Can be combined with manual definitions in endpoints\n- **as_generic** (Optional, bool): Use generic/basic clusters where possible. Currently sensors and switches. Defaults to false\n- **endpoints** (Optional, list): endpoint list for advanced definitions. See examples\n\n[Todo]\n\n### Basic mode\n\nBy adding `components: all` the endpoint definition is generated automatically. Currently [sensor](https://esphome.io/components/sensor/), [binary_sensor](https://esphome.io/components/binary_sensor/), [light](https://esphome.io/components/light/) and [switch](https://esphome.io/components/switch/) ESPHome components are supported.\nBecause this is an [external component](https://esphome.io/components/external_components/) the whole implementation is a bit hacky and likely to fail with some setups. Also it is not possible to tweak the generated definitions.\nEach entity creates a new endpoint. For sensors the unit/type is set automatically. Please note that these definitions are not complete. Feel free to open an issue or pull request (see zigbee_ep.py)\n\n| ESPHome Entity  | Zigbee Cluster                                                     |\n| --------------- | ------------------------------------------------------------------ |\n| `light`         | `light`                                                            |\n| `switch`        | `on_off`, `binary_output`                                          |\n| `binary_sensor` | `binary_input`                                                     |\n| `sensor`        | `analog_input` or mapped to specific (e.g. `temperature`) clusters |\n\n**Basic example**:\n\n```\nzigbee:\n  id: \"zb\"\n  components: all\n```\n\n### Advanced mode\n\nEndpoint/Cluster definitions can be defined manually. Can be combined with automated definition.\n\nAdvanced example:\n\n```\nzigbee:\n  id: \"zb\"\n  endpoints:\n    - num: 1\n      device_type: COLOR_DIMMABLE_LIGHT\n      clusters:\n        - id: ON_OFF\n          attributes:\n            - attribute_id: 0\n              type: bool\n              on_value:\n                then:\n                  - light.control:\n                      id: light_1\n                      state: !lambda \"return (bool)x;\"\n        - id: LEVEL_CONTROL\n          attributes:\n            - attribute_id: 0\n              type: U8\n              on_value:\n                then:\n                  - light.control:\n                      id: light_1\n                      brightness: !lambda \"return ((float)x)/255;\"\n    - device_type: TEMPERATURE_SENSOR\n      num: 2\n      clusters:\n        - id: REL_HUMIDITY_MEASUREMENT\n          attributes:\n            - attribute_id: 0\n              id: hum_attr\n              type: U16\n              report: true\n              value: 200\n        - id: TEMP_MEASUREMENT\n          attributes:\n            - attribute_id: 0x0\n              type: S16\n              report: true\n              value: 100\n              device: temp_sensor_id\n              scale: 100\n    - device_type: HEATING_COOLING_UNIT\n      num: 3\n      clusters:\n       - id: THERMOSTAT\n         role: \"Client\"\n         attributes:\n           - attribute_id: 0x0008 # PIHeatingDemand\n             type: U8\n             value: 0\n             on_report:\n               then:\n                 # The below lambda will be called with an argument\n                 # `ZigBeeReportData <T> x` where ZigBeeReportData is defined as\n                 #\n                 # template<typename T> struct ZigBeeReportData {\n                 #   // Value of the attribute sent from server side.\n                 #   T value;\n                 #   // Address of device which sent this value.\n                 #   esp_zb_zcl_addr_t src_address;\n                 #   // Number of the endpoint on device which sent this value.\n                 #   uint8_t src_endpoint;\n                 # };\n                 #\n                 # And T is a C++ type matching the type of the attribute.\n                 - lambda: |-\n                     ESP_LOGD(\"main\", \"Received PIHeatingDemand | address type: 0x%02x; short addr: 0x%04x; ieee addr: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\",\n                              x.src_address.addr_type, x.src_address.u.short_addr,\n                              x.src_address.u.ieee_addr[7], x.src_address.u.ieee_addr[6],\n                              x.src_address.u.ieee_addr[5], x.src_address.u.ieee_addr[4],\n                              x.src_address.u.ieee_addr[3], x.src_address.u.ieee_addr[2],\n                              x.src_address.u.ieee_addr[1], x.src_address.u.ieee_addr[0]);\n                 - switch.control:\n                     id: relay_1_switch\n                     state: !lambda \"return (x.value>10);\"\n  on_join:\n    then:\n      - logger.log: \"Joined network\"\n```\n\n### Actions\n\n- `zigbee.setAttr`\n  - `id`: id of attribute\n  - `value`: attribute value. (can be a `lambda`)\n    - only numeric or string types\n- `zigbee.report`: `id` of zigbee component\n  - Manually send reports for all attributes with `report=true`\n- `zigbee.reportAttr`: `id` of zigbee_attribute component\n  - Manually send report for attribute\n- `zigbee.reset`: `id` of zigbee component\n  - Reset Zigbee Network and Device. Leave the current network and tries to join open networks.\n\nExamples:\n\n```\n    on_value:\n      then:\n        - zigbee.setAttr:\n            id: hum_attr\n            value: !lambda \"return x*100;\"\n```\n\n```\n    on_press:\n      then:\n        - zigbee.report: zb\n```\n\n### Time sync\n\nAdd a 'time' component with platform 'zigbee', e.g.:\n\n```\nzigbee:\n  id: \"zb\"\n  ...\n\ntime:\n  - platform: zigbee\n    timezone: Europe/London\n    on_time_sync:\n      then:\n        - logger.log: \"Synchronized system clock\"\n    on_time:\n      - seconds: /10\n        then:\n          - logger.log: \"Tick-tock 10 seconds\"\n```\n\n## Troubleshooting\n\n- Build errors\n  - Try to run `esphome clean <name.ymal>`\n  - Try to delete the `.esphome/build/<name>/` folder\n- ESP crashes\n  - Try to erase completely with `esptool.py erase_flash` and flash again.\n  - Make sure your configuration is valid. Config validation is not complete. Always consult [Zigbee Cluster Library](https://csa-iot.org/wp-content/uploads/2022/01/07-5123-08-Zigbee-Cluster-Library-1.pdf) for cluster definitions\n  - Common issues are that attributes do not support reporting (try set `report: false`), use a different type, or are not readable/writable (see ZCL).\n- Zigbee is not working as expected\n  - Whenever the cluster definition changed you need to re-interview and remove/add the device to your network.\n  - Sometimes it helps to power-cycle the coordinator and restarting z2m.\n  - Remove other endpoints. Sometimes coordinators struggle with multiple endpoints.\n\n## Notes\n\n- I don't have much free time to work on this right now, so feel free to fork/improve/create PRs/etc.\n- At the moment, the C++ implementation is rather simple and generic. I tried to keep as much logic as possible in the python part. However, endpoints/clusters ~~/attributes~~ could also be classes, this would simplify the yaml setup but requires more sophisticated C++ code.\n- There is also a project with more advanced C++ zigbee libraries for esp32 that could be used here as well: https://github.com/Muk911/esphome/tree/main/esp32c6/hello-zigbee\n- [parse_zigbee_headers.py](components/zigbee/files_to_parse/parse_zigbee_headers.py) is used to create the python enums and C helper functions automatically from zigbee sdk headers.\n- Deprecated [custom zigbee component](https://github.com/luar123/esphome_zb_sensor)\n\n## Example Zigbee device\n\nESPHome Zigbee using only dev board or additionally [AHT10 Temperature+Humidity Sensor](https://next.esphome.io/components/sensor/aht10).\n\n### Hardware Required\n\n- One development board with ESP32-H2, ESP32-C5 or ESP32-C6 SoC acting as Zigbee end-device (that you will load ESPHome with the example config to).\n  - For example, the official [ESP32-H2-DevKitM-1](https://docs.espressif.com/projects/espressif-esp-dev-kits/en/latest/esp32h2/esp32-h2-devkitm-1/user_guide.html) development kit board.\n- [AHT10 Temperature+Humidity Sensor](https://next.esphome.io/components/sensor/aht10) connected to I2C pins (SDA: 12, SCL: 22) for the aht10 example.\n- A USB cable for power supply and programming.\n- (Optional) A USB-C cable to get ESP32 logs from the UART USB port (UART0).\n\n### Build ESPHome Zigbee sensor\n\n- We will build [example_esp32h2.yaml](example_esp32h2.yaml) file.\n- Check [Getting Started with the ESPHome Command Line](https://esphome.io/guides/getting_started_command_line.html) tutorial to set up your dev environment.\n- Build with `esphome run example_esp32h2.yaml`.\n\n## How to contribute\n\n**Please submit all PRs here** and not to https://github.com/luar123/esphome/tree/zigbee\n\nUse pre-commit hook by enabling you esphome environment first and then running `pre-commit install` in the git root foulder.\n\nIf looking to contribute to this project, then suggest follow steps in these guides + look at issues in Espressif's ESP Zigbee SDK repository:\n\n- https://github.com/espressif/esp-zigbee-sdk/issues\n- https://github.com/firstcontributions/first-contributions/blob/master/README.md\n- https://github.com/firstcontributions/first-contributions/blob/master/github-desktop-tutorial.md\n\n## External documentation and reference\n\n> [!NOTE]\n> The official documentation and reference examples for the ESP Zigbee SDK can currently be obtained from Espressif:\n\n- [ESP32 Zigbee SDK Programming Guide](https://docs.espressif.com/projects/esp-zigbee-sdk/en/latest/esp32/)\n  - [ESP32-H2 Application User Guide](https://docs.espressif.com/projects/esp-zigbee-sdk/en/latest/esp32h2/application.html)\n  - [ESP32-C6 Application User Guide](https://docs.espressif.com/projects/esp-zigbee-sdk/en/latest/esp32c6/application.html)\n- [ESP-Zigbee-SDK Github repo](https://github.com/espressif/esp-zigbee-sdk)\n  - [ESP-Zigbee-SDK examples](https://github.com/espressif/esp-zigbee-sdk/tree/main/examples/)\n    - [Zigbee HA Example](https://github.com/espressif/esp-zigbee-sdk/tree/main/examples/esp_zigbee_HA_sample)\n      - [Zigbee HA Light Bulb example](https://github.com/espressif/esp-zigbee-sdk/tree/main/examples/esp_zigbee_HA_sample/HA_on_off_light)\n      - [Zigbee HA temperature sensor example](https://github.com/espressif/esp-zigbee-sdk/tree/main/examples/esp_zigbee_HA_sample/HA_temperature_sensor)\n      - [Zigbee HA thermostat example](https://github.com/espressif/esp-zigbee-sdk/tree/main/examples/esp_zigbee_HA_sample/HA_thermostat)\n"
  },
  {
    "path": "components/zigbee/__init__.py",
    "content": "import datetime\nimport inspect\nfrom pathlib import Path\nimport re\n\nfrom esphome import automation\nimport esphome.codegen as cg\nfrom esphome.components import text_sensor\nfrom esphome.components.esp32 import (\n    CONF_PARTITIONS,\n    add_extra_script,\n    add_idf_component,\n    add_idf_sdkconfig_option,\n    only_on_variant,\n)\nfrom esphome.components.esp32.const import (\n    VARIANT_ESP32C5,\n    VARIANT_ESP32C6,\n    VARIANT_ESP32H2,\n)\nimport esphome.config_validation as cv\nfrom esphome.const import (\n    CONF_AP,\n    CONF_AREA,\n    CONF_COMPONENTS,\n    CONF_DATE,\n    CONF_DEBUG,\n    CONF_DEVICE,\n    CONF_ID,\n    CONF_LAMBDA,\n    CONF_MAX_LENGTH,\n    CONF_NAME,\n    CONF_ON_VALUE,\n    CONF_POWER_SUPPLY,\n    CONF_TRIGGER_ID,\n    CONF_TYPE,\n    CONF_VALUE,\n    CONF_VERSION,\n    CONF_WIFI,\n)\nfrom esphome.core import CORE, EsphomeError\nimport esphome.final_validate as fv\n\nfrom .const import (\n    CONF_ACCESS,\n    CONF_AS_GENERIC,\n    CONF_ATTRIBUTE_ID,\n    CONF_ATTRIBUTES,\n    CONF_CLUSTERS,\n    CONF_DEVICE_TYPE,\n    CONF_DEVICE_VERSION,\n    CONF_ENDPOINTS,\n    CONF_MANUFACTURER,\n    CONF_NUM,\n    CONF_ON_JOIN,\n    CONF_ON_REPORT,\n    CONF_REPORT,\n    CONF_ROLE,\n    CONF_ROUTER,\n    CONF_SCALE,\n    CONF_TRUST_CENTER_KEY,\n    BinarySensor,\n    Sensor,\n    Switch,\n)\nfrom .types import (\n    ReportAction,\n    ReportAttrAction,\n    ResetZigbeeAction,\n    SetAttrAction,\n    ZigBeeAttribute,\n    ZigBeeComponent,\n    ZigBeeOnReportTrigger,\n    ZigBeeOnValueTrigger,\n)\nfrom .zigbee_const import ATTR_ACCESS, ATTR_TYPE, CLUSTER_ID, CLUSTER_ROLE, DEVICE_ID\nfrom .zigbee_ep import create_ep\n\ntry:\n    from esphome.components.esp32 import require_vfs_select\nexcept ImportError:\n\n    def require_vfs_select():\n        pass\n\n\n_supports_synchronous = (\n    \"synchronous\" in inspect.signature(automation.register_action).parameters\n)\n\n\ndef _register_action(name, action_type, schema, **kwargs):\n    if _supports_synchronous:\n        kwargs.setdefault(\"synchronous\", True)\n    else:\n        kwargs.pop(\"synchronous\", None)\n    return automation.register_action(name, action_type, schema, **kwargs)\n\n\nDEPENDENCIES = [\"esp32\"]\n\n_CALLBACK_AUTOMATIONS = (\n    automation.CallbackAutomation(CONF_ON_JOIN, \"add_on_join_callback\"),\n)\n\ncomp_ids = 0\n\n# dummies for upstream compatibility\nBINARY_SENSOR_SCHEMA = cv.Schema({})\nSENSOR_SCHEMA = cv.Schema({})\nSWITCH_SCHEMA = cv.Schema({})\nNUMBER_SCHEMA = cv.Schema({})\n\n\ndef validate_binary_sensor(x):\n    return x\n\n\ndef validate_sensor(x):\n    return x\n\n\ndef validate_switch(x):\n    return x\n\n\ndef validate_number(x):\n    return x\n\n\nasync def setup_binary_sensor(sensor, config):\n    pass\n\n\nasync def setup_sensor(sensor, config):\n    pass\n\n\nasync def setup_switch(switch, config):\n    pass\n\n\nasync def setup_number(number, config, min_value, max_value, step):\n    pass\n\n\ndef get_c_size(bits, options):\n    return str([n for n in options if n >= int(bits)][0])\n\n\ndef get_c_type(attr_type):\n    if attr_type == \"BOOL\":\n        return cg.bool_\n    if attr_type == \"SINGLE\":\n        return cg.float_\n    if attr_type == \"DOUBLE\":\n        return cg.double\n    if \"STRING\" in attr_type:\n        return cg.std_string\n    test = re.match(r\"(^U?)(\\d{1,2})(BITMAP$|BIT$|BIT_ENUM$|$)\", attr_type)\n    if test and test.group(2):\n        return getattr(cg, \"uint\" + get_c_size(test.group(2), [8, 16, 32, 64]))\n    test = re.match(r\"^S(\\d{1,2})$\", attr_type)\n    if test and test.group(1):\n        return getattr(cg, \"int\" + get_c_size(test.group(1), [16, 32, 64]))\n    raise EsphomeError(f\"Zigbee: type {attr_type} not supported or implemented\")\n\n\ndef get_cv_by_type(attr_type):\n    if attr_type == \"BOOL\":\n        return cv.boolean\n    if attr_type in [\"SEMI\", \"SINGLE\", \"DOUBLE\"]:\n        return cv.float_\n    if \"STRING\" in attr_type:\n        return cv.string\n    test = re.match(r\"(^U?)(\\d{1,2})(BITMAP$|BIT$|BIT_ENUM$|$)\", attr_type)\n    if test and test.group(2):\n        return cv.positive_int\n    test = re.match(r\"^S(\\d{1,2})$\", attr_type)\n    if test and test.group(1):\n        return cv.int_\n    raise cv.Invalid(f\"Zigbee: type {attr_type} not supported or implemented\")\n\n\ndef get_default_by_type(attr_type):\n    if \"CHAR_STRING\" == attr_type:\n        return \"\"\n    return 0\n\n\ndef validate_clusters(config):\n    for attr in config.get(CONF_ATTRIBUTES):\n        if isinstance(config.get(CONF_ID), int) and config.get(CONF_ID) >= 0xFC00:\n            if not {CONF_TYPE, CONF_ACCESS, CONF_VALUE} <= attr.keys():\n                raise cv.Invalid(\n                    f\"Parameters {CONF_TYPE}, {CONF_VALUE} and {CONF_ACCESS} are need for custom cluster.\"\n                )\n    return config\n\n\ndef validate_string_attributes(config):\n    if \"CHAR_STRING\" == config[CONF_TYPE]:\n        if CONF_MAX_LENGTH not in config.keys():\n            raise cv.Invalid(\n                f\"The '{CONF_MAX_LENGTH}' parameter is mandatory for string attributes.\"\n            )\n        if config[CONF_MAX_LENGTH] == 0:\n            config[CONF_MAX_LENGTH] = len(config.get(CONF_VALUE, \"\"))\n        # Check that size of default value matches CONF_MAX_LENGTH\n        if len(config[CONF_VALUE]) > config[CONF_MAX_LENGTH]:\n            raise cv.Invalid(\n                \"The default value is larger than the maximum length of the string attribute.\"\n            )\n    return config\n\n\ndef validate_attributes(config):\n    if CONF_VALUE in config:\n        config[CONF_VALUE] = get_cv_by_type(config[CONF_TYPE])(config[CONF_VALUE])\n    else:\n        config[CONF_VALUE] = get_default_by_type(config[CONF_TYPE])\n    config[CONF_ACCESS] = (\n        ATTR_ACCESS[config[CONF_ACCESS]] + config[CONF_REPORT] * 4\n        if CONF_ACCESS in config\n        else 0\n    )\n    validate_string_attributes(config)\n    if (CONF_ID not in config) and (\n        CONF_DEVICE in config or CONF_ON_VALUE in config or CONF_ON_REPORT in config\n    ):\n        config[CONF_ID] = cv.declare_id(ZigBeeAttribute)(None)\n    elif all(\n        i not in config for i in [CONF_ID, CONF_DEVICE, CONF_ON_VALUE, CONF_ON_REPORT]\n    ) and (config[CONF_SCALE] != 1.0 or CONF_LAMBDA in config or config[CONF_REPORT]):\n        raise cv.Invalid(\n            f\"Parameters {CONF_SCALE}', '{CONF_LAMBDA}' or '{CONF_REPORT}' are not allowed without '{CONF_ID}', '{CONF_DEVICE}', '{CONF_ON_VALUE}' or '{CONF_ON_REPORT}'.\"\n        )\n\n    return config\n\n\ndef final_validate(config):\n    esp_conf = fv.full_config.get()[\"esp32\"]\n    if CONF_PARTITIONS in esp_conf:\n        with open(\n            CORE.relative_config_path(esp_conf[CONF_PARTITIONS]), encoding=\"utf8\"\n        ) as f:\n            partitions = f.read()\n            if (\"zb_storage\" not in partitions) and (\"zb_fct\" not in partitions):\n                raise cv.Invalid(\n                    \"Add \\n'zb_storage, data, fat,   , 16K,'\\n'zb_fct, data, fat, , 1K,'\\n to your custom partition table.\"\n                )\n    else:\n        raise cv.Invalid(\n            f\"Use '{CONF_PARTITIONS}' in esp32 to specify a custom partition table including zigbee partitions\"\n        )\n    if CONF_WIFI in fv.full_config.get():\n        if CONF_AP in fv.full_config.get()[CONF_WIFI]:\n            raise cv.Invalid(\"Zigbee can't be used together with an Wifi Access Point.\")\n    global comp_ids  # noqa: PLW0603\n    comp_ids = len(CORE.component_ids)\n    return config\n\n\nFINAL_VALIDATE_SCHEMA = cv.Schema(final_validate)\n\n\ndef _require_vfs_select(config):\n    \"\"\"Register VFS select requirement during config validation.\"\"\"\n    # ZigBee uses esp_vfs_eventfd which requires VFS select support\n    require_vfs_select()\n    return config\n\n\nCONFIG_SCHEMA = cv.All(\n    cv.Schema(\n        {\n            cv.GenerateID(): cv.declare_id(ZigBeeComponent),\n            cv.Optional(CONF_NAME): cv.string,\n            cv.Optional(CONF_MANUFACTURER, default=\"esphome\"): cv.string,\n            cv.Optional(\n                CONF_DATE, default=datetime.datetime.now().strftime(\"%Y%m%d\")\n            ): cv.string,\n            cv.Optional(CONF_POWER_SUPPLY, default=0): cv.int_,  # make enum\n            cv.Optional(CONF_VERSION, default=0): cv.int_,\n            cv.Optional(CONF_AREA, default=0): cv.int_,  # make enum\n            cv.Optional(CONF_ROUTER, default=False): cv.boolean,\n            cv.Optional(CONF_TRUST_CENTER_KEY): cv.bind_key,\n            cv.Optional(CONF_DEVICE_VERSION): cv.int_,\n            cv.Optional(CONF_DEBUG, default=False): cv.boolean,\n            cv.Optional(CONF_COMPONENTS): cv.Any(\n                cv.one_of(\"all\", \"none\", lower=True),\n                cv.ensure_list(cv.use_id(cg.EntityBase)),\n            ),\n            cv.Optional(CONF_AS_GENERIC, default=False): cv.boolean,\n            cv.Optional(CONF_ENDPOINTS): cv.ensure_list(\n                cv.Schema(\n                    {\n                        cv.Required(CONF_DEVICE_TYPE): cv.enum(DEVICE_ID, upper=True),\n                        cv.Optional(CONF_NUM): cv.int_range(1, 240),\n                        cv.Optional(CONF_CLUSTERS): cv.ensure_list(\n                            cv.Schema(\n                                {\n                                    cv.Required(CONF_ID): cv.Any(\n                                        cv.enum(CLUSTER_ID, upper=True),\n                                        cv.int_range(0xFC00, 0xFFFF),\n                                    ),\n                                    cv.Optional(CONF_ROLE, default=\"Server\"): cv.enum(\n                                        CLUSTER_ROLE, upper=True\n                                    ),\n                                    cv.Optional(CONF_ATTRIBUTES): cv.ensure_list(\n                                        cv.Schema(\n                                            {\n                                                cv.Optional(CONF_ID): cv.declare_id(\n                                                    ZigBeeAttribute\n                                                ),\n                                                cv.Required(CONF_ATTRIBUTE_ID): cv.int_,\n                                                cv.Required(CONF_TYPE): cv.enum(\n                                                    ATTR_TYPE, upper=True\n                                                ),\n                                                cv.Optional(CONF_ACCESS): cv.enum(\n                                                    ATTR_ACCESS, upper=True\n                                                ),\n                                                cv.Optional(CONF_VALUE): cv.valid,\n                                                cv.Optional(\n                                                    CONF_REPORT, default=False\n                                                ): cv.Any(\n                                                    cv.boolean,\n                                                    cv.one_of(\"force\", lower=True),\n                                                ),\n                                                cv.Optional(\n                                                    CONF_ON_VALUE\n                                                ): automation.validate_automation(\n                                                    {\n                                                        cv.GenerateID(\n                                                            CONF_TRIGGER_ID\n                                                        ): cv.declare_id(\n                                                            ZigBeeOnValueTrigger\n                                                        ),\n                                                    }\n                                                ),\n                                                cv.Optional(CONF_DEVICE): cv.use_id(\n                                                    cg.EntityBase\n                                                ),\n                                                cv.Optional(\n                                                    CONF_SCALE, default=1.0\n                                                ): cv.float_,\n                                                cv.Optional(\n                                                    CONF_LAMBDA\n                                                ): cv.returning_lambda,\n                                                cv.Optional(\n                                                    CONF_MAX_LENGTH\n                                                ): cv.int_range(0, 254),\n                                                cv.Optional(\n                                                    CONF_ON_REPORT\n                                                ): automation.validate_automation(\n                                                    {\n                                                        cv.GenerateID(\n                                                            CONF_TRIGGER_ID\n                                                        ): cv.declare_id(\n                                                            ZigBeeOnReportTrigger\n                                                        ),\n                                                    }\n                                                ),\n                                            }\n                                        ),\n                                        validate_attributes,\n                                    ),\n                                },\n                            ),\n                            validate_clusters,\n                        ),\n                    }\n                ),\n            ),\n            cv.Optional(CONF_ON_JOIN): automation.validate_automation({}),\n        }\n    ).extend(cv.COMPONENT_SCHEMA),\n    cv.require_framework_version(esp_idf=cv.Version(5, 1, 2)),\n    _require_vfs_select,\n    only_on_variant(\n        supported=[\n            VARIANT_ESP32H2,\n            VARIANT_ESP32C6,\n            VARIANT_ESP32C5,\n        ]\n    ),\n)\n\n\ndef find_attr(conf, id):\n    for ep in conf[CONF_ENDPOINTS]:\n        for cl in ep.get(CONF_CLUSTERS, []):\n            for attr in cl.get(CONF_ATTRIBUTES, []):\n                if attr[CONF_ID] == id:\n                    return attr\n    raise EsphomeError(f\"Zigbee: Cannot find attribute {id}.\")\n\n\nasync def attributes_to_code(var, ep_num, cl):\n    for attr in cl.get(CONF_ATTRIBUTES, []):\n        if attr.get(CONF_ID) is None:\n            cg.add(\n                var.add_attr(\n                    ep_num,\n                    CLUSTER_ID.get(cl[CONF_ID], cl[CONF_ID]),\n                    CLUSTER_ROLE[cl[CONF_ROLE]],\n                    attr[CONF_ATTRIBUTE_ID],\n                    ATTR_TYPE[attr[CONF_TYPE]],\n                    attr[CONF_ACCESS],\n                    attr.get(CONF_MAX_LENGTH, 0),\n                    attr[CONF_VALUE],\n                )\n            )\n            continue\n        attr_var = cg.new_Pvariable(\n            attr[CONF_ID],\n            var,\n            ep_num,\n            CLUSTER_ID.get(cl[CONF_ID], cl[CONF_ID]),\n            CLUSTER_ROLE[cl[CONF_ROLE]],\n            attr[CONF_ATTRIBUTE_ID],\n            ATTR_TYPE[attr[CONF_TYPE]],\n            attr[CONF_SCALE],\n        )\n        await cg.register_component(attr_var, attr)\n\n        cg.add(\n            attr_var.add_attr(\n                attr[CONF_ACCESS],\n                attr.get(CONF_MAX_LENGTH, 0),\n                attr[CONF_VALUE],\n            )\n        )\n        if attr[CONF_REPORT]:\n            cg.add(attr_var.set_report(attr[CONF_REPORT] == \"force\"))\n\n        if CONF_LAMBDA in attr:\n            lambda_ = await cg.process_lambda(\n                attr[CONF_LAMBDA],\n                [(cg.float_, \"x\")],\n                return_type=get_c_type(attr[CONF_TYPE]),\n            )\n\n        if CONF_DEVICE in attr:\n            device = await cg.get_variable(attr[CONF_DEVICE])\n            template_arg = cg.TemplateArguments(get_c_type(attr[CONF_TYPE]))\n            if CONF_LAMBDA in attr:\n                if device.base.type.inherits_from(Sensor):\n                    lambda_ = await cg.process_lambda(\n                        attr[CONF_LAMBDA],\n                        [(cg.float_, \"x\")],\n                        return_type=get_c_type(attr[CONF_TYPE]),\n                    )\n                elif device.base.type.inherits_from(BinarySensor):\n                    lambda_ = await cg.process_lambda(\n                        attr[CONF_LAMBDA],\n                        [(cg.bool_, \"x\")],\n                        return_type=get_c_type(attr[CONF_TYPE]),\n                    )\n                elif device.base.type.inherits_from(text_sensor.TextSensor):\n                    lambda_ = await cg.process_lambda(\n                        attr[CONF_LAMBDA],\n                        [(cg.std_string, \"x\")],\n                        return_type=get_c_type(attr[CONF_TYPE]),\n                    )\n                elif device.base.type.inherits_from(Switch):\n                    lambda_ = await cg.process_lambda(\n                        attr[CONF_LAMBDA],\n                        [(get_c_type(attr[CONF_TYPE]), \"x\")],\n                        return_type=cg.bool_,\n                    )\n                cg.add(attr_var.connect(template_arg, device, lambda_))\n            else:\n                cg.add(attr_var.connect(template_arg, device))\n\n        for conf in attr.get(CONF_ON_VALUE, []):\n            trigger = cg.new_Pvariable(\n                conf[CONF_TRIGGER_ID],\n                cg.TemplateArguments(get_c_type(attr[CONF_TYPE])),\n                attr_var,\n            )\n            await cg.register_component(trigger, conf)\n            await automation.build_automation(\n                trigger, [(get_c_type(attr[CONF_TYPE]), \"x\")], conf\n            )\n\n        for conf in attr.get(CONF_ON_REPORT, []):\n            trigger = cg.new_Pvariable(\n                conf[CONF_TRIGGER_ID],\n                cg.TemplateArguments(get_c_type(attr[CONF_TYPE])),\n                attr_var,\n            )\n            await cg.register_component(trigger, conf)\n            value_type = get_c_type(attr[CONF_TYPE])\n            automation_arg_type = \"esphome::zigbee::ZigBeeReportData\" + str(\n                cg.TemplateArguments(value_type)\n            )\n            await automation.build_automation(\n                trigger, [(cg.RawExpression(automation_arg_type), \"x\")], conf\n            )\n\n\nasync def to_code(config):\n    add_idf_component(\n        name=\"espressif/esp-zboss-lib\",\n        ref=\"1.6.4\",\n    )\n    add_idf_component(\n        name=\"espressif/esp-zigbee-lib\",\n        ref=\"1.6.8\",\n    )\n\n    add_idf_sdkconfig_option(\"CONFIG_ZB_ENABLED\", True)\n    if config.get(CONF_ROUTER):\n        add_idf_sdkconfig_option(\"CONFIG_ZB_ZCZR\", True)\n    else:\n        add_idf_sdkconfig_option(\"CONFIG_ZB_ZED\", True)\n    add_idf_sdkconfig_option(\"CONFIG_ZB_RADIO_NATIVE\", True)\n    # The pre-built Zigbee library uses esp_log_default_level which requires\n    # dynamic log level control to be enabled\n    add_idf_sdkconfig_option(\"CONFIG_LOG_DYNAMIC_LEVEL_CONTROL\", True)\n\n    if CONF_WIFI in CORE.config:\n        add_idf_sdkconfig_option(\"CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE\", 4096)\n        cg.add_define(\"CONFIG_WIFI_COEX\")\n    if config.get(CONF_DEBUG):\n        add_idf_sdkconfig_option(\"CONFIG_ZB_DEBUG_MODE\", True)\n\n    # create endpoints\n    ep_list, added_ids = create_ep(config, CORE.config)\n    if added_ids:\n        # update ESPHOME_COMPONENT_COUNT via pre build script\n        cg.add_define(\"ZB_ESPHOME_COMPONENT_COUNT\", comp_ids + added_ids)\n        add_extra_script(\n            \"pre\",\n            \"zigbee_pre_build.py\",\n            Path(__file__).parent / \"zigbee_pre_build.py.script\",\n        )\n\n    # setup zigbee components\n    var = cg.new_Pvariable(config[CONF_ID])\n    await cg.register_component(var, config)\n\n    if CONF_TRUST_CENTER_KEY in config:\n        cg.add(var.set_trust_center_key(config[CONF_TRUST_CENTER_KEY]))\n    if CONF_DEVICE_VERSION in config:\n        cg.add(var.set_device_version(config[CONF_DEVICE_VERSION]))\n\n    if CONF_NAME not in config:\n        config[CONF_NAME] = CORE.name or \"\"\n    cg.add(\n        var.set_basic_cluster(\n            config[CONF_NAME],\n            config[CONF_MANUFACTURER],\n            config[CONF_DATE],\n            config[CONF_POWER_SUPPLY],\n            config[CONF_VERSION],\n            0,\n            0,\n            CORE.area or \"\",\n            config[CONF_AREA],\n        )\n    )\n    for ep in ep_list:\n        cg.add(\n            var.create_default_cluster(ep[CONF_NUM], DEVICE_ID[ep[CONF_DEVICE_TYPE]])\n        )\n        for cl in ep.get(CONF_CLUSTERS, []):\n            cg.add(\n                var.add_cluster(\n                    ep[CONF_NUM],\n                    CLUSTER_ID.get(cl[CONF_ID], cl[CONF_ID]),\n                    CLUSTER_ROLE[cl[CONF_ROLE]],\n                )\n            )\n            await attributes_to_code(var, ep[CONF_NUM], cl)\n    await automation.build_callback_automations(var, config, _CALLBACK_AUTOMATIONS)\n\n\nZIGBEE_ACTION_SCHEMA = cv.Schema(\n    {\n        cv.GenerateID(): cv.use_id(ZigBeeComponent),\n    }\n)\n\n\n@_register_action(\n    \"zigbee.reset\",\n    ResetZigbeeAction,\n    automation.maybe_simple_id(ZIGBEE_ACTION_SCHEMA),\n    synchronous=True,\n)\nasync def reset_zigbee_to_code(config, action_id, template_arg, args):\n    var = cg.new_Pvariable(action_id, template_arg)\n    await cg.register_parented(var, config[CONF_ID])\n    return var\n\n\n@_register_action(\n    \"zigbee.report\",\n    ReportAction,\n    automation.maybe_simple_id(ZIGBEE_ACTION_SCHEMA),\n    synchronous=True,\n)\nasync def report_to_code(config, action_id, template_arg, args):\n    var = cg.new_Pvariable(action_id, template_arg)\n    await cg.register_parented(var, config[CONF_ID])\n    return var\n\n\nZIGBEE_ATTRIBUTE_ACTION_SCHEMA = cv.Schema(\n    {\n        cv.GenerateID(): cv.use_id(ZigBeeAttribute),\n    }\n)\n\nZIGBEE_SET_ATTR_SCHEMA = cv.All(\n    automation.maybe_simple_id(\n        ZIGBEE_ATTRIBUTE_ACTION_SCHEMA.extend(\n            cv.Schema(\n                {\n                    cv.Required(CONF_VALUE): cv.templatable(cv.valid),\n                }\n            )\n        )\n    ),\n)\n\n\n@_register_action(\n    \"zigbee.setAttr\", SetAttrAction, ZIGBEE_SET_ATTR_SCHEMA, synchronous=True\n)\nasync def zigbee_set_attr_to_code(config, action_id, template_arg, args):\n    attr = find_attr(\n        CORE.config[\"zigbee\"],\n        config[CONF_ID],\n    )\n    template_arg = cg.TemplateArguments(\n        get_c_type(attr[CONF_TYPE]),\n        template_arg.args if template_arg.args.args else None,\n    )\n    parent = await cg.get_variable(config[CONF_ID])\n    var = cg.new_Pvariable(action_id, template_arg, parent)\n    template_ = await cg.templatable(\n        config[CONF_VALUE], args, get_c_type(attr[CONF_TYPE])\n    )\n    cg.add(var.set_value(template_))\n\n    return var\n\n\n@_register_action(\n    \"zigbee.reportAttr\",\n    ReportAttrAction,\n    automation.maybe_simple_id(ZIGBEE_ATTRIBUTE_ACTION_SCHEMA),\n    synchronous=True,\n)\nasync def zigbee_report_attr_to_code(config, action_id, template_arg, args):\n    var = cg.new_Pvariable(action_id, template_arg)\n    await cg.register_parented(var, config[CONF_ID])\n    return var\n"
  },
  {
    "path": "components/zigbee/automation.cpp",
    "content": "#include \"automation.h\"\n#include <algorithm>\n#include \"esphome/core/log.h\"\n\nnamespace esphome {\nnamespace zigbee {\n\n/**\n * @brief sRGB to R'G'B' gamma correction as outlined in\n *        https://en.wikipedia.org/wiki/SRGB\n *\n * @param linear a component value in sRGB color space\n * @return float the corresponding component value in R'G'B' color space\n */\ninline float gamma_correct(float linear) {\n  if(linear < 0.0031308f) {\n    return linear * 12.92f;\n  } else {\n    return (1.055f) * pow(linear, (1.0f / 2.4f)) - 0.055f;\n  }\n}\n\n/* The following conversions operate on xyY with Y = 1.0, hence Y\n   doesn't appear in the formulas. The coefficients originate from\n   https://en.wikipedia.org/wiki/SRGB#Primaries\n */\nfloat get_r_from_xy(float x, float y) {\n  float z = 1.0f - x - y;\n  float X = x / y;\n  float Z = z / y;\n  float r = X * 3.2406 - 1.5372f - Z * 0.4986f;\n  return std::clamp(gamma_correct(r), 0.0f, 1.0f);\n}\n\nfloat get_g_from_xy(float x, float y) {\n  float z = 1.0f - x - y;\n  float X = x / y;\n  float Z = z / y;\n  float g = -X * 0.9689f + 1.8758f + Z * 0.0415f;\n  return std::clamp(gamma_correct(g), 0.0f, 1.0f);\n}\n\nfloat get_b_from_xy(float x, float y) {\n  float z = 1.0f - x - y;\n  float X = x / y;\n  float Z = z / y;\n\n  float b = X * 0.0557f - 0.2040f + Z * 1.0570f;\n  return std::clamp(gamma_correct(b), 0.0f, 1.0f);\n}\n\n#ifdef USE_LIGHT\nvoid set_light_color(uint8_t ep, light::LightCall *call, uint16_t value, bool is_x) {\n  static std::map<uint8_t, float> x;\n  static std::map<uint8_t, float> y;\n  if (is_x) {\n    x[ep] = (float) value / 65536;\n  } else {\n    y[ep] = (float) value / 65536;\n  }\n  ESP_LOGD(TAG, \"Set color, x: %f, y: %f\", x[ep], y[ep]);\n  call->set_rgb(get_r_from_xy(x[ep], y[ep]), get_g_from_xy(x[ep], y[ep]), get_b_from_xy(x[ep], y[ep]));\n}\n#endif\n\n}  // namespace zigbee\n}  // namespace esphome\n"
  },
  {
    "path": "components/zigbee/automation.h",
    "content": "#pragma once\n\n//#include <stdfloat> //deactive because not working with esp-idf 5.1.4\n\n#include \"esphome/core/automation.h\"\n#include \"esphome/core/component.h\"\n#include \"esphome/core/defines.h\"\n#include \"esphome/core/version.h\"\n#include \"esphome/core/log.h\"\n#include \"zigbee_attribute.h\"\n#include \"zigbee.h\"\n#ifdef USE_LIGHT\n#include \"esphome/components/light/light_state.h\"\n#endif\n\nnamespace esphome {\nnamespace zigbee {\n\ntemplate<typename... Ts> class ResetZigbeeAction : public Action<Ts...>, public Parented<ZigBeeComponent> {\n public:\n#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0)\n  void play(const Ts &...x) override { this->parent_->reset(); }\n#else\n  void play(Ts... x) override { this->parent_->reset(); }\n#endif\n};\n\ntemplate<typename... Ts> class ReportAction : public Action<Ts...>, public Parented<ZigBeeComponent> {\n public:\n#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0)\n  void play(const Ts &...x) override { this->parent_->report(); }\n#else\n  void play(Ts... x) override { this->parent_->report(); }\n#endif\n};\n\ntemplate<typename... Ts> class ReportAttrAction : public Action<Ts...>, public Parented<ZigBeeAttribute> {\n public:\n#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0)\n  void play(const Ts &...x) override { this->parent_->report(); }\n#else\n  void play(Ts... x) override { this->parent_->report(); }\n#endif\n};\n\ntemplate<typename T, typename... Ts> class SetAttrAction : public Action<Ts...> {\n public:\n  SetAttrAction(ZigBeeAttribute *parent) : parent_(parent) {}\n  TEMPLATABLE_VALUE(T, value);\n\n#if ESPHOME_VERSION_CODE >= VERSION_CODE(2025, 11, 0)\n  void play(const Ts &...x) override { this->parent_->set_attr(this->value_.value(x...)); }\n#else\n  void play(Ts... x) override { this->parent_->set_attr(this->value_.value(x...)); }\n#endif\n\n protected:\n  ZigBeeAttribute *parent_;\n};\n\ntemplate<typename Ts> class ZigBeeOnValueTrigger : public Trigger<Ts>, public Component {\n public:\n  explicit ZigBeeOnValueTrigger(ZigBeeAttribute *parent) : parent_(parent) {}\n  void setup() override {\n    this->parent_->add_on_value_callback([this](esp_zb_zcl_attribute_t attribute) { this->on_value_(attribute); });\n  }\n\n protected:\n  void on_value_(esp_zb_zcl_attribute_t attribute) {\n    if (attribute.data.type == parent_->attr_type() && attribute.data.value) {\n      this->trigger(get_value_by_type<Ts>(parent_->attr_type(), attribute.data.value));\n    }\n  }\n  ZigBeeAttribute *parent_;\n};\n\ntemplate<typename T> struct ZigBeeReportData {\n  T value;\n  esp_zb_zcl_addr_t src_address;\n  uint8_t src_endpoint;\n};\n\ntemplate<typename T> class ZigBeeOnReportTrigger : public Trigger<ZigBeeReportData<T>>, public Component {\n public:\n  explicit ZigBeeOnReportTrigger(ZigBeeAttribute *parent) : parent_(parent) {}\n  void setup() override {\n    this->parent_->add_on_report_callback(\n        [this](esp_zb_zcl_attribute_t attribute, esp_zb_zcl_addr_t src_address, uint8_t src_endpoint) {\n          this->on_report_(attribute, src_address, src_endpoint);\n        });\n  }\n\n protected:\n  void on_report_(esp_zb_zcl_attribute_t attribute, esp_zb_zcl_addr_t src_address, uint8_t src_endpoint) {\n    if (attribute.data.type == parent_->attr_type() && attribute.data.value) {\n      this->trigger(ZigBeeReportData<T>{\n          .value = get_value_by_type<T>(parent_->attr_type(), attribute.data.value),\n          .src_address = src_address,\n          .src_endpoint = src_endpoint,\n      });\n    }\n  }\n  ZigBeeAttribute *parent_;\n};\n\ntemplate<class T> T get_value_by_type(uint8_t attr_type, void *data) {\n  switch (attr_type) {\n    case ESP_ZB_ZCL_ATTR_TYPE_SEMI:\n      return 0;  //(T) * (std::float16_t *) data;\n    case ESP_ZB_ZCL_ATTR_TYPE_OCTET_STRING:\n      return 0;\n    case ESP_ZB_ZCL_ATTR_TYPE_CHAR_STRING:\n      return 0;\n    case ESP_ZB_ZCL_ATTR_TYPE_LONG_OCTET_STRING:\n      return 0;\n    case ESP_ZB_ZCL_ATTR_TYPE_LONG_CHAR_STRING:\n      return 0;\n    case ESP_ZB_ZCL_ATTR_TYPE_ARRAY:\n      return 0;\n    case ESP_ZB_ZCL_ATTR_TYPE_16BIT_ARRAY:\n      return 0;\n    case ESP_ZB_ZCL_ATTR_TYPE_32BIT_ARRAY:\n      return 0;\n    case ESP_ZB_ZCL_ATTR_TYPE_STRUCTURE:\n      return 0;\n    case ESP_ZB_ZCL_ATTR_TYPE_SET:\n      return 0;\n    case ESP_ZB_ZCL_ATTR_TYPE_BAG:\n      return 0;\n    case ESP_ZB_ZCL_ATTR_TYPE_TIME_OF_DAY:\n      return (T) * (uint32_t *) data;\n    case ESP_ZB_ZCL_ATTR_TYPE_DATE:\n      return (T) * (uint32_t *) data;\n    case ESP_ZB_ZCL_ATTR_TYPE_UTC_TIME:\n      return (T) * (uint32_t *) data;\n    case ESP_ZB_ZCL_ATTR_TYPE_CLUSTER_ID:\n      return (T) * (uint16_t *) data;\n    case ESP_ZB_ZCL_ATTR_TYPE_ATTRIBUTE_ID:\n      return (T) * (uint16_t *) data;\n    case ESP_ZB_ZCL_ATTR_TYPE_BACNET_OID:\n      return 0;\n    case ESP_ZB_ZCL_ATTR_TYPE_IEEE_ADDR:\n      return (T) * (uint64_t *) data;\n    case ESP_ZB_ZCL_ATTR_TYPE_128_BIT_KEY:\n      return 0;\n    default:\n      return *(T *) data;\n  }\n}\n\nfloat get_r_from_xy(float x, float y);\nfloat get_g_from_xy(float x, float y);\nfloat get_b_from_xy(float x, float y);\n\n#ifdef USE_LIGHT\nvoid set_light_color(uint8_t ep, light::LightCall *call, uint16_t value, bool is_x);\n#endif\n\n}  // namespace zigbee\n}  // namespace esphome\n"
  },
  {
    "path": "components/zigbee/const.py",
    "content": "from dataclasses import dataclass\n\nimport esphome.codegen as cg\n\nCONF_ENDPOINTS = \"endpoints\"\nCONF_DEVICE_TYPE = \"device_type\"\nCONF_NUM = \"num\"\nCONF_CLUSTERS = \"clusters\"\nCONF_ON_JOIN = \"on_join\"\nCONF_MANUFACTURER = \"manufacturer\"\nCONF_ATTRIBUTES = \"attributes\"\nCONF_ROLE = \"role\"\nCONF_ENDPOINT = \"endpoint\"\nCONF_CLUSTER = \"cluster\"\nCONF_REPORT = \"report\"\nCONF_ACCESS = \"access\"\nCONF_SCALE = \"scale\"\nCONF_ATTRIBUTE_ID = \"attribute_id\"\nCONF_ZIGBEE_ID = \"zigbee_id\"\nCONF_ROUTER = \"router\"\nCONF_AS_GENERIC = \"as_generic\"\nCONF_ON_REPORT = \"on_report\"\nCONF_TRUST_CENTER_KEY = \"trust_center_key\"\nCONF_DEVICE_VERSION = \"device_version\"\n\n# dummies for upstream compatibility\nbinary_sensor_ns = cg.esphome_ns.namespace(\"binary_sensor\")\nBinarySensor = binary_sensor_ns.class_(\"BinarySensor\", cg.EntityBase)\nsensor_ns = cg.esphome_ns.namespace(\"sensor\")\nSensor = sensor_ns.class_(\"Sensor\", cg.EntityBase)\nswitch_ns = cg.esphome_ns.namespace(\"switch_\")  # NB: \"switch_\" not \"switch\"\nSwitch = switch_ns.class_(\"Switch\", cg.EntityBase)\n\n\n@dataclass\nclass AnalogInputType:\n    Temp_Degrees_C = 0x00\n    Relative_Humidity_Percent = 0x01\n    Pressure_Pascal = 0x02\n    Flow_Liters_Per_Sec = 0x03\n    Percentage = 0x04\n    Parts_Per_Million = 0x05\n    Rotational_Speed_RPM = 0x06\n    Current_Amps = 0x07\n    Frequency_Hz = 0x08\n    Power_Watts = 0x09\n    Power_Kilo_Watts = 0x0A\n    Energy_Kilo_Watt_Hours = 0x0B\n    Count = 0x0C\n    Enthalpy_KJoules_Per_Kg = 0x0D\n    Time_Seconds = 0x0E\n\n\n@dataclass\nclass BacnetUnit:\n    \"\"\"BACnet units.\"\"\"\n\n    SQUARE_METERS = 0\n    SQUARE_FEET = 1\n    MILLIAMPERES = 2\n    AMPERES = 3\n    OHMS = 4\n    VOLTS = 5\n    KILOVOLTS = 6\n    MEGAVOLTS = 7\n    VOLT_AMPERES = 8\n    KILOVOLT_AMPERES = 9\n    MEGAVOLT_AMPERES = 10\n    VOLT_AMPERES_REACTIVE = 11\n    KILOVOLT_AMPERES_REACTIVE = 12\n    MEGAVOLT_AMPERES_REACTIVE = 13\n    DEGREES_PHASE = 14\n    POWER_FACTOR = 15\n    JOULES = 16\n    KILOJOULES = 17\n    WATT_HOURS = 18\n    KILOWATT_HOURS = 19\n    BTUS = 20\n    THERMS = 21\n    TON_HOURS = 22\n    JOULES_PER_KILOGRAM_DRY_AIR = 23\n    BTUS_PER_POUND_DRY_AIR = 24\n    CYCLES_PER_HOUR = 25\n    CYCLES_PER_MINUTE = 26\n    HERTZ = 27\n    GRAMS_OF_WATER_PER_KILOGRAM_DRY_AIR = 28\n    PERCENT_RELATIVE_HUMIDITY = 29\n    MILLIMETERS = 30\n    METERS = 31\n    INCHES = 32\n    FEET = 33\n    WATTS_PER_SQUARE_FOOT = 34\n    WATTS_PER_SQUARE_METER = 35\n    LUMENS = 36\n    LUXES = 37\n    FOOT_CANDLES = 38\n    KILOGRAMS = 39\n    POUNDS_MASS = 40\n    TONS = 41\n    KILOGRAMS_PER_SECOND = 42\n    KILOGRAMS_PER_MINUTE = 43\n    KILOGRAMS_PER_HOUR = 44\n    POUNDS_MASS_PER_MINUTE = 45\n    POUNDS_MASS_PER_HOUR = 46\n    WATTS = 47\n    KILOWATTS = 48\n    MEGAWATTS = 49\n    BTUS_PER_HOUR = 50\n    HORSEPOWER = 51\n    TONS_REFRIGERATION = 52\n    PASCALS = 53\n    KILOPASCALS = 54\n    BARS = 55\n    POUNDS_FORCE_PER_SQUARE_INCH = 56\n    CENTIMETERS_OF_WATER = 57\n    INCHES_OF_WATER = 58\n    MILLIMETERS_OF_MERCURY = 59\n    CENTIMETERS_OF_MERCURY = 60\n    INCHES_OF_MERCURY = 61\n    DEGREES_CELSIUS = 62\n    KELVIN = 63\n    DEGREES_FAHRENHEIT = 64\n    DEGREE_DAYS_CELSIUS = 65\n    DEGREE_DAYS_FAHRENHEIT = 66\n    YEARS = 67\n    MONTHS = 68\n    WEEKS = 69\n    DAYS = 70\n    HOURS = 71\n    MINUTES = 72\n    SECONDS = 73\n    METERS_PER_SECOND = 74\n    KILOMETERS_PER_HOUR = 75\n    FEET_PER_SECOND = 76\n    FEET_PER_MINUTE = 77\n    MILES_PER_HOUR = 78\n    CUBIC_FEET = 79\n    CUBIC_METERS = 80\n    IMPERIAL_GALLONS = 81\n    LITERS = 82\n    US_GALLONS = 83\n    CUBIC_FEET_PER_MINUTE = 84\n    CUBIC_METERS_PER_SECOND = 85\n    IMPERIAL_GALLONS_PER_MINUTE = 86\n    LITERS_PER_SECOND = 87\n    LITERS_PER_MINUTE = 88\n    US_GALLONS_PER_MINUTE = 89\n    DEGREES_ANGULAR = 90\n    DEGREES_CELSIUS_PER_HOUR = 91\n    DEGREES_CELSIUS_PER_MINUTE = 92\n    DEGREES_FAHRENHEIT_PER_HOUR = 93\n    DEGREES_FAHRENHEIT_PER_MINUTE = 94\n    NO_UNITS = 95\n    PARTS_PER_MILLION = 96\n    PARTS_PER_BILLION = 97\n    PERCENT = 98\n    PERCENT_PER_SECOND = 99\n    PER_MINUTE = 100\n    PER_SECOND = 101\n    PSI_PER_DEGREE_FAHRENHEIT = 102\n    RADIANS = 103\n    REVOLUTIONS_PER_MINUTE = 104\n    CURRENCY1 = 105\n    CURRENCY2 = 106\n    CURRENCY3 = 107\n    CURRENCY4 = 108\n    CURRENCY5 = 109\n    CURRENCY6 = 110\n    CURRENCY7 = 111\n    CURRENCY8 = 112\n    CURRENCY9 = 113\n    CURRENCY10 = 114\n    SQUARE_INCHES = 115\n    SQUARE_CENTIMETERS = 116\n    BTUS_PER_POUND = 117\n    CENTIMETERS = 118\n    POUNDS_MASS_PER_SECOND = 119\n    DELTA_DEGREES_FAHRENHEIT = 120\n    DELTA_KELVIN = 121\n    KILOHMS = 122\n    MEGOHMS = 123\n    MILLIVOLTS = 124\n    KILOJOULES_PER_KILOGRAM = 125\n    MEGAJOULES = 126\n    JOULES_PER_DEGREE_KELVIN = 127\n    JOULES_PER_KILOGRAM_DEGREE_KELVIN = 128\n    KILOHERTZ = 129\n    MEGAHERTZ = 130\n    PER_HOUR = 131\n    MILLIWATTS = 132\n    HECTOPASCALS = 133\n    MILLIBARS = 134\n    CUBIC_METERS_PER_HOUR = 135\n    LITERS_PER_HOUR = 136\n    KW_HOURS_PER_SQUARE_METER = 137\n    KW_HOURS_PER_SQUARE_FOOT = 138\n    MEGAJOULES_PER_SQUARE_METER = 139\n    MEGAJOULES_PER_SQUARE_FOOT = 140\n    WATTS_PER_SQUARE_METER_DEGREE_KELVIN = 141\n    CUBIC_FEET_PER_SECOND = 142\n    PERCENT_OBSCURATION_PER_FOOT = 143\n    PERCENT_OBSCURATION_PER_METER = 144\n    MILLIOHMS = 145\n    MEGAWATT_HOURS = 146\n    KILO_BTUS = 147\n    MEGA_BTUS = 148\n    KILOJOULES_PER_KILOGRAM_DRY_AIR = 149\n    MEGAJOULES_PER_KILOGRAM_DRY_AIR = 150\n    KILOJOULES_PER_DEGREE_KELVIN = 151\n    MEGAJOULES_PER_DEGREE_KELVIN = 152\n    NEWTON = 153\n    GRAMS_PER_SECOND = 154\n    GRAMS_PER_MINUTE = 155\n    TONS_PER_HOUR = 156\n    KILO_BTUS_PER_HOUR = 157\n    HUNDREDTHS_SECONDS = 158\n    MILLISECONDS = 159\n    NEWTON_METERS = 160\n    MILLIMETERS_PER_SECOND = 161\n    MILLIMETERS_PER_MINUTE = 162\n    METERS_PER_MINUTE = 163\n    METERS_PER_HOUR = 164\n    CUBIC_METERS_PER_MINUTE = 165\n    METERS_PER_SECOND_PER_SECOND = 166\n    AMPERES_PER_METER = 167\n    AMPERES_PER_SQUARE_METER = 168\n    AMPERE_SQUARE_METERS = 169\n    FARADS = 170\n    HENRYS = 171\n    OHM_METERS = 172\n    SIEMENS_PER_METER = 174\n    TESLAS = 175\n    VOLTS_PER_DEGREE_KELVIN = 176\n    VOLTS_PER_METER = 177\n    WEBERS = 178\n    CANDELAS = 179\n    CANDELAS_PER_SQUARE_METER = 180\n    KELVIN_PER_HOUR = 181\n    KELVIN_PER_MINUTE = 182\n    JOULE_SECONDS = 183\n    RADIANS_PER_SECOND = 184\n    SQUARE_METERS_PER_NEWTON = 185\n    KILOGRAMS_PER_CUBIC_METER = 186\n    NEWTON_SECONDS = 187\n    NEWTONS_PER_METER = 188\n    WATTS_PER_METER_PER_DEGREE_KELVIN = 189\n    MICROSIEMENS = 190\n    CUBIC_FEET_PER_HOUR = 191\n    US_GALLONS_PER_HOUR = 192\n    KILOMETERS = 193\n    MICROMETERS = 194\n    GRAMS = 195\n    MILLIGRAMS = 196\n    MILLILITERS = 197\n    MILLILITERS_PER_SECOND = 198\n    DECIBELS = 199\n    DECIBELS_MILLIVOLT = 200\n    DECIBELS_VOLT = 201\n    MILLISIEMENS = 202\n    WATT_REACTIVE_HOURS = 203\n    KILOWATT_REACTIVE_HOURS = 204\n    MEGAWATT_REACTIVE_HOURS = 205\n    MILLIMETERS_OF_WATER = 206\n    PER_MILLE = 207\n    GRAMS_PER_GRAM = 208\n    KILOGRAMS_PER_KILOGRAM = 209\n    GRAMS_PER_KILOGRAM = 210\n    MILLIGRAMS_PER_GRAM = 211\n    MILLIGRAMS_PER_KILOGRAM = 212\n    GRAMS_PER_MILLILITER = 213\n    GRAMS_PER_LITER = 214\n    MILLIGRAMS_PER_LITER = 215\n    MICROGRAMS_PER_LITER = 216\n    GRAMS_PER_CUBIC_METER = 217\n    MILLIGRAMS_PER_CUBIC_METER = 218\n    MICROGRAMS_PER_CUBIC_METER = 219\n    NANOGRAMS_PER_CUBIC_METER = 220\n    GRAMS_PER_CUBIC_CENTIMETER = 221\n    BECQUERELS = 222\n    KILOBECQUERELS = 223\n    MEGABECQUERELS = 224\n    GRAY = 225\n    MILLIGRAY = 226\n    MICROGRAY = 227\n    SIEVERTS = 228\n    MILLISIEVERTS = 229\n    MICROSIEVERTS = 230\n    MICROSIEVERTS_PER_HOUR = 231\n    DECIBELS_A = 232\n    NEPHELOMETRIC_TURBIDITY_UNIT = 233\n    PH = 234\n    GRAMS_PER_SQUARE_METER = 235\n    MINUTES_PER_DEGREE_KELVIN = 236\n    OHM_METER_SQUARED_PER_METER = 237\n    AMPERE_SECONDS = 238\n    VOLT_AMPERE_HOURS = 239\n    KILOVOLT_AMPERE_HOURS = 240\n    MEGAVOLT_AMPERE_HOURS = 241\n    VOLT_AMPERE_REACTIVE_HOURS = 242\n    KILOVOLT_AMPERE_REACTIVE_HOURS = 243\n    MEGAVOLT_AMPERE_REACTIVE_HOURS = 244\n    VOLT_SQUARE_HOURS = 245\n    AMPERE_SQUARE_HOURS = 246\n    JOULE_PER_HOURS = 247\n    CUBIC_FEET_PER_DAY = 248\n    CUBIC_METERS_PER_DAY = 249\n    WATT_HOURS_PER_CUBIC_METER = 250\n    JOULES_PER_CUBIC_METER = 251\n    MOLE_PERCENT = 252\n    PASCAL_SECONDS = 253\n    MILLION_STANDARD_CUBIC_FEET_PER_MINUTE = 254\n"
  },
  {
    "path": "components/zigbee/esp_zb_event.h",
    "content": "#pragma once\n\n#ifdef USE_ESP32\n\n#include <cstddef>  // for offsetof\n#include <cstring>  // for memcpy\n#include \"esp_zigbee_core.h\"\n#include \"zboss_api.h\"\n#include \"ha/esp_zigbee_ha_standard.h\"\n\nnamespace esphome::zigbee {\n\nclass ZBEvent {\n public:\n  ZBEvent(esp_zb_device_cb_common_info_t info, esp_zb_zcl_attribute_t attribute, uint8_t *current_level) {\n    this->callback_id_ = ESP_ZB_CORE_SET_ATTR_VALUE_CB_ID;\n    this->init_set_attr_value_data(info, attribute, current_level);\n  }\n\n  ZBEvent(const esp_zb_zcl_report_attr_message_t *message) {\n    this->callback_id_ = ESP_ZB_CORE_REPORT_ATTR_CB_ID;\n    this->init_report_attr_data(message);\n  }\n\n  ZBEvent(esp_zb_zcl_cmd_info_t info, esp_zb_zcl_read_attr_resp_variable_t *variables) {\n    this->callback_id_ = ESP_ZB_CORE_CMD_READ_ATTR_RESP_CB_ID;\n    this->init_read_attr_resp_data(info, variables);\n  }\n\n  ~ZBEvent() { this->release(); }\n\n  ZBEvent() : event_{}, callback_id_(ESP_ZB_CORE_BASIC_RESET_TO_FACTORY_RESET_CB_ID) {}\n\n  void release() {\n    // Free any allocated memory within the event\n    switch (this->callback_id_) {\n      case ESP_ZB_CORE_SET_ATTR_VALUE_CB_ID:\n        if (this->event_.set_attr.attribute.data.value != nullptr &&\n            this->event_.set_attr.attribute.data.value != this->event_.set_attr.inline_data) {\n          free(this->event_.set_attr.attribute.data.value);\n          this->event_.set_attr.attribute.data.value = nullptr;\n        }\n        break;\n      case ESP_ZB_CORE_REPORT_ATTR_CB_ID:\n        if (this->event_.report_attr.attribute.data.value != nullptr &&\n            this->event_.report_attr.attribute.data.value != this->event_.report_attr.inline_data) {\n          free(this->event_.report_attr.attribute.data.value);\n          this->event_.report_attr.attribute.data.value = nullptr;\n        }\n        break;\n      case ESP_ZB_CORE_CMD_READ_ATTR_RESP_CB_ID: {\n        esp_zb_zcl_read_attr_resp_variable_t *var = &(this->event_.read_attr_resp.variables);\n        while (var != nullptr) {\n          if (var->attribute.data.value != nullptr &&\n              var->attribute.data.value != this->event_.read_attr_resp.inline_data) {\n            free(var->attribute.data.value);\n            var->attribute.data.value = nullptr;\n          }\n          var = var->next;\n        }\n      }\n        // Reset the event data\n        this->callback_id_ = ESP_ZB_CORE_BASIC_RESET_TO_FACTORY_RESET_CB_ID;  // or some invalid value\n        break;\n      default:\n        break;\n    }\n  }\n\n  void load_set_attr_value_event(esp_zb_device_cb_common_info_t info, esp_zb_zcl_attribute_t attribute,\n                                 uint8_t *current_level) {\n    this->release();\n    this->callback_id_ = ESP_ZB_CORE_SET_ATTR_VALUE_CB_ID;\n    this->init_set_attr_value_data(info, attribute, current_level);\n  }\n\n  void load_report_attr_event(const esp_zb_zcl_report_attr_message_t *message) {\n    this->release();\n    this->callback_id_ = ESP_ZB_CORE_REPORT_ATTR_CB_ID;\n    this->init_report_attr_data(message);\n  }\n\n  void load_read_attr_resp_event(esp_zb_zcl_cmd_info_t info, esp_zb_zcl_read_attr_resp_variable_t *variables) {\n    this->release();\n    this->callback_id_ = ESP_ZB_CORE_CMD_READ_ATTR_RESP_CB_ID;\n    this->init_read_attr_resp_data(info, variables);\n  }\n\n  // Disable copy to prevent double-delete\n  ZBEvent(const ZBEvent &) = delete;\n  ZBEvent &operator=(const ZBEvent &) = delete;\n\n  union {\n    struct set_attr_event {\n      esp_zb_device_cb_common_info_t info;\n      esp_zb_zcl_attribute_t attribute;\n      uint8_t current_level;\n      bool has_current_level;\n      uint8_t inline_data[4];  // For small data types\n    } set_attr;\n    struct report_attr_event {\n      uint8_t dst_endpoint;\n      uint16_t cluster;\n      esp_zb_zcl_attribute_t attribute;\n      esp_zb_zcl_addr_t src_address;\n      uint8_t src_endpoint;\n      uint8_t inline_data[4];  // For small data types\n    } report_attr;\n    struct read_attr_resp_event {\n      esp_zb_zcl_cmd_info_t info;\n      esp_zb_zcl_read_attr_resp_variable_t variables;\n      uint8_t inline_data[4];  // For small data types\n    } read_attr_resp;\n  } event_;\n\n  esp_zb_core_action_callback_id_t callback_id_;\n\n private:\n  void init_set_attr_value_data(esp_zb_device_cb_common_info_t info, esp_zb_zcl_attribute_t attribute,\n                                uint8_t *current_level) {\n    this->event_.set_attr.info = info;\n    this->event_.set_attr.attribute = attribute;\n    // get attribute.data.value with correct type\n    this->event_.set_attr.has_current_level = (current_level != nullptr);\n    if (current_level != nullptr) {\n      this->event_.set_attr.current_level = *current_level;\n    }\n    if (attribute.data.value != nullptr) {\n      // Copy the attribute value to avoid dangling pointer issues\n      size_t value_size = this->get_attribute_value_size_(attribute.data);\n      if (value_size > 4) {\n        this->event_.set_attr.attribute.data.value = malloc(value_size);\n        if (this->event_.set_attr.attribute.data.value != nullptr) {\n          memcpy(this->event_.set_attr.attribute.data.value, attribute.data.value, value_size);\n        }\n      } else {\n        memcpy(this->event_.set_attr.inline_data, attribute.data.value, value_size);\n        this->event_.set_attr.attribute.data.value = this->event_.set_attr.inline_data;\n      }\n    }\n  }\n\n  void init_report_attr_data(const esp_zb_zcl_report_attr_message_t *message) {\n    this->event_.report_attr.dst_endpoint = message->dst_endpoint;\n    this->event_.report_attr.cluster = message->cluster;\n    this->event_.report_attr.attribute = message->attribute;\n    this->event_.report_attr.src_address = message->src_address;\n    this->event_.report_attr.src_endpoint = message->src_endpoint;\n    if (message->attribute.data.value != nullptr) {\n      // Copy the attribute value to avoid dangling pointer issues\n      size_t value_size = this->get_attribute_value_size_(message->attribute.data);\n      if (value_size > 4) {\n        this->event_.report_attr.attribute.data.value = malloc(value_size);\n        if (this->event_.report_attr.attribute.data.value != nullptr) {\n          memcpy(this->event_.report_attr.attribute.data.value, message->attribute.data.value, value_size);\n        }\n      } else {\n        memcpy(this->event_.report_attr.inline_data, message->attribute.data.value, value_size);\n        this->event_.report_attr.attribute.data.value = this->event_.report_attr.inline_data;\n      }\n    }\n  }\n\n  void init_read_attr_resp_data(esp_zb_zcl_cmd_info_t info, esp_zb_zcl_read_attr_resp_variable_t *variables) {\n    this->event_.read_attr_resp.info = info;\n    if (variables == nullptr) {\n      return;\n    }\n    this->event_.read_attr_resp.variables.status = variables->status;\n    this->event_.read_attr_resp.variables.attribute.id = variables->attribute.id;\n    this->event_.read_attr_resp.variables.attribute.data.type = variables->attribute.data.type;\n    this->event_.read_attr_resp.variables.attribute.data.size = variables->attribute.data.size;\n    // Note: variables is a pointer to a struct/list; deep copy needed\n    if (variables->attribute.data.value != nullptr) {\n      size_t value_size = this->get_attribute_value_size_(variables->attribute.data);\n      if (value_size > 4) {\n        this->event_.read_attr_resp.variables.attribute.data.value = malloc(value_size);\n        if (this->event_.read_attr_resp.variables.attribute.data.value != nullptr) {\n          memcpy(this->event_.read_attr_resp.variables.attribute.data.value, variables->attribute.data.value,\n                 value_size);\n        }\n      } else {\n        memcpy(this->event_.read_attr_resp.inline_data, variables->attribute.data.value, value_size);\n        this->event_.read_attr_resp.variables.attribute.data.value = this->event_.read_attr_resp.inline_data;\n      }\n    }\n    esp_zb_zcl_read_attr_resp_variable_t *var = variables;\n    esp_zb_zcl_read_attr_resp_variable_t *var_last = &(this->event_.read_attr_resp.variables);\n    while (var->next != nullptr) {\n      var = var->next;\n      size_t value_size = this->get_attribute_value_size_(var->attribute.data);\n      var_last->next = new esp_zb_zcl_read_attr_resp_variable_t();\n      if (var_last->next != nullptr) {\n        var_last->next->attribute.id = var->attribute.id;\n        var_last->next->status = var->status;\n        var_last->next->attribute.data.type = var->attribute.data.type;\n        var_last->next->attribute.data.size = var->attribute.data.size;\n        var_last->next->attribute.data.value = malloc(value_size);\n        if (var_last->next->attribute.data.value != nullptr) {\n          memcpy(var_last->next->attribute.data.value, var->attribute.data.value, value_size);\n        }\n        var_last = var_last->next;\n      }\n    }\n  }\n\n  size_t get_attribute_value_size_(esp_zb_zcl_attribute_data_t data) {\n    // Determine attribute value size based on attribute type (and size)\n    uint8_t len = 0;\n    switch (data.type) {\n      case ESP_ZB_ZCL_ATTR_TYPE_BOOL:\n        return sizeof(bool);\n      case ESP_ZB_ZCL_ATTR_TYPE_U8:\n        return sizeof(uint8_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_U16:\n        return sizeof(uint16_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_U24:\n        return 3;  // 24 bits = 3 bytes\n      case ESP_ZB_ZCL_ATTR_TYPE_U32:\n        return sizeof(uint32_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_U40:\n        return 5;  // 40 bits = 5 bytes\n      case ESP_ZB_ZCL_ATTR_TYPE_U48:\n        return 6;  // 48 bits = 6 bytes\n      case ESP_ZB_ZCL_ATTR_TYPE_U56:\n        return 7;  // 56 bits = 7 bytes\n      case ESP_ZB_ZCL_ATTR_TYPE_U64:\n        return sizeof(uint64_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_S8:\n        return sizeof(int8_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_S16:\n        return sizeof(int16_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_S24:\n        return 3;  // 24 bits = 3 bytes\n      case ESP_ZB_ZCL_ATTR_TYPE_S32:\n        return sizeof(int32_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_S40:\n        return 5;  // 40 bits = 5 bytes\n      case ESP_ZB_ZCL_ATTR_TYPE_S48:\n        return 6;  // 48 bits = 6 bytes\n      case ESP_ZB_ZCL_ATTR_TYPE_S56:\n        return 7;  // 56 bits = 7 bytes\n      case ESP_ZB_ZCL_ATTR_TYPE_S64:\n        return sizeof(int64_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_8BITMAP:\n        return sizeof(uint8_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_16BITMAP:\n        return sizeof(uint16_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_24BITMAP:\n        return 3;  // 24 bits = 3 bytes\n      case ESP_ZB_ZCL_ATTR_TYPE_32BITMAP:\n        return sizeof(uint32_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_40BITMAP:\n        return 5;  // 40 bits = 5 bytes\n      case ESP_ZB_ZCL_ATTR_TYPE_48BITMAP:\n        return 6;  // 48 bits = 6 bytes\n      case ESP_ZB_ZCL_ATTR_TYPE_56BITMAP:\n        return 7;  // 56 bits = 7 bytes\n      case ESP_ZB_ZCL_ATTR_TYPE_64BITMAP:\n        return sizeof(uint64_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_8BIT:\n        return sizeof(uint8_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_16BIT:\n        return sizeof(uint16_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_24BIT:\n        return 3;  // 24 bits = 3 bytes\n      case ESP_ZB_ZCL_ATTR_TYPE_32BIT:\n        return sizeof(uint32_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_40BIT:\n        return 5;  // 40 bits = 5 bytes\n      case ESP_ZB_ZCL_ATTR_TYPE_48BIT:\n        return 6;  // 48 bits = 6 bytes\n      case ESP_ZB_ZCL_ATTR_TYPE_56BIT:\n        return 7;  // 56 bits = 7 bytes\n      case ESP_ZB_ZCL_ATTR_TYPE_64BIT:\n        return sizeof(uint64_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_8BIT_ENUM:\n        return sizeof(uint8_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_16BIT_ENUM:\n        return sizeof(uint16_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_SEMI:\n        return sizeof(uint16_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_SINGLE:\n        return sizeof(float);\n      case ESP_ZB_ZCL_ATTR_TYPE_DOUBLE:\n        return sizeof(double);\n      case ESP_ZB_ZCL_ATTR_TYPE_CHAR_STRING:\n        len = *((uint8_t *) data.value);  // first byte is length\n        return len + 1;                   // length byte + string\n      case ESP_ZB_ZCL_ATTR_TYPE_TIME_OF_DAY:\n        return sizeof(uint32_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_DATE:\n        return sizeof(uint32_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_UTC_TIME:\n        return sizeof(uint32_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_CLUSTER_ID:\n        return sizeof(uint16_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_ATTRIBUTE_ID:\n        return sizeof(uint16_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_BACNET_OID:\n        return sizeof(uint32_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_IEEE_ADDR:\n        return sizeof(uint64_t);\n      case ESP_ZB_ZCL_ATTR_TYPE_128_BIT_KEY:\n        return 16;  // 128 bits = 16 bytes\n      default:\n        return 0;  // Unknown type\n    }\n  }\n};\n}  // namespace esphome::zigbee\n#endif  // USE_ESP32"
  },
  {
    "path": "components/zigbee/files_to_parse/parse_zigbee_headers.py",
    "content": "#!/usr/bin/env python3\n\"\"\"\nCreate python enums\n\"\"\"\n\nfrom pycparser import c_ast, parse_file\n\nfilename = \"esp_zigbee_zcl_common.h\"\n\nast = parse_file(filename, use_cpp=True)\n\ntypedefs = [t[1].type for t in ast.children() if isinstance(t[1], c_ast.Typedef)]\nmy_enums = [t for t in typedefs if isinstance(t.type, c_ast.Enum)]\n\n\ndef print_enum(enum):\n    print(enum.declname + \":\")\n    print(\n        \"\\n\".join([e.name + \": \" + e.value.value for e in enum.type.values.enumerators])\n    )\n\n\ndef write_profileIDs(enums):\n    enum = list(filter(lambda e: e.declname == \"esp_zb_ha_standard_devices_t\", enums))[\n        0\n    ]\n    enum_name = \"ha_standard_devices\"\n    to_py = f'{enum_name} = cg.esphome_ns.enum(\"{enum.declname}\")\\nDEVICE_ID' + \" = {\\n\"\n    for e in enum.type.values.enumerators:\n        to_py += f'    \"{e.name.removeprefix(\"ESP_ZB_HA_\").removesuffix(\"_DEVICE_ID\")}\": {enum_name}.{e.name},\\n'\n        to_py += f'    {(e.value.value.removesuffix(\"U\").upper().replace(\"X\",\"x\"))}: {enum_name}.{e.name},\\n'\n    to_py += \"}\\n\"\n    return to_py\n\n\ndef write_clusterIDs(enums):\n    enum = list(filter(lambda e: e.declname == \"esp_zb_zcl_cluster_id_t\", enums))[0]\n    enum_name = \"cluster_id\"\n    to_py = (\n        f'{enum_name} = cg.esphome_ns.enum(\"{enum.declname}\")\\nCLUSTER_ID' + \" = {\\n\"\n    )\n    for e in enum.type.values.enumerators:\n        to_py += f'    \"{e.name.removeprefix(\"ESP_ZB_ZCL_CLUSTER_ID_\")}\": {enum_name}.{e.name},\\n'\n        to_py += f'    {(e.value.value.removesuffix(\"U\").upper().replace(\"X\",\"x\"))}: {enum_name}.{e.name},\\n'\n    to_py += \"}\\n\"\n    return to_py\n\n\ndef write_clusterRoles(enums):\n    enum = list(filter(lambda e: e.declname == \"esp_zb_zcl_cluster_role_t\", enums))[0]\n    enum_name = \"cluster_role\"\n    to_py = (\n        f'{enum_name} = cg.esphome_ns.enum(\"{enum.declname}\")\\nCLUSTER_ROLE' + \" = {\\n\"\n    )\n    for e in enum.type.values.enumerators:\n        to_py += f'    \"{e.name.removeprefix(\"ESP_ZB_ZCL_CLUSTER_\").removesuffix(\"_ROLE\")}\": {enum_name}.{e.name},\\n'\n    to_py += \"}\\n\"\n    return to_py\n\n\ndef write_ZBtypes(enums):\n    enum = list(filter(lambda e: e.declname == \"esp_zb_zcl_attr_type_t\", enums))[0]\n    enum_name = \"attr_type\"\n    to_py = f'{enum_name} = cg.esphome_ns.enum(\"{enum.declname}\")\\nATTR_TYPE' + \" = {\\n\"\n    for e in enum.type.values.enumerators:\n        to_py += f'    \"{e.name.removeprefix(\"ESP_ZB_ZCL_ATTR_TYPE_\")}\": {enum_name}.{e.name},\\n'\n    to_py += \"}\\n\"\n    return to_py\n\n\nwith open(\"zigbee_const.py\", \"w\", encoding=\"utf-8\") as f:\n    f.write(\"import esphome.codegen as cg\\n\\n\")\n    f.write(write_profileIDs(my_enums))\n    f.write(write_clusterIDs(my_enums))\n    f.write(write_clusterRoles(my_enums))\n    f.write(write_ZBtypes(my_enums))\n    f.write(\n        \"\"\"ATTR_ACCESS = {\n    \"READ_ONLY\": 1,\n    \"WRITE_ONLY\": 2,\n    \"READ_WRITE\": 3,\n}\n\"\"\"\n    )\n# [print_enum(e) for e in enums]\n\nreplacements_cl = {\n    \"level_control\": \"level\",\n    \"multi_value\": \"multistate_value\",\n    \"multi_input\": \"multistate_input\",\n    \"multi_output\": \"multistate_output\",\n    \"ota_upgrade\": \"ota\",\n    \"illuminance_measurement\": \"illuminance_meas\",\n    \"temp_measurement\": \"temperature_meas\",\n    \"pressure_measurement\": \"pressure_meas\",\n    \"rel_humidity_measurement\": \"humidity_meas\",\n    \"electrical_measurement\": \"electrical_meas\",\n    \"flow_measurement\": \"flow_meas\",\n}\n\nremove_cl = [\n    \"rssi_location\",\n    \"green_power\",\n    \"keep_alive\",\n    \"pump_config_control\",\n    \"ballast_config\",\n]\n\n\ndef write_cluster_list_aou(enums):\n    enum = list(filter(lambda e: e.declname == \"esp_zb_zcl_cluster_id_t\", enums))[0]\n    ids = [e.name for e in enum.type.values.enumerators]\n    to_py = \"esp_err_t esphome_zb_cluster_list_add_or_update_cluster(uint16_t cluster_id, esp_zb_cluster_list_t *cluster_list, esp_zb_attribute_list_t *attr_list, uint8_t role_mask) {\\n  esp_err_t ret;\\n\"\n    to_py += \"  ret = esp_zb_cluster_list_update_cluster(cluster_list, attr_list, cluster_id, role_mask);\\n\"\n    to_py += \"  if (ret != ESP_OK) {\\n\"\n    to_py += (\n        '    ESP_LOGE(\"zigbee_helper\", \"Ignore previous cluster not found error\");\\n'\n    )\n    to_py += \"    switch (cluster_id) {\\n\"\n    for id in ids:\n        cluster_name = id.removeprefix(\"ESP_ZB_ZCL_CLUSTER_ID_\").lower()\n        if cluster_name in remove_cl:\n            continue\n        for k, i in replacements_cl.items():\n            cluster_name = cluster_name.replace(k, i)\n        to_py += f\"      case {id}:\\n\"\n        to_py += f\"        ret = esp_zb_cluster_list_add_{cluster_name}_cluster(cluster_list, attr_list, role_mask);\\n\"\n        to_py += \"        break;\\n\"\n    to_py += \"      default:\\n        ret = esp_zb_cluster_list_add_custom_cluster(cluster_list, attr_list, role_mask);\\n    }\\n  }\\n  return ret;\\n}\\n\\n\"\n    return to_py\n\n\nreplacements_attrl = {\n    \"level_control\": \"level\",\n    \"multi_value\": \"multistate_value\",\n    \"multi_input\": \"multistate_input\",\n    \"multi_output\": \"multistate_output\",\n    \"ota_upgrade\": \"ota\",\n    \"illuminance_measurement\": \"illuminance_meas\",\n    \"temp_measurement\": \"temperature_meas\",\n    \"pressure_measurement\": \"pressure_meas\",\n    \"rel_humidity_measurement\": \"humidity_meas\",\n    \"electrical_measurement\": \"electrical_meas\",\n    \"flow_measurement\": \"flow_meas\",\n}\n\nvoid_list = []\n\n\ndef write_attr_list_create(enums):\n    enum = list(filter(lambda e: e.declname == \"esp_zb_zcl_cluster_id_t\", enums))[0]\n    ids = [e.name for e in enum.type.values.enumerators]\n    to_py = \"esp_zb_attribute_list_t *esphome_zb_default_attr_list_create(uint16_t cluster_id) {\\n\"\n    to_py += \"  switch (cluster_id) {\\n\"\n    for id in ids:\n        cluster_name = id.removeprefix(\"ESP_ZB_ZCL_CLUSTER_ID_\").lower()\n        if cluster_name in remove_cl:\n            continue\n        for k, i in replacements_attrl.items():\n            cluster_name = cluster_name.replace(k, i)\n        to_py += f\"    case {id}:\\n\"\n        if cluster_name in void_list:\n            to_py += f\"      return esp_zb_{cluster_name}_cluster_create();\\n\"\n        else:\n            to_py += f\"      return esp_zb_{cluster_name}_cluster_create(NULL);\\n\"\n    to_py += \"    default:\\n      return esp_zb_zcl_attr_list_create(cluster_id);\\n  }\\n}\\n\\n\"\n    return to_py\n\n\nremove_attra = [\n    \"rssi_location\",\n    \"green_power\",\n    \"keep_alive\",\n    \"pump_config_control\",\n    \"ballast_config\",\n    \"ias_ace\",\n    \"price\",\n    \"metering\",\n]\n\n\ndef write_attr_add(enums):\n    enum = list(filter(lambda e: e.declname == \"esp_zb_zcl_cluster_id_t\", enums))[0]\n    ids = [e.name for e in enum.type.values.enumerators]\n    to_py = \"esp_err_t esphome_zb_cluster_add_attr(uint16_t cluster_id, esp_zb_attribute_list_t *attr_list, uint16_t attr_id, void *value_p) {\\n\"\n    to_py += \"  switch (cluster_id) {\\n\"\n    for id in ids:\n        cluster_name = id.removeprefix(\"ESP_ZB_ZCL_CLUSTER_ID_\").lower()\n        if cluster_name in remove_attra:\n            continue\n        for k, i in replacements_cl.items():\n            cluster_name = cluster_name.replace(k, i)\n        to_py += f\"    case {id}:\\n\"\n        to_py += f\"      return esp_zb_{cluster_name}_cluster_add_attr(attr_list, attr_id, value_p);\\n\"\n    to_py += \"    default:\\n      return ESP_FAIL;\\n  }\\n}\\n\\n\"\n    return to_py\n\n\nwith open(\"c_helpers.c\", \"w\", encoding=\"utf-8\") as f:\n    f.write(write_cluster_list_aou(my_enums))\n    f.write(write_attr_list_create(my_enums))\n    f.write(write_attr_add(my_enums))\n"
  },
  {
    "path": "components/zigbee/partitions_zb.csv",
    "content": "otadata,  data, ota,     ,        0x2000,\nphy_init, data, phy,     ,        0x1000,\napp0,     app,  ota_0,   ,        0x1B0000,\napp1,     app,  ota_1,   ,        0x1B0000,\nnvs,      data, nvs,     ,        0x6D000,\nzb_storage, data, fat,   , 16K,\nzb_fct,     data, fat,   , 1K,\n"
  },
  {
    "path": "components/zigbee/time/__init__.py",
    "content": "import esphome.codegen as cg\nfrom esphome.components import time as time_\nimport esphome.config_validation as cv\nfrom esphome.const import CONF_ID\nfrom esphome.cpp_generator import get_variable\n\nfrom ..const import CONF_ZIGBEE_ID\n\nDEPENDENCIES = [\"zigbee\"]\n\nzigbee_ns = cg.esphome_ns.namespace(\"zigbee\")\nZigbeeTime = zigbee_ns.class_(\"ZigbeeTime\", time_.RealTimeClock)\nZigBeeComponent = zigbee_ns.class_(\"ZigBeeComponent\", cg.Component)\nCONFIG_SCHEMA = time_.TIME_SCHEMA.extend(\n    {\n        cv.GenerateID(): cv.declare_id(ZigbeeTime),\n        cv.GenerateID(CONF_ZIGBEE_ID): cv.use_id(ZigBeeComponent),\n    }\n).extend(cv.COMPONENT_SCHEMA)\n\n\nasync def to_code(config):\n    cg.add_define(\"USE_ZIGBEE_TIME\")\n    zb = await get_variable(config[CONF_ZIGBEE_ID])\n    var = cg.new_Pvariable(config[CONF_ID], zb)\n    await cg.register_component(var, config)\n    await time_.register_time(var, config)\n"
  },
  {
    "path": "components/zigbee/time/zigbee_time.cpp",
    "content": "#include \"zigbee_time.h\"\n#include \"esphome/core/log.h\"\n\nnamespace esphome {\nnamespace zigbee {\n\nvoid ZigbeeTime::send_timesync_request() {\n  ESP_LOGD(TAG, \"Requesting time from coordinator...\");\n  uint16_t attributes[] = {ESP_ZB_ZCL_ATTR_TIME_TIME_ID, ESP_ZB_ZCL_ATTR_TIME_TIME_STATUS_ID};\n  esp_zb_zcl_read_attr_cmd_t read_req;\n  read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;\n  read_req.attr_field = attributes;\n  read_req.attr_number = sizeof(attributes) / sizeof(uint16_t);\n  read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TIME;\n  read_req.zcl_basic_cmd.dst_endpoint = 1;\n  read_req.zcl_basic_cmd.src_endpoint = 1;\n  read_req.zcl_basic_cmd.dst_addr_u.addr_short = 0x0000;  // coordinator\n  if (esp_zb_lock_acquire(30 / portTICK_PERIOD_MS)) {\n    esp_zb_zcl_read_attr_cmd_req(&read_req);\n    esp_zb_lock_release();\n    this->requested_ = true;\n    ESP_LOGD(TAG, \"Sent request\");\n  }\n}\n\nvoid ZigbeeTime::recieve_timesync_response(esp_zb_zcl_read_attr_resp_variable_t *variable) {\n  uint32_t utc = 0;\n  uint8_t sync_status = 0;\n  while (variable != nullptr) {\n    ESP_LOGD(TAG, \"Read attribute response: status(%d), attribute(0x%x), type(0x%x), value(%d)\", variable->status,\n             variable->attribute.id, variable->attribute.data.type,\n             variable->attribute.data.value ? *(uint32_t *) variable->attribute.data.value : 0);\n    switch (variable->attribute.id) {\n      case ESP_ZB_ZCL_ATTR_TIME_TIME_ID:\n        utc = *(uint32_t *) variable->attribute.data.value;\n        utc = utc + zigbee_time_offset;\n        ESP_LOGD(TAG, \"Recieved UTC time: %d\", utc);\n        break;\n      case ESP_ZB_ZCL_ATTR_TIME_TIME_STATUS_ID:\n        sync_status = *(uint8_t *) variable->attribute.data.value;\n        ESP_LOGD(TAG, \"Recieved sync status time: 0x%x\", sync_status);\n        break;\n      default:\n        ESP_LOGD(TAG, \"Recieved other time property: not yet handled\");\n        break;\n    }\n    variable = variable->next;\n  }\n  if ((utc != 0) && (sync_status & 0x3 != 0)) { /* 0x3 = either Master or Syncronized bits set */\n    this->set_utc_time(utc);\n  } else {\n    ESP_LOGD(TAG, \"Did not recieve both time and status; clock NOT updated\");\n  }\n}\n\nvoid ZigbeeTime::setup() {\n  ESP_LOGD(TAG, \"Using Zigbee network as time source\");\n  this->synced_ = false;\n  this->requested_ = false;\n  this->zc_->zt_ = this;\n}\n\nvoid ZigbeeTime::update() {\n  ESP_LOGD(TAG, \"Updating time sync from Zigbee network...\");\n  this->synced_ = false;\n}\n\nvoid ZigbeeTime::loop() {\n  if (this->synced_)\n    return;\n  if (this->requested_)\n    return;\n  if (zc_->is_connected()) {\n    this->send_timesync_request();\n  }\n}\n\nvoid ZigbeeTime::set_utc_time(uint32_t utc) {\n  this->synchronize_epoch_(utc);\n  this->synced_ = true;\n  this->requested_ = false;\n}\n\n}  // namespace zigbee\n}  // namespace esphome"
  },
  {
    "path": "components/zigbee/time/zigbee_time.h",
    "content": "#pragma once\n\n#include \"esphome/core/component.h\"\n#include \"esphome/components/time/real_time_clock.h\"\n#include \"../zigbee.h\"\n\nnamespace esphome {\nnamespace zigbee {\n\nstatic const uint32_t zigbee_time_offset =\n    946684800; /* Zigbee time is based on counting seconds from 1 Jan 2000 (=946684800) */\n\nclass ZigBeeComponent;\n\nclass ZigbeeTime : public time::RealTimeClock {\n public:\n  ZigbeeTime(ZigBeeComponent *zc) : zc_(zc) {}\n  void setup() override;\n  void loop() override;\n  void update() override;\n  void set_utc_time(uint32_t utc);\n  void send_timesync_request();\n  void recieve_timesync_response(esp_zb_zcl_read_attr_resp_variable_t *variable);\n\n protected:\n  ZigBeeComponent *zc_;\n  bool synced_;\n  bool requested_;\n};\n\n}  // namespace zigbee\n}  // namespace esphome\n"
  },
  {
    "path": "components/zigbee/types.py",
    "content": "from esphome import automation\nimport esphome.codegen as cg\n\nzigbee_ns = cg.esphome_ns.namespace(\"zigbee\")\nZigBeeComponent = zigbee_ns.class_(\"ZigBeeComponent\", cg.Component)\nZigBeeAttribute = zigbee_ns.class_(\"ZigBeeAttribute\", cg.Component)\nZigBeeOnValueTrigger = zigbee_ns.class_(\n    \"ZigBeeOnValueTrigger\", automation.Trigger.template(int), cg.Component\n)\nZigBeeOnReportTrigger = zigbee_ns.class_(\n    \"ZigBeeOnReportTrigger\", automation.Trigger.template(int), cg.Component\n)\nResetZigbeeAction = zigbee_ns.class_(\n    \"ResetZigbeeAction\", automation.Action, cg.Parented.template(ZigBeeComponent)\n)\nSetAttrAction = zigbee_ns.class_(\"SetAttrAction\", automation.Action)\nReportAttrAction = zigbee_ns.class_(\n    \"ReportAttrAction\", automation.Action, cg.Parented.template(ZigBeeAttribute)\n)\nReportAction = zigbee_ns.class_(\n    \"ReportAction\", automation.Action, cg.Parented.template(ZigBeeComponent)\n)\n"
  },
  {
    "path": "components/zigbee/zigbee.cpp",
    "content": "#include \"zigbee.h\"\n#include \"freertos/FreeRTOS.h\"\n#include \"freertos/task.h\"\n#include \"esp_check.h\"\n#include \"nvs_flash.h\"\n#include \"zigbee_attribute.h\"\n#include \"esphome/core/application.h\"\n#include \"esphome/core/log.h\"\n#include \"zigbee_helpers.h\"\n#ifdef CONFIG_WIFI_COEX\n#include \"esp_coexist.h\"\n#endif\n\n#if !(defined ZB_ED_ROLE || defined ZB_ROUTER_ROLE)\n#error Define ZB_ED_ROLE or ZB_ROUTER_ROLE in idf.py menuconfig.\n#endif\n\nnamespace esphome {\nnamespace zigbee {\n\nZigBeeComponent *global_zigbee;\n\ndevice_params_t coord;\n\n/**\n * Creates a ZCL string from the given input string.\n *\n * @param str          Pointer to the input null-terminated C-style string.\n * @param max_size     Maximum allowable size for the resulting ZCL string. Maximum value: 254.\n * @param use_max_size Optional. If true, the `max_size` is used directly,\n *                     overriding the actual size of the input string.\n * @return             Pointer to a dynamically allocated ZCL string.\n *                     NOTE: Caller is responsible for freeing the allocated memory with `delete[]`.\n *\n */\nuint8_t *get_zcl_string(const char *str, uint8_t max_size, bool use_max_size) {\n  uint8_t str_len = static_cast<uint8_t>(strlen(str));\n  uint8_t zcl_str_size = use_max_size ? max_size : std::min(max_size, str_len);\n\n  uint8_t *zcl_str = new uint8_t[zcl_str_size + 1];  // string + length octet\n  zcl_str[0] = zcl_str_size;\n  memcpy(zcl_str + 1, str, str_len);\n\n  return zcl_str;\n}\n\nstatic void bdb_start_top_level_commissioning_cb(uint8_t mode_mask) {\n  if (esp_zb_bdb_start_top_level_commissioning(mode_mask) != ESP_OK) {\n    ESP_LOGE(TAG, \"Start network steering failed!\");\n  }\n}\n\nvoid ZigBeeComponent::report() {\n  for (const auto &[_, attribute] : this->attributes_) {\n    attribute->report();\n  }\n}\n\nvoid esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {\n  static uint8_t steering_retry_count = 0;\n  uint32_t *p_sg_p = signal_struct->p_app_signal;\n  esp_err_t err_status = signal_struct->esp_err_status;\n  esp_zb_app_signal_type_t sig_type = (esp_zb_app_signal_type_t) *p_sg_p;\n  esp_zb_zdo_signal_leave_params_t *leave_params = NULL;\n  switch (sig_type) {\n    case ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP:\n      // Notifies the application that ZBOSS framework (scheduler, buffer pool, etc.) has started, but no\n      // join/rejoin/formation/BDB initialization has been done yet.\n      ESP_LOGD(TAG, \"Zigbee stack initialized\");\n      esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_INITIALIZATION);\n      break;\n    case ESP_ZB_BDB_SIGNAL_DEVICE_FIRST_START:\n      // Device started for the first time after the NVRAM erase\n    case ESP_ZB_BDB_SIGNAL_DEVICE_REBOOT:\n      // Device started using the NVRAM contents.\n      if (err_status == ESP_OK) {\n        ESP_LOGD(TAG, \"Device started up in %sfactory-reset mode\", esp_zb_bdb_is_factory_new() ? \"\" : \"non \");\n        global_zigbee->started_ = true;\n        if (esp_zb_bdb_is_factory_new()) {\n          ESP_LOGD(TAG, \"Start network steering\");\n          esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);\n        } else {\n          ESP_LOGD(TAG, \"Device rebooted\");\n          global_zigbee->searchBindings();\n        }\n      } else {\n        ESP_LOGE(TAG, \"FIRST_START.  Device started up in %sfactory-reset mode with an error %d (%s)\",\n                 esp_zb_bdb_is_factory_new() ? \"\" : \"non \", err_status, esp_err_to_name(err_status));\n        ESP_LOGW(TAG, \"Failed to initialize Zigbee stack (status: %s)\", esp_err_to_name(err_status));\n        esp_zb_scheduler_alarm((esp_zb_callback_t) bdb_start_top_level_commissioning_cb, ESP_ZB_BDB_MODE_INITIALIZATION,\n                               1000);\n      }\n      break;\n    case ESP_ZB_BDB_SIGNAL_STEERING:\n      // BDB network steering completed (Network steering only)\n      if (err_status == ESP_OK) {\n        steering_retry_count = 0;\n        esp_zb_ieee_addr_t extended_pan_id;\n        esp_zb_get_extended_pan_id(extended_pan_id);\n        ESP_LOGI(TAG,\n                 \"Joined network successfully (Extended PAN ID: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, PAN ID: \"\n                 \"0x%04hx, Channel:%d)\",\n                 extended_pan_id[7], extended_pan_id[6], extended_pan_id[5], extended_pan_id[4], extended_pan_id[3],\n                 extended_pan_id[2], extended_pan_id[1], extended_pan_id[0], esp_zb_get_pan_id(),\n                 esp_zb_get_current_channel());\n        global_zigbee->joined_ = true;\n        global_zigbee->enable_loop_soon_any_context();\n      } else {\n        ESP_LOGI(TAG, \"Network steering was not successful (status: %s)\", esp_err_to_name(err_status));\n        if (steering_retry_count < 10) {\n          steering_retry_count++;\n          esp_zb_scheduler_alarm((esp_zb_callback_t) bdb_start_top_level_commissioning_cb,\n                                 ESP_ZB_BDB_MODE_NETWORK_STEERING, 1000);\n        } else {\n          esp_zb_scheduler_alarm((esp_zb_callback_t) bdb_start_top_level_commissioning_cb,\n                                 ESP_ZB_BDB_MODE_NETWORK_STEERING, 600 * 1000);\n        }\n      }\n      break;\n    case ESP_ZB_ZDO_SIGNAL_LEAVE:\n      leave_params = (esp_zb_zdo_signal_leave_params_t *) esp_zb_app_signal_get_params(p_sg_p);\n      if (leave_params->leave_type == ESP_ZB_NWK_LEAVE_TYPE_RESET) {\n        ESP_LOGD(TAG, \"Reset device\");\n        esp_zb_factory_reset();\n      } else {\n        ESP_LOGD(TAG, \"Leave_type: %u\", leave_params->leave_type);\n      }\n      break;\n    default:\n      ESP_LOGD(TAG, \"ZDO signal: %s (0x%x), status: %s\", esp_zb_zdo_signal_to_string(sig_type), sig_type,\n               esp_err_to_name(err_status));\n      break;\n  }\n}\n\n// Recall bounded devices from the binding table after reboot\nvoid ZigBeeComponent::bindingTableCb(const esp_zb_zdo_binding_table_info_t *table_info, void *user_ctx) {\n  bool done = true;\n  esp_zb_zdo_mgmt_bind_param_t *req = (esp_zb_zdo_mgmt_bind_param_t *) user_ctx;\n  esp_zb_zdp_status_t zdo_status = (esp_zb_zdp_status_t) table_info->status;\n  ESP_LOGD(TAG, \"Binding table callback for address 0x%04x with status %d\", req->dst_addr, zdo_status);\n  if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {\n    // Print binding table log simple\n    ESP_LOGD(TAG, \"Binding table info: total %d, index %d, count %d\", table_info->total, table_info->index,\n             table_info->count);\n\n    if (table_info->total == 0) {\n      ESP_LOGD(TAG, \"No binding table entries found\");\n      free(req);\n      global_zigbee->connected_ = true;\n      return;\n    }\n\n    esp_zb_zdo_binding_table_record_t *record = table_info->record;\n    for (int i = 0; i < table_info->count; i++) {\n      ESP_LOGD(TAG, \"Binding table record: src_endp %d, dst_endp %d, cluster_id 0x%04x, dst_addr_mode %d\",\n               record->src_endp, record->dst_endp, record->cluster_id, record->dst_addr_mode);\n\n      zb_device_params_t *device = (zb_device_params_t *) calloc(1, sizeof(zb_device_params_t));\n      device->endpoint = record->dst_endp;\n      if (record->dst_addr_mode == ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT ||\n          record->dst_addr_mode == ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT) {\n        device->short_addr = record->dst_address.addr_short;\n      } else {  // ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT\n        memcpy(device->ieee_addr, record->dst_address.addr_long, sizeof(esp_zb_ieee_addr_t));\n      }\n      ESP_LOGD(TAG,\n               \"Device bound to EP %d -> device endpoint: %d, short addr: 0x%04x, ieee addr: \"\n               \"%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\",\n               record->src_endp, device->endpoint, device->short_addr, device->ieee_addr[7], device->ieee_addr[6],\n               device->ieee_addr[5], device->ieee_addr[4], device->ieee_addr[3], device->ieee_addr[2],\n               device->ieee_addr[1], device->ieee_addr[0]);\n      record = record->next;\n    }\n\n    // Continue reading the binding table\n    if (table_info->index + table_info->count < table_info->total) {\n      /* There are unreported binding table entries, request for them. */\n      req->start_index = table_info->index + table_info->count;\n      esp_zb_zdo_binding_table_req(req, bindingTableCb, req);\n      done = false;\n    }\n  }\n\n  if (done) {\n    // Print bound devices\n    ESP_LOGD(TAG, \"Filling bounded devices finished\");\n    free(req);\n    global_zigbee->connected_ = true;\n  }\n}\n\nvoid ZigBeeComponent::searchBindings() {\n  esp_zb_zdo_mgmt_bind_param_t *mb_req = (esp_zb_zdo_mgmt_bind_param_t *) malloc(sizeof(esp_zb_zdo_mgmt_bind_param_t));\n  mb_req->dst_addr = esp_zb_get_short_address();\n  mb_req->start_index = 0;\n  ESP_LOGD(TAG, \"Requesting binding table for address 0x%04x\", mb_req->dst_addr);\n  esp_zb_zdo_binding_table_req(mb_req, bindingTableCb, (void *) mb_req);\n}\n\nvoid load_zb_event(ZBEvent *event, esp_zb_device_cb_common_info_t info, esp_zb_zcl_attribute_t attribute,\n                   uint8_t *current_level) {\n  event->load_set_attr_value_event(info, attribute, current_level);\n}\n\nvoid load_zb_event(ZBEvent *event, const esp_zb_zcl_report_attr_message_t *message) {\n  event->load_report_attr_event(message);\n}\n\nvoid load_zb_event(ZBEvent *event, esp_zb_zcl_cmd_info_t info, esp_zb_zcl_read_attr_resp_variable_t *variables) {\n  event->load_read_attr_resp_event(info, variables);\n}\n\ntemplate<typename... Args> void enqueue_zb_event(Args... args) {\n  // Allocate an event from the pool\n  ZBEvent *event = global_zigbee->zb_event_pool_.allocate();\n  if (event == nullptr) {\n    // No events available - queue is full or we're out of memory\n    global_zigbee->zb_events_.increment_dropped_count();\n    return;\n  }\n\n  // Load new event data (replaces previous event)\n  load_zb_event(event, args...);\n\n  // Push the event to the queue\n  global_zigbee->zb_events_.push(event);\n  // Push always succeeds because we're the only producer and the pool ensures we never exceed queue size\n  global_zigbee->enable_loop_soon_any_context();\n  App.wake_loop_threadsafe();\n}\n\n// Explicit template instantiations for the friend function\ntemplate void enqueue_zb_event(esp_zb_device_cb_common_info_t info, esp_zb_zcl_attribute_t attribute,\n                               uint8_t *current_level);\ntemplate void enqueue_zb_event(const esp_zb_zcl_report_attr_message_t *message);\ntemplate void enqueue_zb_event(esp_zb_zcl_cmd_info_t info, esp_zb_zcl_read_attr_resp_variable_t *variables);\n\nstatic esp_err_t zb_attribute_handler(const esp_zb_zcl_set_attr_value_message_t *message) {\n  ESP_RETURN_ON_FALSE(message, ESP_FAIL, TAG, \"Empty message\");\n  ESP_RETURN_ON_FALSE(message->info.status == ESP_ZB_ZCL_STATUS_SUCCESS, ESP_ERR_INVALID_ARG, TAG,\n                      \"Received message: error status(%d)\", message->info.status);\n  ESP_LOGD(TAG, \"Received message: endpoint(%d), cluster(0x%x), attribute(0x%x), data size(%d)\",\n           message->info.dst_endpoint, message->info.cluster, message->attribute.id, message->attribute.data.size);\n\n  // if the attribute is On/Off and it is set to Off, restore the previous level\n  esp_zb_zcl_attr_t *current_level = nullptr;\n  if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_ON_OFF) {\n    if (message->attribute.id == ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID &&\n        message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_BOOL && !*(bool *) message->attribute.data.value) {\n      ESP_LOGD(TAG, \"turned off\");\n      if (esp_zb_zcl_get_cluster(message->info.dst_endpoint, ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL,\n                                 ESP_ZB_ZCL_CLUSTER_SERVER_ROLE) != nullptr) {\n        current_level =\n            esp_zb_zcl_get_attribute(message->info.dst_endpoint, ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL,\n                                     ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID);\n        if (current_level) {\n          ESP_LOGD(TAG, \"got level\");\n          esp_zb_zcl_set_attribute_val(message->info.dst_endpoint, ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL,\n                                       ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID,\n                                       current_level->data_p, false);\n        }\n      }\n    }\n  }\n  if (current_level != nullptr) {\n    enqueue_zb_event(message->info, message->attribute, (uint8_t *) current_level->data_p);\n  } else {\n    enqueue_zb_event(message->info, message->attribute, nullptr);\n  }\n  return ESP_OK;\n}\n\nstatic esp_err_t zb_cmd_attribute_handler(const esp_zb_zcl_cmd_read_attr_resp_message_t *message) {\n  ESP_RETURN_ON_FALSE(message, ESP_FAIL, TAG, \"Empty message\");\n  ESP_RETURN_ON_FALSE(message->info.status == ESP_ZB_ZCL_STATUS_SUCCESS, ESP_ERR_INVALID_ARG, TAG,\n                      \"Received message: error status(%d)\", message->info.status);\n  enqueue_zb_event(message->info, message->variables);\n  return ESP_OK;\n}\n\nstatic esp_err_t zb_report_attribute_handler(const esp_zb_zcl_report_attr_message_t *message) {\n  ESP_RETURN_ON_FALSE(message, ESP_FAIL, TAG, \"Empty message\");\n  ESP_RETURN_ON_FALSE(message->status == ESP_ZB_ZCL_STATUS_SUCCESS, ESP_ERR_INVALID_ARG, TAG,\n                      \"Received message: error status(%d)\", message->status);\n  enqueue_zb_event(message);\n  return ESP_OK;\n}\n\nstatic esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, const void *message) {\n  esp_err_t ret = ESP_OK;\n  switch (callback_id) {\n    case ESP_ZB_CORE_SET_ATTR_VALUE_CB_ID:\n      ret = zb_attribute_handler((esp_zb_zcl_set_attr_value_message_t *) message);\n      break;\n    case ESP_ZB_CORE_CMD_READ_ATTR_RESP_CB_ID:\n      ret = zb_cmd_attribute_handler((esp_zb_zcl_cmd_read_attr_resp_message_t *) message);\n      break;\n    case ESP_ZB_CORE_REPORT_ATTR_CB_ID:\n      ret = zb_report_attribute_handler((esp_zb_zcl_report_attr_message_t *) message);\n      break;\n    case ESP_ZB_CORE_CMD_DEFAULT_RESP_CB_ID:\n      ESP_LOGD(TAG, \"Receive Zigbee default response callback\");\n      break;\n    default:\n      ESP_LOGW(TAG, \"Receive Zigbee action(0x%x) callback\", callback_id);\n      break;\n  }\n  return ret;\n}\n\nvoid ZigBeeComponent::handle_attribute(esp_zb_device_cb_common_info_t info, esp_zb_zcl_attribute_t attribute,\n                                       uint8_t *current_level) {\n  if (this->attributes_.find({info.dst_endpoint, info.cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, attribute.id}) !=\n      this->attributes_.end()) {\n    this->attributes_[{info.dst_endpoint, info.cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, attribute.id}]->on_value(\n        attribute);\n    // if the attribute is On/Off and it is set to Off, restore the previous level\n    if (info.cluster == ESP_ZB_ZCL_CLUSTER_ID_ON_OFF && current_level != nullptr) {\n      if (attribute.id == ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID && attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_BOOL &&\n          !*(bool *) attribute.data.value) {\n        ESP_LOGD(TAG, \"turned off\");\n        if (this->attributes_.find({info.dst_endpoint, ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL,\n                                    ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID}) !=\n            this->attributes_.end()) {\n          ESP_LOGD(TAG, \"found level\");\n          esp_zb_zcl_attribute_t lvl_attr = {\n              .id = ESP_ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID,\n              .data =\n                  {\n                      .type = ESP_ZB_ZCL_ATTR_TYPE_U8,\n                      .size = sizeof(uint8_t),\n                      .value = current_level,\n                  },\n          };\n          this->attributes_[{info.dst_endpoint, ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE,\n                             ESP_ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID}]\n              ->on_value(lvl_attr);\n          ESP_LOGD(TAG, \"Light set to restore-level: %d\", *current_level);\n        }\n      }\n    }\n  }\n}\n\nvoid ZigBeeComponent::handle_report_attribute(uint8_t dst_endpoint, uint16_t cluster, esp_zb_zcl_attribute_t attribute,\n                                              esp_zb_zcl_addr_t src_address, uint8_t src_endpoint) {\n  auto attr = this->attributes_.find({dst_endpoint, cluster, ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE, attribute.id});\n  if (attr == this->attributes_.end()) {\n    ESP_LOGD(TAG, \"No attributes configured for report (endpoint %d; cluster 0x%04x; attribute id 0x%04x)\",\n             dst_endpoint, cluster, attribute.id);\n    return;\n  }\n  attr->second->on_report(attribute, src_address, src_endpoint);\n}\n\nvoid ZigBeeComponent::handle_read_attribute_response(esp_zb_zcl_cmd_info_t info,\n                                                     esp_zb_zcl_read_attr_resp_variable_t *variables) {\n  switch (info.cluster) {\n    case ESP_ZB_ZCL_CLUSTER_ID_TIME:\n      ESP_LOGD(TAG, \"Recieved time information\");\n#ifdef USE_ZIGBEE_TIME\n      if (this->zt_ == nullptr) {\n        ESP_LOGD(TAG, \"No time component linked to update time!\");\n      } else {\n        this->zt_->recieve_timesync_response(variables);\n      }\n#else\n      ESP_LOGD(TAG, \"No zigbee time component included at build time!\");\n#endif\n      break;\n    default:\n      ESP_LOGD(TAG, \"Attribute data recieved (but not yet handled):\");\n      while (variables) {\n        ESP_LOGD(TAG, \"Read attribute response: status(%d), cluster(0x%x), attribute(0x%x), type(0x%x), value(%d)\",\n                 variables->status, info.cluster, variables->attribute.id, variables->attribute.data.type,\n                 variables->attribute.data.value ? *(uint8_t *) variables->attribute.data.value : 0);\n        variables = variables->next;\n      }\n  }\n}\n\nvoid ZigBeeComponent::create_default_cluster(uint8_t endpoint_id, esp_zb_ha_standard_devices_t device_id) {\n  esp_zb_cluster_list_t *cluster_list = esphome_zb_default_clusters_create(device_id);\n  this->endpoint_list_[endpoint_id] =\n      std::tuple<esp_zb_ha_standard_devices_t, esp_zb_cluster_list_t *>(device_id, cluster_list);\n  // Add basic cluster\n  this->add_cluster(endpoint_id, ESP_ZB_ZCL_CLUSTER_ID_BASIC, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);\n  // Add identify cluster if not already present\n  if (esp_zb_cluster_list_get_cluster(cluster_list, ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE) ==\n      nullptr) {\n    this->add_cluster(endpoint_id, ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);\n  }\n}\n\nvoid ZigBeeComponent::add_cluster(uint8_t endpoint_id, uint16_t cluster_id, uint8_t role) {\n  esp_zb_attribute_list_t *attr_list;\n  switch (cluster_id) {\n    case 0:\n      attr_list = create_basic_cluster_();\n      break;\n    default:\n      attr_list = esphome_zb_default_attr_list_create(cluster_id);\n  }\n  this->attribute_list_[{endpoint_id, cluster_id, role}] = attr_list;\n}\n\nvoid ZigBeeComponent::set_basic_cluster(std::string model, std::string manufacturer, std::string date, uint8_t power,\n                                        uint8_t app_version, uint8_t stack_version, uint8_t hw_version,\n                                        std::string area, uint8_t physical_env) {\n  this->basic_cluster_data_ = {\n      .model = model,\n      .manufacturer = manufacturer,\n      .date = date,\n      .power = power,\n      .app_version = app_version,\n      .stack_version = stack_version,\n      .hw_version = hw_version,\n      .area = area,\n      .physical_env = physical_env,\n  };\n}\n\nesp_zb_attribute_list_t *ZigBeeComponent::create_basic_cluster_() {\n  // ------------------------------ Cluster BASIC ------------------------------\n  esp_zb_basic_cluster_cfg_t basic_cluster_cfg = {\n      .zcl_version = ESP_ZB_ZCL_BASIC_ZCL_VERSION_DEFAULT_VALUE,\n      .power_source = this->basic_cluster_data_.power,\n  };\n  ESP_LOGD(TAG, \"Model: %s\", this->basic_cluster_data_.model.c_str());\n  ESP_LOGD(TAG, \"Manufacturer: %s\", this->basic_cluster_data_.manufacturer.c_str());\n  ESP_LOGD(TAG, \"Date: %s\", this->basic_cluster_data_.date.c_str());\n  ESP_LOGD(TAG, \"Area: %s\", this->basic_cluster_data_.area.c_str());\n  uint8_t *ManufacturerName = get_zcl_string(this->basic_cluster_data_.manufacturer.c_str(),\n                                             32);  // warning: this is in format {length, 'string'} :\n  uint8_t *ModelIdentifier = get_zcl_string(this->basic_cluster_data_.model.c_str(), 32);\n  uint8_t *DateCode = get_zcl_string(this->basic_cluster_data_.date.c_str(), 16);\n  uint8_t *Location = get_zcl_string(this->basic_cluster_data_.area.c_str(), 16);\n  esp_zb_attribute_list_t *attr_list = esp_zb_basic_cluster_create(&basic_cluster_cfg);\n  esp_zb_basic_cluster_add_attr(attr_list, ESP_ZB_ZCL_ATTR_BASIC_APPLICATION_VERSION_ID,\n                                &(this->basic_cluster_data_.app_version));\n  esp_zb_basic_cluster_add_attr(attr_list, ESP_ZB_ZCL_ATTR_BASIC_STACK_VERSION_ID,\n                                &(this->basic_cluster_data_.stack_version));\n  esp_zb_basic_cluster_add_attr(attr_list, ESP_ZB_ZCL_ATTR_BASIC_HW_VERSION_ID,\n                                &(this->basic_cluster_data_.hw_version));\n  esp_zb_basic_cluster_add_attr(attr_list, ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID, ManufacturerName);\n  esp_zb_basic_cluster_add_attr(attr_list, ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, ModelIdentifier);\n  esp_zb_basic_cluster_add_attr(attr_list, ESP_ZB_ZCL_ATTR_BASIC_DATE_CODE_ID, DateCode);\n  esp_zb_basic_cluster_add_attr(attr_list, ESP_ZB_ZCL_ATTR_BASIC_LOCATION_DESCRIPTION_ID, Location);\n  esp_zb_basic_cluster_add_attr(attr_list, ESP_ZB_ZCL_ATTR_BASIC_PHYSICAL_ENVIRONMENT_ID,\n                                &(this->basic_cluster_data_.physical_env));\n  delete[] ManufacturerName;\n  delete[] ModelIdentifier;\n  delete[] DateCode;\n  delete[] Location;\n  return attr_list;\n}\n\nesp_err_t ZigBeeComponent::create_endpoint(uint8_t endpoint_id, esp_zb_ha_standard_devices_t device_id,\n                                           esp_zb_cluster_list_t *esp_zb_cluster_list) {\n  // ------------------------------ Create endpoint list ------------------------------\n  esp_zb_endpoint_config_t endpoint_config = {.endpoint = endpoint_id,\n                                              .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID,\n                                              .app_device_id = device_id,\n                                              .app_device_version = this->device_version_};\n  return esp_zb_ep_list_add_ep(this->esp_zb_ep_list_, esp_zb_cluster_list, endpoint_config);\n}\n\nstatic void esp_zb_task_(void *pvParameters) {\n  if (esp_zb_start(false) != ESP_OK) {\n    ESP_LOGE(TAG, \"Could not setup Zigbee\");\n    // this->mark_failed();\n    vTaskDelete(NULL);\n  }\n\n  if ((global_zigbee->device_role_ == ESP_ZB_DEVICE_TYPE_ED) && (global_zigbee->basic_cluster_data_.power == 0x03)) {\n    ESP_LOGD(TAG, \"Battery powered!\");\n    // zb_set_ed_node_descriptor(0, 1, 1);  // workaround, rx_on_when_idle should be 0 for battery powered devices.\n    esp_zb_set_node_descriptor_power_source(0);\n  } else {\n    esp_zb_set_node_descriptor_power_source(1);\n  }\n  esp_zb_stack_main_loop();\n}\n\nvoid ZigBeeComponent::setup() {\n  global_zigbee = this;\n  esp_zb_platform_config_t config = {\n      .radio_config = ESP_ZB_DEFAULT_RADIO_CONFIG(),\n      .host_config = ESP_ZB_DEFAULT_HOST_CONFIG(),\n  };\n#ifdef CONFIG_WIFI_COEX\n  if (esp_coex_wifi_i154_enable() != ESP_OK) {\n    this->mark_failed();\n    return;\n  }\n#endif\n  // ESP_ERROR_CHECK(nvs_flash_init()); not needed, called by esp32 component\n  if (esp_zb_platform_config(&config) != ESP_OK) {\n    this->mark_failed();\n    return;\n  }\n\n  /* initialize Zigbee stack */\n  esp_zb_zed_cfg_t zb_zed_cfg = {\n      .ed_timeout = ED_AGING_TIMEOUT,\n      .keep_alive = ED_KEEP_ALIVE,\n  };\n  esp_zb_zczr_cfg_t zb_zczr_cfg = {\n      .max_children = MAX_CHILDREN,\n  };\n  esp_zb_cfg_t zb_nwk_cfg = {\n      .esp_zb_role = this->device_role_,\n      .install_code_policy = INSTALLCODE_POLICY_ENABLE,\n  };\n#ifdef ZB_ROUTER_ROLE\n  zb_nwk_cfg.nwk_cfg.zczr_cfg = zb_zczr_cfg;\n#else\n  zb_nwk_cfg.nwk_cfg.zed_cfg = zb_zed_cfg;\n#endif\n  esp_zb_init(&zb_nwk_cfg);\n\n  if (this->custom_trust_center_key_) {\n    esp_zb_enable_joining_to_distributed(true);\n    esp_zb_secur_TC_standard_distributed_key_set(this->trustkey_);\n  }\n\n  esp_err_t ret;\n\n  // clusters\n  for (auto const &[key, val] : this->attribute_list_) {\n    esp_zb_cluster_list_t *esp_zb_cluster_list = std::get<1>(this->endpoint_list_[std::get<0>(key)]);\n    ret = esphome_zb_cluster_list_add_or_update_cluster(std::get<1>(key), esp_zb_cluster_list, val, std::get<2>(key));\n    if (ret != ESP_OK) {\n      ESP_LOGE(TAG, \"Could not create cluster 0x%04X with role %u: %s\", std::get<1>(key), std::get<2>(key),\n               esp_err_to_name(ret));\n    }\n  }\n  this->attribute_list_.clear();\n\n  // endpoints\n  for (auto const &[ep_id, dev] : this->endpoint_list_) {\n    // create_default_cluster(key, val);\n    if (create_endpoint(ep_id, std::get<0>(dev), std::get<1>(dev)) != ESP_OK) {\n      ESP_LOGE(TAG, \"Could not create endpoint %u\", ep_id);\n    }\n  }\n\n  // ------------------------------ Register Device ------------------------------\n  if (esp_zb_device_register(this->esp_zb_ep_list_) != ESP_OK) {\n    ESP_LOGE(TAG, \"Could not register the endpoint list\");\n    this->mark_failed();\n    return;\n  }\n\n  esp_zb_core_action_handler_register(zb_action_handler);\n\n  if (esp_zb_set_primary_network_channel_set(ESP_ZB_PRIMARY_CHANNEL_MASK) != ESP_OK) {\n    ESP_LOGE(TAG, \"Could not setup Zigbee\");\n    this->mark_failed();\n    return;\n  }\n\n  // reporting\n  for (auto &[_, attribute] : this->attributes_) {\n    if (attribute->report_enabled) {\n      esp_zb_zcl_reporting_info_t reporting_info = attribute->get_reporting_info();\n      ESP_LOGD(TAG, \"set reporting for cluster: %u\", reporting_info.cluster_id);\n      if (esp_zb_zcl_update_reporting_info(&reporting_info) != ESP_OK) {\n        ESP_LOGE(TAG, \"Could not configure reporting for attribute 0x%04X in cluster 0x%04X in endpoint %u\",\n                 reporting_info.attr_id, reporting_info.cluster_id, reporting_info.ep);\n      }\n    }\n  }\n  xTaskCreate(esp_zb_task_, \"Zigbee_main\", 4096, NULL, 24, NULL);\n  this->disable_loop();  // loop is only needed for processing events, so disable until we join a network\n}\n\nvoid ZigBeeComponent::loop() {\n  // Process all pending events\n  ZBEvent *event = this->zb_events_.pop();\n  while (event != nullptr) {\n    // Handle the event\n    switch (event->callback_id_) {\n      case ESP_ZB_CORE_SET_ATTR_VALUE_CB_ID:\n        this->handle_attribute(\n            event->event_.set_attr.info, event->event_.set_attr.attribute,\n            event->event_.set_attr.has_current_level ? &event->event_.set_attr.current_level : nullptr);\n        break;\n      case ESP_ZB_CORE_CMD_READ_ATTR_RESP_CB_ID:\n        this->handle_read_attribute_response(event->event_.read_attr_resp.info,\n                                             &(event->event_.read_attr_resp.variables));\n        break;\n      case ESP_ZB_CORE_REPORT_ATTR_CB_ID:\n        this->handle_report_attribute(event->event_.report_attr.dst_endpoint, event->event_.report_attr.cluster,\n                                      event->event_.report_attr.attribute, event->event_.report_attr.src_address,\n                                      event->event_.report_attr.src_endpoint);\n\n        break;\n    }\n\n    // Free the event back to the pool\n    this->zb_event_pool_.release(event);\n    // Get the next event\n    event = this->zb_events_.pop();\n  }\n  // Log dropped events periodically\n  uint16_t dropped = this->zb_events_.get_and_reset_dropped_count();\n  if (dropped > 0) {\n    ESP_LOGW(TAG, \"Dropped %u Zigbee events due to buffer overflow\", dropped);\n  }\n\n  if (this->joined_) {\n    this->on_join_callback_.call();\n    this->joined_ = false;  // only call once\n    this->connected_ = true;\n  } else if (this->connected_) {\n    this->disable_loop();  // only disable once connected\n  }\n}\n\nvoid ZigBeeComponent::dump_config() {\n  char trustkey_hex[format_hex_pretty_size(sizeof(this->trustkey_))];\n  ESP_LOGCONFIG(TAG, \"ZigBee:\");\n  ESP_LOGCONFIG(TAG, \"  Device Version: %u\", this->device_version_);\n  if (this->custom_trust_center_key_) {\n    ESP_LOGCONFIG(TAG, \"  Custom Trust Center Key: %s\",\n                  format_hex_pretty_to(trustkey_hex, this->trustkey_, sizeof(this->trustkey_), '.'));\n  }\n  for (auto const &[key, val] : this->endpoint_list_) {\n    ESP_LOGCONFIG(TAG, \"  Endpoint: %u, %d\", key, std::get<0>(val));\n  }\n}\n\nvoid ZigBeeComponent::set_trust_center_key(const char *trust_center_key) {\n  parse_hex(trust_center_key, this->trustkey_, sizeof(this->trustkey_));\n  this->custom_trust_center_key_ = true;\n}\n\n}  // namespace zigbee\n}  // namespace esphome\n"
  },
  {
    "path": "components/zigbee/zigbee.h",
    "content": "#pragma once\n\n#include <map>\n#include <tuple>\n\n#include \"esphome/core/defines.h\"\n#include \"esphome/core/automation.h\"\n#include \"esphome/core/component.h\"\n#include \"esphome/core/log.h\"\n#include \"esphome/core/lock_free_queue.h\"\n#include \"esphome/core/event_pool.h\"\n\n#include \"esp_zb_event.h\"\n\n#include \"esp_zigbee_core.h\"\n#include \"zboss_api.h\"\n#include \"ha/esp_zigbee_ha_standard.h\"\n#include \"zigbee_helpers.h\"\n\n#ifdef USE_ZIGBEE_TIME\n#include \"time/zigbee_time.h\"\n#endif\n\nnamespace esphome {\nnamespace zigbee {\n\nstatic const char *const TAG = \"zigbee\";\nstatic constexpr uint8_t MAX_ZB_QUEUE_SIZE = 32;\n\nusing device_params_t = struct DeviceParamsS {\n  esp_zb_ieee_addr_t ieee_addr;\n  uint8_t endpoint;\n  uint16_t short_addr;\n};\n\nusing zdo_info_user_ctx_t = struct ZdoInfoCtxS {\n  uint8_t endpoint;\n  uint16_t short_addr;\n};\n\nusing zb_device_params_t = struct zb_device_params_s {\n  esp_zb_ieee_addr_t ieee_addr;\n  uint8_t endpoint;\n  uint16_t short_addr;\n};\n\n/* Zigbee configuration */\n#define INSTALLCODE_POLICY_ENABLE false /* enable the install code policy for security */\n#define ED_AGING_TIMEOUT ESP_ZB_ED_AGING_TIMEOUT_64MIN\n#define ED_KEEP_ALIVE 3000 /* 3000 millisecond */\n#define MAX_CHILDREN 10\n#define ESP_ZB_PRIMARY_CHANNEL_MASK \\\n  ESP_ZB_TRANSCEIVER_ALL_CHANNELS_MASK /* Zigbee primary channel mask use in the example */\n\n#define ESP_ZB_DEFAULT_RADIO_CONFIG() \\\n  { .radio_mode = ZB_RADIO_MODE_NATIVE, }\n\n#define ESP_ZB_DEFAULT_HOST_CONFIG() \\\n  { .host_connection_mode = ZB_HOST_CONNECTION_MODE_NONE, }\n\ntemplate<class T> T get_value_by_type(uint8_t attr_type, void *data);\nuint8_t *get_zcl_string(const char *str, uint8_t max_size, bool use_max_size = false);\n\nclass ZigBeeAttribute;\nclass ZigbeeTime;\n\nclass ZigBeeComponent : public Component {\n public:\n  void setup() override;\n  void loop() override;\n  void dump_config() override;\n  esp_err_t create_endpoint(uint8_t endpoint_id, esp_zb_ha_standard_devices_t device_id,\n                            esp_zb_cluster_list_t *esp_zb_cluster_list);\n  void set_basic_cluster(std::string model, std::string manufacturer, std::string date, uint8_t power,\n                         uint8_t app_version, uint8_t stack_version, uint8_t hw_version, std::string area,\n                         uint8_t physical_env);\n  void set_trust_center_key(const char *trust_center_key);\n  void set_device_version(uint8_t version) { this->device_version_ = version; }\n  void add_cluster(uint8_t endpoint_id, uint16_t cluster_id, uint8_t role);\n  void create_default_cluster(uint8_t endpoint_id, esp_zb_ha_standard_devices_t device_id);\n\n  template<typename T>\n  void add_attr(ZigBeeAttribute *attr, uint8_t endpoint_id, uint16_t cluster_id, uint8_t role, uint16_t attr_id,\n                uint8_t attr_type, uint8_t attr_access, uint8_t max_size, T value);\n\n  template<typename T>\n  void add_attr(uint8_t endpoint_id, uint16_t cluster_id, uint8_t role, uint16_t attr_id, uint8_t attr_type,\n                uint8_t attr_access, uint8_t max_size, T value);\n\n  void handle_attribute(esp_zb_device_cb_common_info_t info, esp_zb_zcl_attribute_t attribute, uint8_t *current_level);\n  void handle_report_attribute(uint8_t dst_endpoint, uint16_t cluster, esp_zb_zcl_attribute_t attribute,\n                               esp_zb_zcl_addr_t src_address, uint8_t src_endpoint);\n  void handle_read_attribute_response(esp_zb_zcl_cmd_info_t info, esp_zb_zcl_read_attr_resp_variable_t *variables);\n  void searchBindings();\n  static void bindingTableCb(const esp_zb_zdo_binding_table_info_t *table_info, void *user_ctx);\n\n  void reset() {\n    esp_zb_lock_acquire(portMAX_DELAY);\n    esp_zb_factory_reset();\n    esp_zb_lock_release();\n  }\n  void report();\n\n#ifdef USE_ZIGBEE_TIME\n  ZigbeeTime *zt_{nullptr};\n#endif\n\n  template<typename F> void add_on_join_callback(F &&callback) {\n    this->on_join_callback_.add(std::forward<F>(callback));\n  }\n\n  bool is_started() { return this->started_; }\n  bool is_connected() { return this->connected_; }\n  bool connected_ = false;\n  bool started_ = false;\n  bool joined_ = false;\n\n  CallbackManager<void()> on_join_callback_{};\n  struct {\n    std::string model;\n    std::string manufacturer;\n    std::string date;\n    uint8_t power;\n    uint8_t app_version;\n    uint8_t stack_version;\n    uint8_t hw_version;\n    std::string area;\n    uint8_t physical_env;\n  } basic_cluster_data_;\n#ifdef ZB_ED_ROLE\n  esp_zb_nwk_device_type_t device_role_ = ESP_ZB_DEVICE_TYPE_ED;\n#else\n  esp_zb_nwk_device_type_t device_role_ = ESP_ZB_DEVICE_TYPE_ROUTER;\n#endif\n\n protected:\n  template<typename... Args> friend void enqueue_zb_event(Args... args);\n  esphome::LockFreeQueue<ZBEvent, MAX_ZB_QUEUE_SIZE> zb_events_;\n  esphome::EventPool<ZBEvent, MAX_ZB_QUEUE_SIZE> zb_event_pool_;\n  esp_zb_attribute_list_t *create_basic_cluster_();\n  template<typename T>\n  void add_attr_(ZigBeeAttribute *attr, uint8_t endpoint_id, uint16_t cluster_id, uint8_t role, uint16_t attr_id,\n                 uint8_t attr_type, uint8_t attr_access, T *value_p);\n  std::map<uint8_t, std::tuple<esp_zb_ha_standard_devices_t, esp_zb_cluster_list_t *>> endpoint_list_;\n  std::map<std::tuple<uint8_t, uint16_t, uint8_t>, esp_zb_attribute_list_t *> attribute_list_;\n  std::map<std::tuple<uint8_t, uint16_t, uint8_t, uint16_t>, ZigBeeAttribute *> attributes_;\n  esp_zb_ep_list_t *esp_zb_ep_list_ = esp_zb_ep_list_create();\n  uint8_t ident_time_;\n  bool custom_trust_center_key_ = false;\n  uint8_t trustkey_[16] = {0};\n  uint8_t device_version_ = 0;\n};\n\nextern \"C\" void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct);\nextern \"C\" void zb_set_ed_node_descriptor(bool power_src, bool rx_on_when_idle, bool alloc_addr);\n\ntemplate<typename T>\nvoid ZigBeeComponent::add_attr(uint8_t endpoint_id, uint16_t cluster_id, uint8_t role, uint16_t attr_id,\n                               uint8_t attr_type, uint8_t attr_access, uint8_t max_size, T value) {\n  this->add_attr<T>(nullptr, endpoint_id, cluster_id, role, attr_id, attr_type, attr_access, max_size, value);\n}\n\ntemplate<typename T>\nvoid ZigBeeComponent::add_attr(ZigBeeAttribute *attr, uint8_t endpoint_id, uint16_t cluster_id, uint8_t role,\n                               uint16_t attr_id, uint8_t attr_type, uint8_t attr_access, uint8_t max_size, T value) {\n  // The size byte of the zcl_str must be set to the maximum value,\n  // even though the initial string may be shorter.\n  if constexpr (std::is_same<T, std::string>::value) {\n    auto zcl_str = get_zcl_string(value.c_str(), max_size, true);\n    add_attr_(attr, endpoint_id, cluster_id, role, attr_id, attr_type, attr_access, zcl_str);\n    delete[] zcl_str;\n  } else if constexpr (std::is_convertible<T, const char *>::value) {\n    auto zcl_str = get_zcl_string(value, max_size, true);\n    add_attr_(attr, endpoint_id, cluster_id, role, attr_id, attr_type, attr_access, zcl_str);\n    delete[] zcl_str;\n  } else {\n    add_attr_(attr, endpoint_id, cluster_id, role, attr_id, attr_type, attr_access, &value);\n  }\n}\n\ntemplate<typename T>\nvoid ZigBeeComponent::add_attr_(ZigBeeAttribute *attr, uint8_t endpoint_id, uint16_t cluster_id, uint8_t role,\n                                uint16_t attr_id, uint8_t attr_type, uint8_t attr_access, T *value_p) {\n  esp_zb_attribute_list_t *attr_list = this->attribute_list_[{endpoint_id, cluster_id, role}];\n  esp_err_t ret =\n      esphome_zb_cluster_add_or_update_attr(cluster_id, attr_list, attr_id, attr_type, attr_access, value_p);\n  if (ret != ESP_OK) {\n    ESP_LOGE(TAG, \"Could not add attribute 0x%04X to cluster 0x%04X in endpoint %u: %s\", attr_id, cluster_id,\n             endpoint_id, esp_err_to_name(ret));\n  }\n  if (attr != nullptr) {\n    this->attributes_[{endpoint_id, cluster_id, role, attr_id}] = attr;\n  }\n}\n\n}  // namespace zigbee\n}  // namespace esphome\n"
  },
  {
    "path": "components/zigbee/zigbee_attribute.cpp",
    "content": "#include \"zigbee_attribute.h\"\n\nnamespace esphome {\nnamespace zigbee {\n\nvoid ZigBeeAttribute::set_attr_() {\n  if (!this->zb_->is_connected()) {\n    return;\n  }\n  if (esp_zb_lock_acquire(20 / portTICK_PERIOD_MS)) {\n    esp_zb_zcl_status_t state = esp_zb_zcl_set_attribute_val(this->endpoint_id_, this->cluster_id_, this->role_,\n                                                             this->attr_id_, this->value_p, false);\n    if (this->force_report_) {\n      this->report_(true);\n    }\n    this->set_attr_requested_ = false;\n    // Check for error\n    if (state != ESP_ZB_ZCL_STATUS_SUCCESS) {\n      ESP_LOGE(TAG, \"Setting attribute failed: %s\", esp_err_to_name(state));\n    }\n    ESP_LOGD(TAG, \"Attribute set!\");\n    esp_zb_lock_release();\n  }\n}\n\nvoid ZigBeeAttribute::report_() { this->report_(false); }\n\nvoid ZigBeeAttribute::report_(bool has_lock) {\n  if (!this->zb_->is_connected()) {\n    return;\n  }\n  if (has_lock or esp_zb_lock_acquire(20 / portTICK_PERIOD_MS)) {\n    esp_zb_zcl_report_attr_cmd_t cmd = {\n        .address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT,\n        .direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI,\n    };\n    cmd.zcl_basic_cmd.dst_addr_u.addr_short = 0x0000;\n    cmd.zcl_basic_cmd.dst_endpoint = 1;\n    cmd.zcl_basic_cmd.src_endpoint = this->endpoint_id_;\n\n    cmd.clusterID = this->cluster_id_;\n    cmd.attributeID = this->attr_id_;\n\n    // cmd.cluster_role = reporting_info.cluster_role;\n    esp_zb_zcl_report_attr_cmd_req(&cmd);\n    this->report_requested_ = false;\n    if (!has_lock) {\n      esp_zb_lock_release();\n    }\n  }\n}\n\nesp_zb_zcl_reporting_info_t ZigBeeAttribute::get_reporting_info() {\n  esp_zb_zcl_reporting_info_t reporting_info = {\n      .direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_SRV,\n      .ep = this->endpoint_id_,\n      .cluster_id = this->cluster_id_,\n      .cluster_role = this->role_,\n      .attr_id = this->attr_id_,\n      .manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC,\n  };\n  reporting_info.dst.profile_id = ESP_ZB_AF_HA_PROFILE_ID;\n  reporting_info.u.send_info.min_interval = 10;     /*!< Actual minimum reporting interval */\n  reporting_info.u.send_info.max_interval = 0;      /*!< Actual maximum reporting interval */\n  reporting_info.u.send_info.def_min_interval = 10; /*!< Default minimum reporting interval */\n  reporting_info.u.send_info.def_max_interval = 0;  /*!< Default maximum reporting interval */\n  reporting_info.u.send_info.delta.s16 = 0;         /*!< Actual reportable change */\n\n  return reporting_info;\n}\n\nvoid ZigBeeAttribute::set_report(bool force) {\n  this->report_enabled = true;\n  this->force_report_ = force;\n}\n\nvoid ZigBeeAttribute::report() {\n  this->report_requested_ = true;\n  this->enable_loop();\n}\n\nvoid ZigBeeAttribute::loop() {\n  if (this->set_attr_requested_) {\n    this->set_attr_();\n  }\n\n  if (this->report_requested_) {\n    this->report_();\n  }\n\n  if (!this->report_requested_ && !this->set_attr_requested_) {\n    this->disable_loop();\n  }\n}\n\n}  // namespace zigbee\n}  // namespace esphome\n"
  },
  {
    "path": "components/zigbee/zigbee_attribute.h",
    "content": "#pragma once\n\n#include <type_traits>\n\n#include \"zigbee.h\"\n#include \"esp_zigbee_core.h\"\n#include \"esphome/core/automation.h\"\n#include \"esphome/core/component.h\"\n#include \"esphome/core/defines.h\"\n#ifdef USE_SENSOR\n#include \"esphome/components/sensor/sensor.h\"\n#endif\n#ifdef USE_BINARY_SENSOR\n#include \"esphome/components/binary_sensor/binary_sensor.h\"\n#endif\n#ifdef USE_TEXT_SENSOR\n#include \"esphome/components/text_sensor/text_sensor.h\"\n#endif\n#ifdef USE_SWITCH\n#include \"esphome/components/switch/switch.h\"\n#endif\n#ifdef USE_LIGHT\n#include \"esphome/components/light/light_state.h\"\n#endif\n\nnamespace esphome {\nnamespace zigbee {\n\n#ifdef USE_LIGHT\nvoid set_light_color(uint8_t ep, light::LightCall *call, uint16_t value, bool is_x);\n#endif\n\nclass ZigBeeAttribute : public Component {\n public:\n  ZigBeeAttribute(ZigBeeComponent *parent, uint8_t endpoint_id, uint16_t cluster_id, uint8_t role, uint16_t attr_id,\n                  uint8_t attr_type, float scale)\n      : zb_(parent),\n        endpoint_id_(endpoint_id),\n        cluster_id_(cluster_id),\n        role_(role),\n        attr_id_(attr_id),\n        attr_type_(attr_type),\n        scale_(scale) {}\n  // void dump_config() override;\n  void loop() override;\n\n  template<typename T> void add_attr(uint8_t attr_access, uint8_t max_size, T value);\n  esp_zb_zcl_reporting_info_t get_reporting_info();\n  void set_report(bool force);\n  void report();\n  template<typename T> void set_attr(const T &value);\n\n  uint8_t attr_type() { return attr_type_; }\n\n  template<typename F> void add_on_value_callback(F &&callback) { on_value_callback_.add(std::forward<F>(callback)); }\n  void on_value(esp_zb_zcl_attribute_t attribute) { this->on_value_callback_.call(attribute); }\n\n  void add_on_report_callback(\n      std::function<void(esp_zb_zcl_attribute_t attribute, esp_zb_zcl_addr_t src_address, uint8_t src_endpoint)>\n          callback) {\n    on_report_callback_.add(std::move(callback));\n  }\n  void on_report(esp_zb_zcl_attribute_t attribute, esp_zb_zcl_addr_t src_address, uint8_t src_endpoint) {\n    this->on_report_callback_.call(attribute, src_address, src_endpoint);\n  }\n  bool report_enabled = false;\n\n#ifdef USE_SENSOR\n  template<typename T> void connect(sensor::Sensor *sensor);\n  template<typename T> void connect(sensor::Sensor *sensor, std::function<T(float)> &&f);\n#endif\n#ifdef USE_BINARY_SENSOR\n  template<typename T> void connect(binary_sensor::BinarySensor *sensor);\n  template<typename T> void connect(binary_sensor::BinarySensor *sensor, std::function<T(bool)> &&f);\n#endif\n#ifdef USE_TEXT_SENSOR\n  template<typename T> void connect(text_sensor::TextSensor *sensor);\n  template<typename T> void connect(text_sensor::TextSensor *sensor, std::function<T(std::string)> &&f);\n#endif\n#ifdef USE_SWITCH\n  template<typename T> void connect(switch_::Switch *device);\n  template<typename T> void connect(switch_::Switch *device, std::function<bool(T)> &&f);\n#endif\n#ifdef USE_LIGHT\n  template<typename T> void connect(light::LightState *device);\n#endif\n\n protected:\n  void set_attr_();\n  void report_();\n  void report_(bool has_lock);\n  ZigBeeComponent *zb_;\n  uint8_t endpoint_id_;\n  uint16_t cluster_id_;\n  uint8_t role_;\n  uint16_t attr_id_;\n  uint8_t attr_type_;\n  uint8_t max_size_;\n  float scale_;\n  CallbackManager<void(esp_zb_zcl_attribute_t attribute)> on_value_callback_{};\n  CallbackManager<void(esp_zb_zcl_attribute_t attribute, esp_zb_zcl_addr_t src_address, uint8_t src_endpoint)>\n      on_report_callback_{};\n  void *value_p{nullptr};\n  bool set_attr_requested_{false};\n  bool report_requested_{false};\n  bool force_report_{false};\n};\n\ntemplate<typename T> void ZigBeeAttribute::add_attr(uint8_t attr_access, uint8_t max_size, T value) {\n  this->max_size_ = max_size;\n  this->zb_->add_attr(this, this->endpoint_id_, this->cluster_id_, this->role_, this->attr_id_, this->attr_type_,\n                      attr_access, max_size, std::move(value));\n}\n\ntemplate<typename T> void ZigBeeAttribute::set_attr(const T &value) {\n  if constexpr (std::is_convertible<T, const char *>::value) {\n    auto zcl_str = get_zcl_string(value, this->max_size_);\n\n    if (this->value_p != nullptr) {\n      delete[](char *) this->value_p;\n    }\n    this->value_p = (void *) zcl_str;\n  } else if constexpr (std::is_same<T, std::string>::value) {\n    auto zcl_str = get_zcl_string(value.c_str(), this->max_size_);\n\n    if (this->value_p != nullptr) {\n      delete[](char *) this->value_p;\n    }\n    this->value_p = (void *) zcl_str;\n  } else {\n    if (this->value_p != nullptr) {\n      delete (T *) this->value_p;\n    }\n    T *value_p = new T;\n    *value_p = value;\n    this->value_p = (void *) value_p;\n  }\n  this->set_attr_requested_ = true;\n  this->enable_loop();\n}\n\n#ifdef USE_SENSOR\ntemplate<typename T> void ZigBeeAttribute::connect(sensor::Sensor *sensor) {\n  sensor->add_on_state_callback([=, this](float value) { this->set_attr((T) (this->scale_ * value)); });\n}\n\ntemplate<typename T> void ZigBeeAttribute::connect(sensor::Sensor *sensor, std::function<T(float)> &&f) {\n  sensor->add_on_state_callback([=, this](float value) { this->set_attr(f(value)); });\n}\n#endif\n\n#ifdef USE_BINARY_SENSOR\ntemplate<typename T> void ZigBeeAttribute::connect(binary_sensor::BinarySensor *sensor) {\n  sensor->add_on_state_callback([=, this](bool value) { this->set_attr((T) (this->scale_ * value)); });\n}\n\ntemplate<typename T> void ZigBeeAttribute::connect(binary_sensor::BinarySensor *sensor, std::function<T(bool)> &&f) {\n  sensor->add_on_state_callback([=, this](bool value) { this->set_attr(f(value)); });\n}\n#endif\n\n#ifdef USE_TEXT_SENSOR\ntemplate<typename T> void ZigBeeAttribute::connect(text_sensor::TextSensor *sensor) {\n  sensor->add_on_state_callback([=, this](std::string value) { this->set_attr((T) (value)); });\n}\n\ntemplate<typename T> void ZigBeeAttribute::connect(text_sensor::TextSensor *sensor, std::function<T(std::string)> &&f) {\n  sensor->add_on_state_callback([=, this](std::string value) { this->set_attr(f(value)); });\n}\n#endif\n\n#ifdef USE_SWITCH\ntemplate<typename T> void ZigBeeAttribute::connect(switch_::Switch *device) {\n  this->add_on_value_callback([=, this](esp_zb_zcl_attribute_t attribute) {\n    if (attribute.data.type == this->attr_type() && attribute.data.value) {\n      if (get_value_by_type<T>(this->attr_type(), attribute.data.value)) {\n        device->turn_on();\n      } else {\n        device->turn_off();\n      }\n    }\n  });\n  device->add_on_state_callback([=, this](bool value) { this->set_attr((T) (this->scale_ * value)); });\n}\n\ntemplate<typename T> void ZigBeeAttribute::connect(switch_::Switch *device, std::function<bool(T)> &&f) {\n  this->add_on_value_callback([=, this](esp_zb_zcl_attribute_t attribute) {\n    if (attribute.data.type == this->attr_type() && attribute.data.value) {\n      if (f(get_value_by_type<T>(this->attr_type(), attribute.data.value))) {\n        device->turn_on();\n      } else {\n        device->turn_off();\n      }\n    }\n  });\n}\n#endif\n\n#ifdef USE_LIGHT\ntemplate<typename T> void ZigBeeAttribute::connect(light::LightState *device) {\n  this->add_on_value_callback([=, this](esp_zb_zcl_attribute_t attribute) {\n    if (attribute.data.type == this->attr_type() && attribute.data.value) {\n      light::LightCall call = device->make_call();\n      ESP_LOGD(TAG, \"Make light call\");\n      if (std::is_same<T, bool>::value) {\n        call.set_state(get_value_by_type<T>(this->attr_type(), attribute.data.value));\n        ESP_LOGD(TAG, \"Set state\");\n      } else if (this->cluster_id_ == 0x0300 && this->attr_id_ == 0x3) {\n        // set X\n        set_light_color(this->endpoint_id_, &call, get_value_by_type<uint16_t>(this->attr_type(), attribute.data.value),\n                        true);\n        ESP_LOGD(TAG, \"Set X\");\n      } else if (this->cluster_id_ == 0x0300 && this->attr_id_ == 0x4) {\n        // set Y\n        set_light_color(this->endpoint_id_, &call, get_value_by_type<uint16_t>(this->attr_type(), attribute.data.value),\n                        false);\n        ESP_LOGD(TAG, \"Set Y\");\n      } else if (this->cluster_id_ != 0x0300 and std::numeric_limits<T>::is_integer) {\n        call.set_brightness((float) get_value_by_type<T>(this->attr_type(), attribute.data.value) /\n                            255);  // integer level between 0 and 255\n        ESP_LOGD(TAG, \"Set level: %f\", (float) get_value_by_type<T>(this->attr_type(), attribute.data.value) / 255);\n        //} else if (this->cluster_id_ != 0x0300 and std::is_floating_point<T>) {\n        //  call.set_brightness(get_value_by_type<T>(this->attr_type(), attribute.data.value));  //float level between 0\n        //  and 1\n      }\n      call.perform();\n    }\n  });\n}\n#endif\n\n}  // namespace zigbee\n}  // namespace esphome\n"
  },
  {
    "path": "components/zigbee/zigbee_const.py",
    "content": "import esphome.codegen as cg\n\nha_standard_devices = cg.esphome_ns.enum(\"esp_zb_ha_standard_devices_t\")\nDEVICE_ID = {\n    \"ON_OFF_SWITCH\": ha_standard_devices.ESP_ZB_HA_ON_OFF_SWITCH_DEVICE_ID,\n    0x0000: ha_standard_devices.ESP_ZB_HA_ON_OFF_SWITCH_DEVICE_ID,\n    \"LEVEL_CONTROL_SWITCH\": ha_standard_devices.ESP_ZB_HA_LEVEL_CONTROL_SWITCH_DEVICE_ID,\n    0x0001: ha_standard_devices.ESP_ZB_HA_LEVEL_CONTROL_SWITCH_DEVICE_ID,\n    \"ON_OFF_OUTPUT\": ha_standard_devices.ESP_ZB_HA_ON_OFF_OUTPUT_DEVICE_ID,\n    0x0002: ha_standard_devices.ESP_ZB_HA_ON_OFF_OUTPUT_DEVICE_ID,\n    \"LEVEL_CONTROLLABLE_OUTPUT\": ha_standard_devices.ESP_ZB_HA_LEVEL_CONTROLLABLE_OUTPUT_DEVICE_ID,\n    0x0003: ha_standard_devices.ESP_ZB_HA_LEVEL_CONTROLLABLE_OUTPUT_DEVICE_ID,\n    \"SCENE_SELECTOR\": ha_standard_devices.ESP_ZB_HA_SCENE_SELECTOR_DEVICE_ID,\n    0x0004: ha_standard_devices.ESP_ZB_HA_SCENE_SELECTOR_DEVICE_ID,\n    \"CONFIGURATION_TOOL\": ha_standard_devices.ESP_ZB_HA_CONFIGURATION_TOOL_DEVICE_ID,\n    0x0005: ha_standard_devices.ESP_ZB_HA_CONFIGURATION_TOOL_DEVICE_ID,\n    \"REMOTE_CONTROL\": ha_standard_devices.ESP_ZB_HA_REMOTE_CONTROL_DEVICE_ID,\n    0x0006: ha_standard_devices.ESP_ZB_HA_REMOTE_CONTROL_DEVICE_ID,\n    \"COMBINED_INTERFACE\": ha_standard_devices.ESP_ZB_HA_COMBINED_INTERFACE_DEVICE_ID,\n    0x0007: ha_standard_devices.ESP_ZB_HA_COMBINED_INTERFACE_DEVICE_ID,\n    \"RANGE_EXTENDER\": ha_standard_devices.ESP_ZB_HA_RANGE_EXTENDER_DEVICE_ID,\n    0x0008: ha_standard_devices.ESP_ZB_HA_RANGE_EXTENDER_DEVICE_ID,\n    \"MAINS_POWER_OUTLET\": ha_standard_devices.ESP_ZB_HA_MAINS_POWER_OUTLET_DEVICE_ID,\n    0x0009: ha_standard_devices.ESP_ZB_HA_MAINS_POWER_OUTLET_DEVICE_ID,\n    \"DOOR_LOCK\": ha_standard_devices.ESP_ZB_HA_DOOR_LOCK_DEVICE_ID,\n    0x000A: ha_standard_devices.ESP_ZB_HA_DOOR_LOCK_DEVICE_ID,\n    \"DOOR_LOCK_CONTROLLER\": ha_standard_devices.ESP_ZB_HA_DOOR_LOCK_CONTROLLER_DEVICE_ID,\n    0x000B: ha_standard_devices.ESP_ZB_HA_DOOR_LOCK_CONTROLLER_DEVICE_ID,\n    \"SIMPLE_SENSOR\": ha_standard_devices.ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID,\n    0x000C: ha_standard_devices.ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID,\n    \"CONSUMPTION_AWARENESS\": ha_standard_devices.ESP_ZB_HA_CONSUMPTION_AWARENESS_DEVICE_ID,\n    0x000D: ha_standard_devices.ESP_ZB_HA_CONSUMPTION_AWARENESS_DEVICE_ID,\n    \"HOME_GATEWAY\": ha_standard_devices.ESP_ZB_HA_HOME_GATEWAY_DEVICE_ID,\n    0x0050: ha_standard_devices.ESP_ZB_HA_HOME_GATEWAY_DEVICE_ID,\n    \"SMART_PLUG\": ha_standard_devices.ESP_ZB_HA_SMART_PLUG_DEVICE_ID,\n    0x0051: ha_standard_devices.ESP_ZB_HA_SMART_PLUG_DEVICE_ID,\n    \"WHITE_GOODS\": ha_standard_devices.ESP_ZB_HA_WHITE_GOODS_DEVICE_ID,\n    0x0052: ha_standard_devices.ESP_ZB_HA_WHITE_GOODS_DEVICE_ID,\n    \"METER_INTERFACE\": ha_standard_devices.ESP_ZB_HA_METER_INTERFACE_DEVICE_ID,\n    0x0053: ha_standard_devices.ESP_ZB_HA_METER_INTERFACE_DEVICE_ID,\n    \"ON_OFF_LIGHT\": ha_standard_devices.ESP_ZB_HA_ON_OFF_LIGHT_DEVICE_ID,\n    0x0100: ha_standard_devices.ESP_ZB_HA_ON_OFF_LIGHT_DEVICE_ID,\n    \"DIMMABLE_LIGHT\": ha_standard_devices.ESP_ZB_HA_DIMMABLE_LIGHT_DEVICE_ID,\n    0x0101: ha_standard_devices.ESP_ZB_HA_DIMMABLE_LIGHT_DEVICE_ID,\n    \"COLOR_DIMMABLE_LIGHT\": ha_standard_devices.ESP_ZB_HA_COLOR_DIMMABLE_LIGHT_DEVICE_ID,\n    0x0102: ha_standard_devices.ESP_ZB_HA_COLOR_DIMMABLE_LIGHT_DEVICE_ID,\n    \"DIMMER_SWITCH\": ha_standard_devices.ESP_ZB_HA_DIMMER_SWITCH_DEVICE_ID,\n    0x0104: ha_standard_devices.ESP_ZB_HA_DIMMER_SWITCH_DEVICE_ID,\n    \"COLOR_DIMMER_SWITCH\": ha_standard_devices.ESP_ZB_HA_COLOR_DIMMER_SWITCH_DEVICE_ID,\n    0x0105: ha_standard_devices.ESP_ZB_HA_COLOR_DIMMER_SWITCH_DEVICE_ID,\n    \"LIGHT_SENSOR\": ha_standard_devices.ESP_ZB_HA_LIGHT_SENSOR_DEVICE_ID,\n    0x0106: ha_standard_devices.ESP_ZB_HA_LIGHT_SENSOR_DEVICE_ID,\n    \"SHADE\": ha_standard_devices.ESP_ZB_HA_SHADE_DEVICE_ID,\n    0x0200: ha_standard_devices.ESP_ZB_HA_SHADE_DEVICE_ID,\n    \"SHADE_CONTROLLER\": ha_standard_devices.ESP_ZB_HA_SHADE_CONTROLLER_DEVICE_ID,\n    0x0201: ha_standard_devices.ESP_ZB_HA_SHADE_CONTROLLER_DEVICE_ID,\n    \"WINDOW_COVERING\": ha_standard_devices.ESP_ZB_HA_WINDOW_COVERING_DEVICE_ID,\n    0x0202: ha_standard_devices.ESP_ZB_HA_WINDOW_COVERING_DEVICE_ID,\n    \"WINDOW_COVERING_CONTROLLER\": ha_standard_devices.ESP_ZB_HA_WINDOW_COVERING_CONTROLLER_DEVICE_ID,\n    0x0203: ha_standard_devices.ESP_ZB_HA_WINDOW_COVERING_CONTROLLER_DEVICE_ID,\n    \"HEATING_COOLING_UNIT\": ha_standard_devices.ESP_ZB_HA_HEATING_COOLING_UNIT_DEVICE_ID,\n    0x0300: ha_standard_devices.ESP_ZB_HA_HEATING_COOLING_UNIT_DEVICE_ID,\n    \"THERMOSTAT\": ha_standard_devices.ESP_ZB_HA_THERMOSTAT_DEVICE_ID,\n    0x0301: ha_standard_devices.ESP_ZB_HA_THERMOSTAT_DEVICE_ID,\n    \"TEMPERATURE_SENSOR\": ha_standard_devices.ESP_ZB_HA_TEMPERATURE_SENSOR_DEVICE_ID,\n    0x0302: ha_standard_devices.ESP_ZB_HA_TEMPERATURE_SENSOR_DEVICE_ID,\n    \"IAS_CONTROL_INDICATING_EQUIPMENT_ID\": ha_standard_devices.ESP_ZB_HA_IAS_CONTROL_INDICATING_EQUIPMENT_ID,\n    0x0400: ha_standard_devices.ESP_ZB_HA_IAS_CONTROL_INDICATING_EQUIPMENT_ID,\n    \"IAS_ANCILLARY_CONTROL_EQUIPMENT_ID\": ha_standard_devices.ESP_ZB_HA_IAS_ANCILLARY_CONTROL_EQUIPMENT_ID,\n    0x0401: ha_standard_devices.ESP_ZB_HA_IAS_ANCILLARY_CONTROL_EQUIPMENT_ID,\n    \"IAS_ZONE_ID\": ha_standard_devices.ESP_ZB_HA_IAS_ZONE_ID,\n    0x0402: ha_standard_devices.ESP_ZB_HA_IAS_ZONE_ID,\n    \"IAS_WARNING\": ha_standard_devices.ESP_ZB_HA_IAS_WARNING_DEVICE_ID,\n    0x0403: ha_standard_devices.ESP_ZB_HA_IAS_WARNING_DEVICE_ID,\n    \"TEST\": ha_standard_devices.ESP_ZB_HA_TEST_DEVICE_ID,\n    0xFFF0: ha_standard_devices.ESP_ZB_HA_TEST_DEVICE_ID,\n    \"CUSTOM_TUNNEL\": ha_standard_devices.ESP_ZB_HA_CUSTOM_TUNNEL_DEVICE_ID,\n    0xFFF1: ha_standard_devices.ESP_ZB_HA_CUSTOM_TUNNEL_DEVICE_ID,\n    \"CUSTOM_ATTR\": ha_standard_devices.ESP_ZB_HA_CUSTOM_ATTR_DEVICE_ID,\n    0xFFF2: ha_standard_devices.ESP_ZB_HA_CUSTOM_ATTR_DEVICE_ID,\n}\ncluster_id = cg.esphome_ns.enum(\"esp_zb_zcl_cluster_id_t\")\nCLUSTER_ID = {\n    \"BASIC\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_BASIC,\n    0x0000: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_BASIC,\n    \"POWER_CONFIG\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_POWER_CONFIG,\n    0x0001: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_POWER_CONFIG,\n    \"DEVICE_TEMP_CONFIG\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_DEVICE_TEMP_CONFIG,\n    0x0002: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_DEVICE_TEMP_CONFIG,\n    \"IDENTIFY\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY,\n    0x0003: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY,\n    \"GROUPS\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_GROUPS,\n    0x0004: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_GROUPS,\n    \"SCENES\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_SCENES,\n    0x0005: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_SCENES,\n    \"ON_OFF\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_ON_OFF,\n    0x0006: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_ON_OFF,\n    \"ON_OFF_SWITCH_CONFIG\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_ON_OFF_SWITCH_CONFIG,\n    0x0007: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_ON_OFF_SWITCH_CONFIG,\n    \"LEVEL_CONTROL\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL,\n    0x0008: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL,\n    \"ALARMS\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_ALARMS,\n    0x0009: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_ALARMS,\n    \"TIME\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_TIME,\n    0x000A: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_TIME,\n    \"RSSI_LOCATION\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_RSSI_LOCATION,\n    0x000B: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_RSSI_LOCATION,\n    \"ANALOG_INPUT\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT,\n    0x000C: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT,\n    \"ANALOG_OUTPUT\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT,\n    0x000D: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT,\n    \"ANALOG_VALUE\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_ANALOG_VALUE,\n    0x000E: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_ANALOG_VALUE,\n    \"BINARY_INPUT\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_BINARY_INPUT,\n    0x000F: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_BINARY_INPUT,\n    \"BINARY_OUTPUT\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT,\n    0x0010: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT,\n    \"BINARY_VALUE\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_BINARY_VALUE,\n    0x0011: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_BINARY_VALUE,\n    \"MULTI_INPUT\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_MULTI_INPUT,\n    0x0012: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_MULTI_INPUT,\n    \"MULTI_OUTPUT\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_MULTI_OUTPUT,\n    0x0013: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_MULTI_OUTPUT,\n    \"MULTI_VALUE\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_MULTI_VALUE,\n    0x0014: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_MULTI_VALUE,\n    \"COMMISSIONING\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_COMMISSIONING,\n    0x0015: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_COMMISSIONING,\n    \"OTA_UPGRADE\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_OTA_UPGRADE,\n    0x0019: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_OTA_UPGRADE,\n    \"POLL_CONTROL\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_POLL_CONTROL,\n    0x0020: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_POLL_CONTROL,\n    \"GREEN_POWER\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_GREEN_POWER,\n    0x0021: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_GREEN_POWER,\n    \"KEEP_ALIVE\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_KEEP_ALIVE,\n    0x0025: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_KEEP_ALIVE,\n    \"SHADE_CONFIG\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_SHADE_CONFIG,\n    0x0100: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_SHADE_CONFIG,\n    \"DOOR_LOCK\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_DOOR_LOCK,\n    0x0101: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_DOOR_LOCK,\n    \"WINDOW_COVERING\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING,\n    0x0102: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING,\n    \"PUMP_CONFIG_CONTROL\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_PUMP_CONFIG_CONTROL,\n    0x0200: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_PUMP_CONFIG_CONTROL,\n    \"THERMOSTAT\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_THERMOSTAT,\n    0x0201: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_THERMOSTAT,\n    \"FAN_CONTROL\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_FAN_CONTROL,\n    0x0202: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_FAN_CONTROL,\n    \"DEHUMIDIFICATION_CONTROL\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_DEHUMIDIFICATION_CONTROL,\n    0x0203: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_DEHUMIDIFICATION_CONTROL,\n    \"THERMOSTAT_UI_CONFIG\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_THERMOSTAT_UI_CONFIG,\n    0x0204: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_THERMOSTAT_UI_CONFIG,\n    \"COLOR_CONTROL\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL,\n    0x0300: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL,\n    \"BALLAST_CONFIG\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_BALLAST_CONFIG,\n    0x0301: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_BALLAST_CONFIG,\n    \"ILLUMINANCE_MEASUREMENT\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_ILLUMINANCE_MEASUREMENT,\n    0x0400: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_ILLUMINANCE_MEASUREMENT,\n    \"TEMP_MEASUREMENT\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT,\n    0x0402: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT,\n    \"PRESSURE_MEASUREMENT\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_PRESSURE_MEASUREMENT,\n    0x0403: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_PRESSURE_MEASUREMENT,\n    \"FLOW_MEASUREMENT\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_FLOW_MEASUREMENT,\n    0x0404: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_FLOW_MEASUREMENT,\n    \"REL_HUMIDITY_MEASUREMENT\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT,\n    0x0405: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT,\n    \"OCCUPANCY_SENSING\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_OCCUPANCY_SENSING,\n    0x0406: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_OCCUPANCY_SENSING,\n    \"PH_MEASUREMENT\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_PH_MEASUREMENT,\n    0x0409: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_PH_MEASUREMENT,\n    \"EC_MEASUREMENT\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_EC_MEASUREMENT,\n    0x040A: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_EC_MEASUREMENT,\n    \"WIND_SPEED_MEASUREMENT\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_WIND_SPEED_MEASUREMENT,\n    0x040B: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_WIND_SPEED_MEASUREMENT,\n    \"CARBON_DIOXIDE_MEASUREMENT\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_CARBON_DIOXIDE_MEASUREMENT,\n    0x040D: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_CARBON_DIOXIDE_MEASUREMENT,\n    \"PM2_5_MEASUREMENT\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_PM2_5_MEASUREMENT,\n    0x042A: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_PM2_5_MEASUREMENT,\n    \"IAS_ZONE\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE,\n    0x0500: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE,\n    \"IAS_ACE\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_IAS_ACE,\n    0x0501: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_IAS_ACE,\n    \"IAS_WD\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_IAS_WD,\n    0x0502: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_IAS_WD,\n    \"PRICE\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_PRICE,\n    0x0700: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_PRICE,\n    \"DRLC\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_DRLC,\n    0x0701: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_DRLC,\n    \"METERING\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_METERING,\n    0x0702: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_METERING,\n    \"METER_IDENTIFICATION\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_METER_IDENTIFICATION,\n    0x0B01: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_METER_IDENTIFICATION,\n    \"ELECTRICAL_MEASUREMENT\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_ELECTRICAL_MEASUREMENT,\n    0x0B04: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_ELECTRICAL_MEASUREMENT,\n    \"DIAGNOSTICS\": cluster_id.ESP_ZB_ZCL_CLUSTER_ID_DIAGNOSTICS,\n    0x0B05: cluster_id.ESP_ZB_ZCL_CLUSTER_ID_DIAGNOSTICS,\n}\ncluster_role = cg.esphome_ns.enum(\"esp_zb_zcl_cluster_role_t\")\nCLUSTER_ROLE = {\n    \"SERVER\": cluster_role.ESP_ZB_ZCL_CLUSTER_SERVER_ROLE,\n    \"CLIENT\": cluster_role.ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE,\n}\nattr_type = cg.esphome_ns.enum(\"esp_zb_zcl_attr_type_t\")\nATTR_TYPE = {\n    \"NULL\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_NULL,\n    \"8BIT\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_8BIT,\n    \"16BIT\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_16BIT,\n    \"24BIT\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_24BIT,\n    \"32BIT\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_32BIT,\n    \"40BIT\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_40BIT,\n    \"48BIT\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_48BIT,\n    \"56BIT\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_56BIT,\n    \"64BIT\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_64BIT,\n    \"BOOL\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_BOOL,\n    \"8BITMAP\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_8BITMAP,\n    \"16BITMAP\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_16BITMAP,\n    \"24BITMAP\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_24BITMAP,\n    \"32BITMAP\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_32BITMAP,\n    \"40BITMAP\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_40BITMAP,\n    \"48BITMAP\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_48BITMAP,\n    \"56BITMAP\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_56BITMAP,\n    \"64BITMAP\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_64BITMAP,\n    \"U8\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_U8,\n    \"U16\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_U16,\n    \"U24\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_U24,\n    \"U32\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_U32,\n    \"U40\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_U40,\n    \"U48\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_U48,\n    \"U56\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_U56,\n    \"U64\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_U64,\n    \"S8\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_S8,\n    \"S16\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_S16,\n    \"S24\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_S24,\n    \"S32\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_S32,\n    \"S40\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_S40,\n    \"S48\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_S48,\n    \"S56\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_S56,\n    \"S64\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_S64,\n    \"8BIT_ENUM\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_8BIT_ENUM,\n    \"16BIT_ENUM\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_16BIT_ENUM,\n    \"SEMI\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_SEMI,\n    \"SINGLE\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_SINGLE,\n    \"DOUBLE\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_DOUBLE,\n    \"OCTET_STRING\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_OCTET_STRING,\n    \"CHAR_STRING\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_CHAR_STRING,\n    \"LONG_OCTET_STRING\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_LONG_OCTET_STRING,\n    \"LONG_CHAR_STRING\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_LONG_CHAR_STRING,\n    \"ARRAY\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_ARRAY,\n    \"16BIT_ARRAY\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_16BIT_ARRAY,\n    \"32BIT_ARRAY\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_32BIT_ARRAY,\n    \"STRUCTURE\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_STRUCTURE,\n    \"SET\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_SET,\n    \"BAG\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_BAG,\n    \"TIME_OF_DAY\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_TIME_OF_DAY,\n    \"DATE\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_DATE,\n    \"UTC_TIME\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_UTC_TIME,\n    \"CLUSTER_ID\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_CLUSTER_ID,\n    \"ATTRIBUTE_ID\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_ATTRIBUTE_ID,\n    \"BACNET_OID\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_BACNET_OID,\n    \"IEEE_ADDR\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_IEEE_ADDR,\n    \"128_BIT_KEY\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_128_BIT_KEY,\n    \"INVALID\": attr_type.ESP_ZB_ZCL_ATTR_TYPE_INVALID,\n}\nATTR_ACCESS = {\n    \"READ_ONLY\": 1,\n    \"WRITE_ONLY\": 2,\n    \"READ_WRITE\": 3,\n}\n"
  },
  {
    "path": "components/zigbee/zigbee_ep.py",
    "content": "import copy\n\nfrom esphome.components import light, output\nimport esphome.config_validation as cv\nfrom esphome.const import (\n    CONF_COMPONENTS,\n    CONF_DEVICE,\n    CONF_DEVICE_CLASS,\n    CONF_ID,\n    CONF_MAX_LENGTH,\n    CONF_TYPE,\n    CONF_UNIT_OF_MEASUREMENT,\n    CONF_VALUE,\n    DEVICE_CLASS_CURRENT,\n    DEVICE_CLASS_DURATION,\n    DEVICE_CLASS_ENERGY,\n    DEVICE_CLASS_FREQUENCY,\n    DEVICE_CLASS_HUMIDITY,\n    DEVICE_CLASS_POWER,\n    DEVICE_CLASS_PRESSURE,\n    DEVICE_CLASS_TEMPERATURE,\n    DEVICE_CLASS_VOLUME_FLOW_RATE,\n    UNIT_AMPERE,\n    UNIT_CELSIUS,\n    UNIT_HECTOPASCAL,\n    UNIT_HERTZ,\n    UNIT_KILOWATT,\n    UNIT_KILOWATT_HOURS,\n    UNIT_LUX,\n    UNIT_MICROGRAMS_PER_CUBIC_METER,\n    UNIT_OHM,\n    UNIT_PARTS_PER_MILLION,\n    UNIT_PASCAL,\n    UNIT_PERCENT,\n    UNIT_SECOND,\n    UNIT_VOLT,\n    UNIT_WATT,\n)\nfrom esphome.core import CORE, ID\n\nfrom .const import (\n    CONF_ACCESS,\n    CONF_AS_GENERIC,\n    CONF_ATTRIBUTE_ID,\n    CONF_ATTRIBUTES,\n    CONF_CLUSTERS,\n    CONF_DEVICE_TYPE,\n    CONF_ENDPOINTS,\n    CONF_NUM,\n    CONF_REPORT,\n    CONF_ROLE,\n    CONF_SCALE,\n    AnalogInputType,\n    BacnetUnit,\n    BinarySensor,\n    Sensor,\n    Switch,\n)\nfrom .types import ZigBeeAttribute\n\n# endpoint configs:\nep_configs = {\n    \"binary_input\": {\n        CONF_DEVICE_TYPE: \"CUSTOM_ATTR\",\n        CONF_CLUSTERS: [\n            {\n                CONF_ID: \"BINARY_INPUT\",\n                CONF_ROLE: \"SERVER\",\n                CONF_ATTRIBUTES: [\n                    {\n                        CONF_ATTRIBUTE_ID: 0x55,\n                        CONF_VALUE: False,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"BOOL\",\n                        CONF_REPORT: True,\n                        CONF_SCALE: 1,\n                        CONF_DEVICE: None,\n                    },\n                    {\n                        CONF_ATTRIBUTE_ID: 0x51,\n                        CONF_VALUE: False,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"BOOL\",\n                        CONF_REPORT: False,\n                        CONF_SCALE: 1,\n                    },\n                    {\n                        CONF_ATTRIBUTE_ID: 0x6F,\n                        CONF_VALUE: 0,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"8BITMAP\",\n                        CONF_REPORT: False,\n                        CONF_SCALE: 1,\n                    },\n                    {\n                        CONF_ATTRIBUTE_ID: 0x1C,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"CHAR_STRING\",\n                        CONF_REPORT: False,\n                        CONF_SCALE: 1,\n                    },\n                ],\n            },\n        ],\n    },\n    \"analog_input\": {\n        CONF_DEVICE_TYPE: \"CUSTOM_ATTR\",\n        CONF_CLUSTERS: [\n            {\n                CONF_ID: \"ANALOG_INPUT\",\n                CONF_ROLE: \"SERVER\",\n                CONF_ATTRIBUTES: [\n                    {\n                        CONF_ATTRIBUTE_ID: 0x55,\n                        CONF_VALUE: False,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"SINGLE\",\n                        CONF_REPORT: True,\n                        CONF_SCALE: 1,\n                        CONF_DEVICE: None,\n                    },\n                    {\n                        CONF_ATTRIBUTE_ID: 0x51,\n                        CONF_VALUE: False,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"BOOL\",\n                        CONF_REPORT: False,\n                        CONF_SCALE: 1,\n                    },\n                    {\n                        CONF_ATTRIBUTE_ID: 0x6F,\n                        CONF_VALUE: 0,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"8BITMAP\",\n                        CONF_REPORT: False,\n                        CONF_SCALE: 1,\n                    },\n                    {\n                        CONF_ATTRIBUTE_ID: 0x1C,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"CHAR_STRING\",\n                        CONF_REPORT: False,\n                        CONF_SCALE: 1,\n                    },\n                ],\n            },\n        ],\n    },\n    \"binary_output\": {\n        CONF_DEVICE_TYPE: \"CUSTOM_ATTR\",\n        CONF_CLUSTERS: [\n            {\n                CONF_ID: \"BINARY_OUTPUT\",\n                CONF_ROLE: \"SERVER\",\n                CONF_ATTRIBUTES: [\n                    {\n                        CONF_ATTRIBUTE_ID: 0x55,\n                        CONF_VALUE: False,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"BOOL\",\n                        CONF_REPORT: True,\n                        CONF_SCALE: 1,\n                        CONF_DEVICE: None,\n                    },\n                    {\n                        CONF_ATTRIBUTE_ID: 0x51,\n                        CONF_VALUE: False,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"BOOL\",\n                        CONF_REPORT: False,\n                        CONF_SCALE: 1,\n                    },\n                    {\n                        CONF_ATTRIBUTE_ID: 0x6F,\n                        CONF_VALUE: 0,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"8BITMAP\",\n                        CONF_REPORT: False,\n                        CONF_SCALE: 1,\n                    },\n                    {\n                        CONF_ATTRIBUTE_ID: 0x1C,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"CHAR_STRING\",\n                        CONF_REPORT: False,\n                        CONF_SCALE: 1,\n                    },\n                ],\n            },\n        ],\n    },\n    \"temperature\": {\n        CONF_DEVICE_TYPE: \"TEMPERATURE_SENSOR\",\n        CONF_CLUSTERS: [\n            {\n                CONF_ID: \"TEMP_MEASUREMENT\",\n                CONF_ROLE: \"SERVER\",\n                CONF_ATTRIBUTES: [\n                    {\n                        CONF_ATTRIBUTE_ID: 0x0,\n                        CONF_VALUE: 0,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"S16\",\n                        CONF_REPORT: True,\n                        CONF_SCALE: 100,\n                        CONF_DEVICE: None,\n                    },\n                ],\n            },\n        ],\n    },\n    \"on_off\": {\n        CONF_DEVICE_TYPE: \"ON_OFF_OUTPUT\",\n        CONF_CLUSTERS: [\n            {\n                CONF_ID: \"ON_OFF\",\n                CONF_ROLE: \"SERVER\",\n                CONF_ATTRIBUTES: [\n                    {\n                        CONF_ATTRIBUTE_ID: 0x0,\n                        CONF_VALUE: False,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"BOOL\",\n                        CONF_REPORT: True,\n                        CONF_SCALE: 1,\n                        CONF_DEVICE: None,\n                    },\n                ],\n            },\n        ],\n    },\n    \"on_off_light\": {\n        CONF_DEVICE_TYPE: \"ON_OFF_LIGHT\",\n        CONF_CLUSTERS: [\n            {\n                CONF_ID: \"ON_OFF\",\n                CONF_ROLE: \"SERVER\",\n                CONF_ATTRIBUTES: [\n                    {\n                        CONF_ATTRIBUTE_ID: 0x0,\n                        CONF_VALUE: False,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"BOOL\",\n                        CONF_REPORT: True,\n                        CONF_SCALE: 1,\n                        CONF_DEVICE: None,\n                    },\n                ],\n            },\n        ],\n    },\n    \"color_light\": {\n        CONF_DEVICE_TYPE: \"COLOR_DIMMABLE_LIGHT\",\n        CONF_CLUSTERS: [\n            {\n                CONF_ID: \"ON_OFF\",\n                CONF_ROLE: \"SERVER\",\n                CONF_ATTRIBUTES: [\n                    {\n                        CONF_ATTRIBUTE_ID: 0x0,\n                        CONF_VALUE: False,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"BOOL\",\n                        CONF_REPORT: True,\n                        CONF_SCALE: 1,\n                        CONF_DEVICE: None,\n                    },\n                ],\n            },\n            {\n                CONF_ID: \"LEVEL_CONTROL\",\n                CONF_ROLE: \"SERVER\",\n                CONF_ATTRIBUTES: [\n                    {\n                        CONF_ATTRIBUTE_ID: 0x0,\n                        CONF_VALUE: 255,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"U8\",\n                        CONF_REPORT: True,\n                        CONF_SCALE: 1,\n                        CONF_DEVICE: None,\n                    },\n                ],\n            },\n            {\n                CONF_ID: \"COLOR_CONTROL\",\n                CONF_ROLE: \"SERVER\",\n                CONF_ATTRIBUTES: [\n                    {\n                        CONF_ATTRIBUTE_ID: 0x3,\n                        CONF_VALUE: 0x616B,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"U16\",\n                        CONF_REPORT: True,\n                        CONF_SCALE: 1,\n                        CONF_DEVICE: None,\n                    },\n                    {\n                        CONF_ATTRIBUTE_ID: 0x4,\n                        CONF_VALUE: 0x607D,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"U16\",\n                        CONF_REPORT: True,\n                        CONF_SCALE: 1,\n                        CONF_DEVICE: None,\n                    },\n                    {\n                        CONF_ATTRIBUTE_ID: 0x400A,\n                        CONF_VALUE: 8,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"16BITMAP\",\n                        CONF_REPORT: False,\n                        CONF_SCALE: 1,\n                    },\n                ],\n            },\n        ],\n    },\n    \"level_light\": {\n        CONF_DEVICE_TYPE: \"DIMMABLE_LIGHT\",\n        CONF_CLUSTERS: [\n            {\n                CONF_ID: \"ON_OFF\",\n                CONF_ROLE: \"SERVER\",\n                CONF_ATTRIBUTES: [\n                    {\n                        CONF_ATTRIBUTE_ID: 0x0,\n                        CONF_VALUE: False,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"BOOL\",\n                        CONF_REPORT: True,\n                        CONF_SCALE: 1,\n                        CONF_DEVICE: None,\n                    },\n                ],\n            },\n            {\n                CONF_ID: \"LEVEL_CONTROL\",\n                CONF_ROLE: \"SERVER\",\n                CONF_ATTRIBUTES: [\n                    {\n                        CONF_ATTRIBUTE_ID: 0x0,\n                        CONF_VALUE: 255,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"U8\",\n                        CONF_REPORT: True,\n                        CONF_SCALE: 1,\n                        CONF_DEVICE: None,\n                    },\n                ],\n            },\n        ],\n    },\n}\n\n\ndef get_next_ep_num(eps):\n    try:\n        ep_num = [i for i in range(1, 240) if i not in eps][0]\n        eps.append(ep_num)\n    except IndexError as e:\n        raise cv.Invalid(\n            \"Too many devices. Zigbee can define only 240 endpoints.\"\n        ) from e\n    return ep_num\n\n\nANALOG_INPUT_APPTYPE = {\n    (DEVICE_CLASS_TEMPERATURE, UNIT_CELSIUS): AnalogInputType.Temp_Degrees_C,\n    (DEVICE_CLASS_HUMIDITY, UNIT_PERCENT): AnalogInputType.Relative_Humidity_Percent,\n    (DEVICE_CLASS_PRESSURE, UNIT_PASCAL): AnalogInputType.Pressure_Pascal,\n    (DEVICE_CLASS_VOLUME_FLOW_RATE, \"L/s\"): AnalogInputType.Flow_Liters_Per_Sec,\n    (DEVICE_CLASS_CURRENT, UNIT_AMPERE): AnalogInputType.Current_Amps,\n    (DEVICE_CLASS_FREQUENCY, UNIT_HERTZ): AnalogInputType.Frequency_Hz,\n    (DEVICE_CLASS_POWER, UNIT_WATT): AnalogInputType.Power_Watts,\n    (DEVICE_CLASS_POWER, UNIT_KILOWATT): AnalogInputType.Power_Kilo_Watts,\n    (DEVICE_CLASS_ENERGY, UNIT_KILOWATT_HOURS): AnalogInputType.Energy_Kilo_Watt_Hours,\n    (DEVICE_CLASS_DURATION, UNIT_SECOND): AnalogInputType.Time_Seconds,\n}\n\"\"\"\nnot implemented:\n    AnalogInputType.Percentage\n    AnalogInputType.Parts_Per_Million\n    AnalogInputType.Rotational_Speed_RPM\n    AnalogInputType.Count\n    AnalogInputType.Enthalpy_KJoules_Per_Kg\n\"\"\"\n\nBACNET_UNIT = {\n    UNIT_CELSIUS: BacnetUnit.DEGREES_CELSIUS,\n    \"°F\": BacnetUnit.DEGREES_FAHRENHEIT,\n    UNIT_PERCENT: BacnetUnit.PERCENT,\n    UNIT_LUX: BacnetUnit.LUXES,\n    UNIT_VOLT: BacnetUnit.VOLTS,\n    UNIT_MICROGRAMS_PER_CUBIC_METER: BacnetUnit.MICROGRAMS_PER_CUBIC_METER,\n    UNIT_PARTS_PER_MILLION: BacnetUnit.PARTS_PER_MILLION,\n    UNIT_OHM: BacnetUnit.OHMS,\n    UNIT_HECTOPASCAL: BacnetUnit.HECTOPASCALS,\n}\n\n\ndef create_device_ep(eps, dev, generic=False):\n    ep = {}\n    ep[CONF_NUM] = get_next_ep_num(eps)\n    if dev[\"id\"].type.inherits_from(Sensor):\n        if dev.get(CONF_DEVICE_CLASS, \"\") in ep_configs and not generic:\n            ep.update(copy.deepcopy(ep_configs[dev[CONF_DEVICE_CLASS]]))\n        else:\n            # get application type from device class and meas unit\n            # if none get BACNET unit from meas unit\n            ep.update(copy.deepcopy(ep_configs[\"analog_input\"]))\n            dev_class = dev.get(CONF_DEVICE_CLASS)\n            unit = dev.get(CONF_UNIT_OF_MEASUREMENT)\n            apptype = ANALOG_INPUT_APPTYPE.get((dev_class, unit))\n            bacunit = BACNET_UNIT.get(unit)\n            if apptype is not None:\n                ep[CONF_CLUSTERS][0][CONF_ATTRIBUTES].append(\n                    {\n                        CONF_ATTRIBUTE_ID: 0x100,\n                        CONF_VALUE: (apptype << 16) + 0xFFFF,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"U32\",\n                        CONF_REPORT: False,\n                        CONF_SCALE: 1,\n                    },\n                )\n            if bacunit is not None:\n                ep[CONF_CLUSTERS][0][CONF_ATTRIBUTES].append(\n                    {\n                        CONF_ATTRIBUTE_ID: 0x75,\n                        CONF_VALUE: bacunit,\n                        CONF_ACCESS: 0,\n                        CONF_TYPE: \"16BIT_ENUM\",\n                        CONF_REPORT: False,\n                        CONF_SCALE: 1,\n                    },\n                )\n\n    elif dev[\"id\"].type.inherits_from(Switch):\n        if generic:\n            ep.update(copy.deepcopy(ep_configs[\"binary_output\"]))\n        else:\n            ep.update(copy.deepcopy(ep_configs[\"on_off\"]))\n    elif dev[\"id\"].type.inherits_from(BinarySensor):\n        ep.update(copy.deepcopy(ep_configs[\"binary_input\"]))\n    elif dev[\"id\"].type.inherits_from(light.LightState):\n        # NB: output.FloatOutput is a subclass of output.BinaryOutput thus must be checked first\n        if (\n            dev[\"platform\"] in [\"monochromatic\"]\n            or \"dimmer\" in dev[\"platform\"]\n            or (\n                \"output\" in dev and dev[\"output\"].type.inherits_from(output.FloatOutput)\n            )\n        ):\n            if generic:\n                ep.update(copy.deepcopy(ep_configs[\"analog_output\"]))\n                ep[CONF_CLUSTERS].append(\n                    copy.deepcopy(ep_configs[\"binary_output\"][CONF_CLUSTERS])\n                )\n            else:\n                ep.update(copy.deepcopy(ep_configs[\"level_light\"]))\n        elif dev[\"platform\"] in [\"binary\", \"status_led\"] or (\n            \"output\" in dev and dev[\"output\"].type.inherits_from(output.BinaryOutput)\n        ):\n            if generic:\n                ep.update(copy.deepcopy(ep_configs[\"binary_output\"]))\n            else:\n                ep.update(copy.deepcopy(ep_configs[\"on_off_light\"]))\n        else:\n            ep.update(copy.deepcopy(ep_configs[\"color_light\"]))\n    for cl in ep.get(CONF_CLUSTERS, []):\n        for attr in cl[CONF_ATTRIBUTES]:\n            if (\n                attr[CONF_ATTRIBUTE_ID] == 0x1C\n                and CONF_VALUE not in attr\n                and \"name\" in dev\n            ):  # set name\n                name = dev[\"name\"].encode(\"ascii\", \"ignore\").decode()  # use unidecode\n                attr[CONF_VALUE] = str(name)\n                attr[CONF_MAX_LENGTH] = len(str(name))\n            if CONF_DEVICE in attr:  # connect device\n                attr[CONF_DEVICE] = dev[\"id\"]\n                # create attribute ID\n                id = ID(None, is_declaration=True, type=ZigBeeAttribute)\n                id.resolve(CORE.component_ids)\n                CORE.component_ids.add(id.id)\n                attr[CONF_ID] = id\n            else:\n                attr[CONF_ID] = None\n    return ep\n\n\ndef get_device_entries(conf: list, component_type):\n    devices = []\n    for d in conf:\n        if CONF_ID in d and d[CONF_ID].type.inherits_from(component_type):\n            devices.append(d)\n        else:\n            for dd in d.values():\n                if isinstance(dd, dict):\n                    devices.extend(get_device_entries([dd], component_type))\n    return devices\n\n\ndef create_ep(config, full_conf):\n    eps = []\n    comp_ids = len(CORE.component_ids)\n    if CONF_ENDPOINTS in config:\n        eps = [ep.get(CONF_NUM) for ep in config[CONF_ENDPOINTS]]\n        for ep in config[CONF_ENDPOINTS]:\n            if CONF_NUM not in ep:\n                ep[CONF_NUM] = get_next_ep_num(eps)\n    ep_list = config.get(CONF_ENDPOINTS, [])\n    if CONF_COMPONENTS in config:\n        devs = [\n            i[\"id\"]\n            for i in get_device_entries(full_conf.get(\"light\", []), light.LightState)\n            + get_device_entries(full_conf.get(\"switch\", []), Switch)\n            + get_device_entries(full_conf.get(\"sensor\", []), Sensor)\n            + get_device_entries(full_conf.get(\"binary_sensor\", []), BinarySensor)\n        ]\n\n        add_devices = []\n\n        if isinstance(config[CONF_COMPONENTS], list):\n            list_devs = []\n            for dev in config[CONF_COMPONENTS]:\n                if dev in devs:\n                    list_devs.append(dev)\n            add_devices = [\n                i\n                for i in get_device_entries(\n                    full_conf.get(\"light\", []), light.LightState\n                )\n                + get_device_entries(full_conf.get(\"switch\", []), Switch)\n                + get_device_entries(full_conf.get(\"sensor\", []), Sensor)\n                + get_device_entries(full_conf.get(\"binary_sensor\", []), BinarySensor)\n                if i[\"id\"] in list_devs\n            ]\n        if config[CONF_COMPONENTS] == \"all\":\n            add_devices = [\n                i\n                for i in get_device_entries(\n                    full_conf.get(\"light\", []), light.LightState\n                )\n                + get_device_entries(full_conf.get(\"switch\", []), Switch)\n                + get_device_entries(full_conf.get(\"sensor\", []), Sensor)\n                + get_device_entries(full_conf.get(\"binary_sensor\", []), BinarySensor)\n                if (\"name\" in i) and not i.get(\"internal\")\n            ]\n        for dev in add_devices:\n            ep_list.append(create_device_ep(eps, dev, config[CONF_AS_GENERIC]))\n    if not ep_list:\n        ep_list = [\n            {\n                CONF_DEVICE_TYPE: \"CUSTOM_ATTR\",\n                CONF_NUM: 1,\n            }\n        ]\n    return ep_list, len(CORE.component_ids) - comp_ids\n"
  },
  {
    "path": "components/zigbee/zigbee_helpers.c",
    "content": "#include \"ha/esp_zigbee_ha_standard.h\"\n#include \"zigbee_helpers.h\"\n\nesp_zb_cluster_list_t *esphome_zb_default_clusters_create(esp_zb_ha_standard_devices_t device_type) {\n  esp_zb_cluster_list_t *cluster_list;\n  switch (device_type) {\n    case ESP_ZB_HA_ON_OFF_SWITCH_DEVICE_ID: {\n      esp_zb_on_off_switch_cfg_t config = ESP_ZB_DEFAULT_ON_OFF_SWITCH_CONFIG();\n      cluster_list = esp_zb_on_off_switch_clusters_create(&config);\n      break;\n    }\n    case ESP_ZB_HA_ON_OFF_LIGHT_DEVICE_ID: {\n      esp_zb_on_off_light_cfg_t config = ESP_ZB_DEFAULT_ON_OFF_LIGHT_CONFIG();\n      cluster_list = esp_zb_on_off_light_clusters_create(&config);\n      break;\n    }\n    case ESP_ZB_HA_COLOR_DIMMER_SWITCH_DEVICE_ID: {\n      esp_zb_color_dimmable_switch_cfg_t config = ESP_ZB_DEFAULT_COLOR_DIMMABLE_SWITCH_CONFIG();\n      cluster_list = esp_zb_color_dimmable_switch_clusters_create(&config);\n      break;\n    }\n    case ESP_ZB_HA_COLOR_DIMMABLE_LIGHT_DEVICE_ID: {\n      esp_zb_color_dimmable_light_cfg_t config = ESP_ZB_DEFAULT_COLOR_DIMMABLE_LIGHT_CONFIG();\n      cluster_list = esp_zb_color_dimmable_light_clusters_create(&config);\n      break;\n    }\n    case ESP_ZB_HA_MAINS_POWER_OUTLET_DEVICE_ID: {\n      esp_zb_mains_power_outlet_cfg_t config = ESP_ZB_DEFAULT_MAINS_POWER_OUTLET_CONFIG();\n      cluster_list = esp_zb_mains_power_outlet_clusters_create(&config);\n      break;\n    }\n    case ESP_ZB_HA_SHADE_DEVICE_ID: {\n      esp_zb_shade_cfg_t config = ESP_ZB_DEFAULT_SHADE_CONFIG();\n      cluster_list = esp_zb_shade_clusters_create(&config);\n      break;\n    }\n    case ESP_ZB_HA_SHADE_CONTROLLER_DEVICE_ID: {\n      esp_zb_shade_controller_cfg_t config = ESP_ZB_DEFAULT_SHADE_CONTROLLER_CONFIG();\n      cluster_list = esp_zb_shade_controller_clusters_create(&config);\n      break;\n    }\n    case ESP_ZB_HA_DOOR_LOCK_DEVICE_ID: {\n      esp_zb_door_lock_cfg_t config = ESP_ZB_DEFAULT_DOOR_LOCK_CONFIG();\n      cluster_list = esp_zb_door_lock_clusters_create(&config);\n      break;\n    }\n    case ESP_ZB_HA_DOOR_LOCK_CONTROLLER_DEVICE_ID: {\n      esp_zb_door_lock_controller_cfg_t config = ESP_ZB_DEFAULT_DOOR_LOCK_CONTROLLER_CONFIG();\n      cluster_list = esp_zb_door_lock_controller_clusters_create(&config);\n      break;\n    }\n    case ESP_ZB_HA_TEMPERATURE_SENSOR_DEVICE_ID: {\n      esp_zb_temperature_sensor_cfg_t config = ESP_ZB_DEFAULT_TEMPERATURE_SENSOR_CONFIG();\n      cluster_list = esp_zb_temperature_sensor_clusters_create(&config);\n      break;\n    }\n    case ESP_ZB_HA_CONFIGURATION_TOOL_DEVICE_ID: {\n      esp_zb_configuration_tool_cfg_t config = ESP_ZB_DEFAULT_CONFIGURATION_TOOL_CONFIG();\n      cluster_list = esp_zb_configuration_tool_clusters_create(&config);\n      break;\n    }\n    case ESP_ZB_HA_THERMOSTAT_DEVICE_ID: {\n      esp_zb_thermostat_cfg_t config = ESP_ZB_DEFAULT_THERMOSTAT_CONFIG();\n      cluster_list = esp_zb_thermostat_clusters_create(&config);\n      break;\n    }\n    case ESP_ZB_HA_WINDOW_COVERING_DEVICE_ID: {\n      esp_zb_window_covering_cfg_t config = ESP_ZB_DEFAULT_WINDOW_COVERING_CONFIG();\n      cluster_list = esp_zb_window_covering_clusters_create(&config);\n      break;\n    }\n    case ESP_ZB_HA_WINDOW_COVERING_CONTROLLER_DEVICE_ID: {\n      esp_zb_window_covering_controller_cfg_t config = ESP_ZB_DEFAULT_WINDOW_COVERING_CONTROLLER_CONFIG();\n      cluster_list = esp_zb_window_covering_controller_clusters_create(&config);\n      break;\n    }\n    default:\n      // create empty cluster list;\n      cluster_list = esp_zb_zcl_cluster_list_create();\n  }\n\n  return cluster_list;\n}\n\nesp_err_t esphome_zb_cluster_add_or_update_attr(uint16_t cluster_id, esp_zb_attribute_list_t *attr_list,\n                                                uint16_t attr_id, uint8_t attr_type, uint8_t attr_access,\n                                                void *value_p) {\n  esp_err_t ret;\n  ret = esp_zb_cluster_update_attr(attr_list, attr_id, value_p);\n  if (ret != ESP_OK) {\n    ESP_LOGE(\"zigbee_helper\", \"Ignore previous attribute not found error\");\n    if (attr_access > 0) {\n      ret = esp_zb_cluster_add_attr(attr_list, cluster_id, attr_id, attr_type, attr_access, value_p);\n    } else {\n      ret = esphome_zb_cluster_add_attr(cluster_id, attr_list, attr_id, value_p);\n    }\n  }\n  return ret;\n}\n\nesp_err_t esphome_zb_cluster_list_add_or_update_cluster(uint16_t cluster_id, esp_zb_cluster_list_t *cluster_list,\n                                                        esp_zb_attribute_list_t *attr_list, uint8_t role_mask) {\n  esp_err_t ret;\n  ret = esp_zb_cluster_list_update_cluster(cluster_list, attr_list, cluster_id, role_mask);\n  if (ret != ESP_OK) {\n    ESP_LOGE(\"zigbee_helper\", \"Ignore previous cluster not found error\");\n    switch (cluster_id) {\n      case ESP_ZB_ZCL_CLUSTER_ID_BASIC:\n        ret = esp_zb_cluster_list_add_basic_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_POWER_CONFIG:\n        ret = esp_zb_cluster_list_add_power_config_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_DEVICE_TEMP_CONFIG:\n        ret = esp_zb_cluster_list_add_device_temp_config_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY:\n        ret = esp_zb_cluster_list_add_identify_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_GROUPS:\n        ret = esp_zb_cluster_list_add_groups_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_SCENES:\n        ret = esp_zb_cluster_list_add_scenes_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_ON_OFF:\n        ret = esp_zb_cluster_list_add_on_off_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_ON_OFF_SWITCH_CONFIG:\n        ret = esp_zb_cluster_list_add_on_off_switch_config_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL:\n        ret = esp_zb_cluster_list_add_level_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_ALARMS:\n        ret = esp_zb_cluster_list_add_alarms_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_TIME:\n        ret = esp_zb_cluster_list_add_time_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT:\n        ret = esp_zb_cluster_list_add_analog_input_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT:\n        ret = esp_zb_cluster_list_add_analog_output_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_ANALOG_VALUE:\n        ret = esp_zb_cluster_list_add_analog_value_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_BINARY_INPUT:\n        ret = esp_zb_cluster_list_add_binary_input_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT:\n        ret = esp_zb_cluster_list_add_binary_output_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_BINARY_VALUE:\n        ret = esp_zb_cluster_list_add_binary_value_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_MULTI_INPUT:\n        ret = esp_zb_cluster_list_add_multistate_input_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_MULTI_OUTPUT:\n        ret = esp_zb_cluster_list_add_multistate_output_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_MULTI_VALUE:\n        ret = esp_zb_cluster_list_add_multistate_value_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_COMMISSIONING:\n        ret = esp_zb_cluster_list_add_commissioning_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_OTA_UPGRADE:\n        ret = esp_zb_cluster_list_add_ota_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_POLL_CONTROL:\n        ret = esp_zb_cluster_list_add_poll_control_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_SHADE_CONFIG:\n        ret = esp_zb_cluster_list_add_shade_config_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_DOOR_LOCK:\n        ret = esp_zb_cluster_list_add_door_lock_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING:\n        ret = esp_zb_cluster_list_add_window_covering_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_THERMOSTAT:\n        ret = esp_zb_cluster_list_add_thermostat_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_FAN_CONTROL:\n        ret = esp_zb_cluster_list_add_fan_control_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_DEHUMIDIFICATION_CONTROL:\n        ret = esp_zb_cluster_list_add_dehumidification_control_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_THERMOSTAT_UI_CONFIG:\n        ret = esp_zb_cluster_list_add_thermostat_ui_config_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL:\n        ret = esp_zb_cluster_list_add_color_control_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_ILLUMINANCE_MEASUREMENT:\n        ret = esp_zb_cluster_list_add_illuminance_meas_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT:\n        ret = esp_zb_cluster_list_add_temperature_meas_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_PRESSURE_MEASUREMENT:\n        ret = esp_zb_cluster_list_add_pressure_meas_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_FLOW_MEASUREMENT:\n        ret = esp_zb_cluster_list_add_flow_meas_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT:\n        ret = esp_zb_cluster_list_add_humidity_meas_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_OCCUPANCY_SENSING:\n        ret = esp_zb_cluster_list_add_occupancy_sensing_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_PH_MEASUREMENT:\n        ret = esp_zb_cluster_list_add_ph_measurement_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_EC_MEASUREMENT:\n        ret = esp_zb_cluster_list_add_ec_measurement_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_WIND_SPEED_MEASUREMENT:\n        ret = esp_zb_cluster_list_add_wind_speed_measurement_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_CARBON_DIOXIDE_MEASUREMENT:\n        ret = esp_zb_cluster_list_add_carbon_dioxide_measurement_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_PM2_5_MEASUREMENT:\n        ret = esp_zb_cluster_list_add_pm2_5_measurement_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE:\n        ret = esp_zb_cluster_list_add_ias_zone_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_IAS_ACE:\n        ret = esp_zb_cluster_list_add_ias_ace_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_IAS_WD:\n        ret = esp_zb_cluster_list_add_ias_wd_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_PRICE:\n        ret = esp_zb_cluster_list_add_price_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_DRLC:\n        ret = esp_zb_cluster_list_add_drlc_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_METERING:\n        ret = esp_zb_cluster_list_add_metering_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_METER_IDENTIFICATION:\n        ret = esp_zb_cluster_list_add_meter_identification_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_ELECTRICAL_MEASUREMENT:\n        ret = esp_zb_cluster_list_add_electrical_meas_cluster(cluster_list, attr_list, role_mask);\n        break;\n      case ESP_ZB_ZCL_CLUSTER_ID_DIAGNOSTICS:\n        ret = esp_zb_cluster_list_add_diagnostics_cluster(cluster_list, attr_list, role_mask);\n        break;\n      default:\n        ret = esp_zb_cluster_list_add_custom_cluster(cluster_list, attr_list, role_mask);\n    }\n  }\n  return ret;\n}\n\nesp_zb_attribute_list_t *esphome_zb_default_attr_list_create(uint16_t cluster_id) {\n  switch (cluster_id) {\n    case ESP_ZB_ZCL_CLUSTER_ID_BASIC:\n      return esp_zb_basic_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_POWER_CONFIG:\n      return esp_zb_power_config_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_DEVICE_TEMP_CONFIG:\n      return esp_zb_device_temp_config_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY:\n      return esp_zb_identify_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_GROUPS:\n      return esp_zb_groups_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_SCENES:\n      return esp_zb_scenes_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_ON_OFF:\n      return esp_zb_on_off_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_ON_OFF_SWITCH_CONFIG:\n      return esp_zb_on_off_switch_config_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL:\n      return esp_zb_level_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_ALARMS:\n      return esp_zb_alarms_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_TIME:\n      return esp_zb_time_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT:\n      return esp_zb_analog_input_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT:\n      return esp_zb_analog_output_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_ANALOG_VALUE:\n      return esp_zb_analog_value_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_BINARY_INPUT:\n      return esp_zb_binary_input_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT:\n      return esp_zb_binary_output_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_BINARY_VALUE:\n      return esp_zb_binary_value_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_MULTI_INPUT:\n      return esp_zb_multistate_input_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_MULTI_OUTPUT:\n      return esp_zb_multistate_output_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_MULTI_VALUE:\n      return esp_zb_multistate_value_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_COMMISSIONING:\n      return esp_zb_commissioning_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_OTA_UPGRADE:\n      return esp_zb_ota_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_POLL_CONTROL:\n      return esp_zb_poll_control_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_SHADE_CONFIG:\n      return esp_zb_shade_config_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_DOOR_LOCK:\n      return esp_zb_door_lock_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING:\n      return esp_zb_window_covering_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_THERMOSTAT:\n      return esp_zb_thermostat_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_FAN_CONTROL:\n      return esp_zb_fan_control_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_DEHUMIDIFICATION_CONTROL:\n      return esp_zb_dehumidification_control_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_THERMOSTAT_UI_CONFIG:\n      return esp_zb_thermostat_ui_config_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL:\n      return esp_zb_color_control_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_ILLUMINANCE_MEASUREMENT:\n      return esp_zb_illuminance_meas_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT:\n      return esp_zb_temperature_meas_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_PRESSURE_MEASUREMENT:\n      return esp_zb_pressure_meas_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_FLOW_MEASUREMENT:\n      return esp_zb_flow_meas_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT:\n      return esp_zb_humidity_meas_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_OCCUPANCY_SENSING:\n      return esp_zb_occupancy_sensing_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_PH_MEASUREMENT:\n      return esp_zb_ph_measurement_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_EC_MEASUREMENT:\n      return esp_zb_ec_measurement_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_WIND_SPEED_MEASUREMENT:\n      return esp_zb_wind_speed_measurement_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_CARBON_DIOXIDE_MEASUREMENT:\n      return esp_zb_carbon_dioxide_measurement_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_PM2_5_MEASUREMENT:\n      return esp_zb_pm2_5_measurement_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE:\n      return esp_zb_ias_zone_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_IAS_ACE:\n      return esp_zb_ias_ace_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_IAS_WD:\n      return esp_zb_ias_wd_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_PRICE:\n      return esp_zb_price_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_DRLC:\n      return esp_zb_drlc_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_METERING:\n      return esp_zb_metering_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_METER_IDENTIFICATION:\n      return esp_zb_meter_identification_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_ELECTRICAL_MEASUREMENT:\n      return esp_zb_electrical_meas_cluster_create(NULL);\n    case ESP_ZB_ZCL_CLUSTER_ID_DIAGNOSTICS:\n      return esp_zb_diagnostics_cluster_create(NULL);\n    default:\n      return esp_zb_zcl_attr_list_create(cluster_id);\n  }\n}\n\nesp_err_t esphome_zb_cluster_add_attr(uint16_t cluster_id, esp_zb_attribute_list_t *attr_list, uint16_t attr_id,\n                                      void *value_p) {\n  switch (cluster_id) {\n    case ESP_ZB_ZCL_CLUSTER_ID_BASIC:\n      return esp_zb_basic_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_POWER_CONFIG:\n      return esp_zb_power_config_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_DEVICE_TEMP_CONFIG:\n      return esp_zb_device_temp_config_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY:\n      return esp_zb_identify_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_GROUPS:\n      return esp_zb_groups_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_SCENES:\n      return esp_zb_scenes_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_ON_OFF:\n      return esp_zb_on_off_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_ON_OFF_SWITCH_CONFIG:\n      return esp_zb_on_off_switch_config_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL:\n      return esp_zb_level_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_ALARMS:\n      return esp_zb_alarms_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_TIME:\n      return esp_zb_time_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT:\n      return esp_zb_analog_input_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT:\n      return esp_zb_analog_output_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_ANALOG_VALUE:\n      return esp_zb_analog_value_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_BINARY_INPUT:\n      return esp_zb_binary_input_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT:\n      return esp_zb_binary_output_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_BINARY_VALUE:\n      return esp_zb_binary_value_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_MULTI_INPUT:\n      return esp_zb_multistate_input_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_MULTI_OUTPUT:\n      return esp_zb_multistate_output_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_MULTI_VALUE:\n      return esp_zb_multistate_value_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_COMMISSIONING:\n      return esp_zb_commissioning_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_OTA_UPGRADE:\n      return esp_zb_ota_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_POLL_CONTROL:\n      return esp_zb_poll_control_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_SHADE_CONFIG:\n      return esp_zb_shade_config_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_DOOR_LOCK:\n      return esp_zb_door_lock_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_WINDOW_COVERING:\n      return esp_zb_window_covering_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_THERMOSTAT:\n      return esp_zb_thermostat_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_FAN_CONTROL:\n      return esp_zb_fan_control_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_DEHUMIDIFICATION_CONTROL:\n      return esp_zb_dehumidification_control_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_THERMOSTAT_UI_CONFIG:\n      return esp_zb_thermostat_ui_config_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL:\n      return esp_zb_color_control_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_ILLUMINANCE_MEASUREMENT:\n      return esp_zb_illuminance_meas_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT:\n      return esp_zb_temperature_meas_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_PRESSURE_MEASUREMENT:\n      return esp_zb_pressure_meas_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_FLOW_MEASUREMENT:\n      return esp_zb_flow_meas_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT:\n      return esp_zb_humidity_meas_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_OCCUPANCY_SENSING:\n      return esp_zb_occupancy_sensing_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_PH_MEASUREMENT:\n      return esp_zb_ph_measurement_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_EC_MEASUREMENT:\n      return esp_zb_ec_measurement_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_WIND_SPEED_MEASUREMENT:\n      return esp_zb_wind_speed_measurement_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_CARBON_DIOXIDE_MEASUREMENT:\n      return esp_zb_carbon_dioxide_measurement_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_PM2_5_MEASUREMENT:\n      return esp_zb_pm2_5_measurement_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE:\n      return esp_zb_ias_zone_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_IAS_WD:\n      return esp_zb_ias_wd_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_DRLC:\n      return esp_zb_drlc_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_METER_IDENTIFICATION:\n      return esp_zb_meter_identification_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_ELECTRICAL_MEASUREMENT:\n      return esp_zb_electrical_meas_cluster_add_attr(attr_list, attr_id, value_p);\n    case ESP_ZB_ZCL_CLUSTER_ID_DIAGNOSTICS:\n      return esp_zb_diagnostics_cluster_add_attr(attr_list, attr_id, value_p);\n    default:\n      return ESP_FAIL;\n  }\n}\n"
  },
  {
    "path": "components/zigbee/zigbee_helpers.h",
    "content": "#pragma once\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"esp_zigbee_core.h\"\n\nesp_zb_cluster_list_t *esphome_zb_default_clusters_create(esp_zb_ha_standard_devices_t device_type);\n\nesp_err_t esphome_zb_cluster_list_add_or_update_cluster(uint16_t cluster_id, esp_zb_cluster_list_t *cluster_list,\n                                                        esp_zb_attribute_list_t *attr_list, uint8_t role_mask);\n\nesp_zb_attribute_list_t *esphome_zb_default_attr_list_create(uint16_t cluster_id);\nesp_err_t esphome_zb_cluster_add_attr(uint16_t cluster_id, esp_zb_attribute_list_t *attr_list, uint16_t attr_id,\n                                      void *value_p);\nesp_err_t esphome_zb_cluster_add_or_update_attr(uint16_t cluster_id, esp_zb_attribute_list_t *attr_list,\n                                                uint16_t attr_id, uint8_t attr_type, uint8_t attr_access,\n                                                void *value_p);\n\n#ifdef __cplusplus\n}\n\nnamespace esphome {\nnamespace zigbee {}\n}  // namespace esphome\n\n#endif\n"
  },
  {
    "path": "components/zigbee/zigbee_pre_build.py.script",
    "content": "from os import path\nimport re\n\ndef_file = \"./src/esphome/core/defines.h\"\n\nif path.exists(def_file):\n    # open file and replace number of components in ESPHOME_COMPONENT_COUNT with number from ZB_ESPHOME_COMPONENT_COUNT\n    with open(def_file, \"rt\", encoding=\"utf-8\") as f:\n        content = f.read()\n    match = re.search(r\"#define ZB_ESPHOME_COMPONENT_COUNT (\\d+)\", content)\n    if match:\n        zb_count = match.group(1)\n        content = re.sub(r\"#define ESPHOME_COMPONENT_COUNT \\d+\", f\"#define ESPHOME_COMPONENT_COUNT {zb_count}\", content)\n        print(f\"updated ESPHOME_COMPONENT_COUNT: {zb_count}\")\n        # remove ZB_ESPHOME_COMPONENT_COUNT line\n        content = re.sub(r\"#define ZB_ESPHOME_COMPONENT_COUNT \\d+\\n\", \"\", content)\n    else:\n        print(\"ZB_ESPHOME_COMPONENT_COUNT not found\")\n    with open(def_file, \"w\", encoding=\"utf-8\") as f:\n        f.write(content)\n"
  },
  {
    "path": "example_aht10_esp32h2.yaml",
    "content": "esphome:\n  name: zb-sensor\n\nexternal_components:\n  - source: components\n    components: [zigbee]\n\nesp32:\n  board: esp32-h2-devkitm-1\n  #flash_size: 4MB\n  partitions: partitions_zb.csv\n  framework:\n    type: esp-idf\n    #sdkconfig_options:\n      #CONFIG_ESPTOOLPY_FLASHSIZE_4MB: y\n\n# Enable logging\nlogger:\n  hardware_uart: UART0\n\nglobals:\n  - id: color_x\n    type: float\n    restore_value: no\n    initial_value: '0'\n  - id: color_y\n    type: float\n    restore_value: no\n    initial_value: '0'\n\ni2c:\n  sda: 12\n  scl: 22\n  scan: false\n\nsensor:\n  - platform: aht10\n    variant: AHT10\n    temperature:\n      name: \"Living Room Temperature\"\n      id: \"temp\"\n      filters:\n        - delta: 0.1\n    humidity:\n      name: \"Living Room Humidity\"\n      id: \"hum\"\n      filters:\n        - delta: 1\n      on_value:\n        then:\n          - zigbee.setAttr:\n              id: hum_attr\n              value: !lambda \"return x*100;\"\n    update_interval: 60s\n\nzigbee:\n  id: \"zb\"\n  endpoints:\n    - num: 1\n      device_type: COLOR_DIMMABLE_LIGHT\n      clusters:\n        - id: ON_OFF\n          attributes:\n            - attribute_id: 0\n              type: bool\n              on_value:\n                then:\n                  - light.control:\n                      id: light_1\n                      state: !lambda \"return x;\"\n        - id: LEVEL_CONTROL\n          attributes:\n            - attribute_id: 0\n              type: U8\n              value: 255\n              on_value:\n                then:\n                  - light.control:\n                      id: light_1\n                      brightness: !lambda \"return ((float)x)/255;\"\n        - id: COLOR_CONTROL\n          attributes:\n            - attribute_id: 3\n              type: U16\n              on_value:\n                then:\n                  - lambda: id(color_x) = (float)x/65536;\n                  - light.control:\n                      id: light_1\n                      red: !lambda \"return zigbee::get_r_from_xy(id(color_x), id(color_y));\"\n                      green: !lambda \"return zigbee::get_g_from_xy(id(color_x), id(color_y));\"\n                      blue: !lambda \"return zigbee::get_b_from_xy(id(color_x), id(color_y));\"\n            - attribute_id: 4\n              type: U16\n              on_value:\n                then:\n                  - lambda: id(color_y) = (float)x/65536;\n                  - light.control:\n                      id: light_1\n                      red: !lambda \"return zigbee::get_r_from_xy(id(color_x), id(color_y));\"\n                      green: !lambda \"return zigbee::get_g_from_xy(id(color_x), id(color_y));\"\n                      blue: !lambda \"return zigbee::get_b_from_xy(id(color_x), id(color_y));\"\n    - device_type: TEMPERATURE_SENSOR\n      num: 2\n      clusters:\n        - id: REL_HUMIDITY_MEASUREMENT\n          attributes:\n            - attribute_id: 0\n              id: hum_attr\n              type: U16\n              report: true\n              value: 200\n        - id: TEMP_MEASUREMENT\n          attributes:\n            - attribute_id: 0x0\n              type: S16\n              report: true\n              value: 100\n              device: temp\n              scale: 100\n  on_join:\n    then:\n      - logger.log: \"Joined network\"\n\nlight:\n  - platform: esp32_rmt_led_strip\n    rgb_order: GRB\n    pin: 8\n    num_leds: 1\n    #rmt_channel: 0\n    id: light_1\n    chipset: ws2812\n    #bit0_high: 100ns  # rmt clk freq seems to be different on H2\n    #bit0_low: 300ns\n    #bit1_high: 300ns\n    #bit1_low: 100ns\n\nbinary_sensor:\n  - platform: gpio\n    pin:\n      number: 9\n      mode:\n        input: true\n        pullup: true\n      inverted: true\n    id: button_1\n    on_press:\n      then:\n        - zigbee.report: zb\n    on_click:\n      min_length: 5s\n      max_length: 20s\n      then:\n        - zigbee.reset: zb\n"
  },
  {
    "path": "example_esp32c6.yaml",
    "content": "esphome:\n  name: zb-example-c6\n\nexternal_components:\n  - source: components\n    components: [zigbee]\n\nesp32:\n  board: esp32-c6-devkitc-1\n  #flash_size: 4MB\n  partitions: partitions_zb.csv\n  framework:\n    type: esp-idf\n    #sdkconfig_options:\n      #CONFIG_ESPTOOLPY_FLASHSIZE_4MB: y\n\n# Enable logging\nlogger:\n  hardware_uart: UART0\n\nglobals:\n  - id: color_x\n    type: float\n    restore_value: no\n    initial_value: '0'\n  - id: color_y\n    type: float\n    restore_value: no\n    initial_value: '0'\n\nsensor:\n  - platform: internal_temperature\n    name: \"Internal Temperature\"\n    id: \"temp\"\n    filters:\n      - delta: 0.1\n\nzigbee:\n  id: \"zb\"\n  endpoints:\n    - num: 1\n      device_type: COLOR_DIMMABLE_LIGHT\n      clusters:\n        - id: ON_OFF\n          attributes:\n            - attribute_id: 0\n              type: bool\n              on_value:\n                then:\n                  - light.control:\n                      id: light_1\n                      state: !lambda \"return (bool)x;\"\n        - id: LEVEL_CONTROL\n          attributes:\n            - attribute_id: 0\n              type: U8\n              value: 255\n              on_value:\n                then:\n                  - light.control:\n                      id: light_1\n                      brightness: !lambda \"return ((float)x)/255;\"\n        - id: COLOR_CONTROL\n          attributes:\n            - attribute_id: 3\n              type: U16\n              on_value:\n                then:\n                  - lambda: id(color_x) = (float)x/65536;\n                  - light.control:\n                      id: light_1\n                      red: !lambda \"return zigbee::get_r_from_xy(id(color_x), id(color_y));\"\n                      green: !lambda \"return zigbee::get_g_from_xy(id(color_x), id(color_y));\"\n                      blue: !lambda \"return zigbee::get_b_from_xy(id(color_x), id(color_y));\"\n            - attribute_id: 4\n              type: U16\n              on_value:\n                then:\n                  - lambda: id(color_y) = (float)x/65536;\n                  - light.control:\n                      id: light_1\n                      red: !lambda \"return zigbee::get_r_from_xy(id(color_x), id(color_y));\"\n                      green: !lambda \"return zigbee::get_g_from_xy(id(color_x), id(color_y));\"\n                      blue: !lambda \"return zigbee::get_b_from_xy(id(color_x), id(color_y));\"\n    - device_type: TEMPERATURE_SENSOR\n      num: 2\n      clusters:\n        - id: TEMP_MEASUREMENT\n          attributes:\n            - attribute_id: 0x0\n              type: S16\n              report: true\n              value: 100\n              device: temp\n              scale: 100\n  on_join:\n    then:\n      - logger.log: \"Joined network\"\n\nlight:\n  - platform: esp32_rmt_led_strip\n    rgb_order: GRB\n    pin: 8\n    num_leds: 1\n    #rmt_channel: 0\n    chipset: ws2812\n    id: light_1\n\nbinary_sensor:\n  - platform: gpio\n    pin:\n      number: 9\n      mode:\n        input: true\n        pullup: true\n      inverted: true\n    id: button_1\n    on_press:\n      then:\n        - zigbee.report: zb\n    on_click:\n      min_length: 5s\n      max_length: 20s\n      then:\n        - zigbee.reset: zb\n"
  },
  {
    "path": "example_esp32h2.yaml",
    "content": "esphome:\n  name: zb-example-h2\n\nexternal_components:\n  - source: components\n    components: [zigbee]\n\nesp32:\n  board: esp32-h2-devkitm-1\n  #flash_size: 4MB\n  partitions: partitions_zb.csv\n  framework:\n    type: esp-idf\n    #sdkconfig_options:\n      #CONFIG_ESPTOOLPY_FLASHSIZE_4MB: y\n\n# Enable logging\nlogger:\n  hardware_uart: UART0\n\nglobals:\n  - id: color_x\n    type: float\n    restore_value: no\n    initial_value: '0'\n  - id: color_y\n    type: float\n    restore_value: no\n    initial_value: '0'\n\nsensor:\n  - platform: internal_temperature\n    name: \"Internal Temperature\"\n    id: \"temp\"\n    filters:\n      - delta: 0.1\n\nzigbee:\n  id: \"zb\"\n  endpoints:\n    - num: 1\n      device_type: COLOR_DIMMABLE_LIGHT\n      clusters:\n        - id: ON_OFF\n          attributes:\n            - attribute_id: 0\n              type: bool\n              on_value:\n                then:\n                  - light.control:\n                      id: light_1\n                      state: !lambda \"return x;\"\n        - id: LEVEL_CONTROL\n          attributes:\n            - attribute_id: 0\n              type: U8\n              value: 255\n              on_value:\n                then:\n                  - light.control:\n                      id: light_1\n                      brightness: !lambda \"return ((float)x)/255;\"\n        - id: COLOR_CONTROL\n          attributes:\n            - attribute_id: 3\n              type: U16\n              on_value:\n                then:\n                  - lambda: id(color_x) = (float)x/65536;\n                  - light.control:\n                      id: light_1\n                      red: !lambda \"return zigbee::get_r_from_xy(id(color_x), id(color_y));\"\n                      green: !lambda \"return zigbee::get_g_from_xy(id(color_x), id(color_y));\"\n                      blue: !lambda \"return zigbee::get_b_from_xy(id(color_x), id(color_y));\"\n            - attribute_id: 4\n              type: U16\n              on_value:\n                then:\n                  - lambda: id(color_y) = (float)x/65536;\n                  - light.control:\n                      id: light_1\n                      red: !lambda \"return zigbee::get_r_from_xy(id(color_x), id(color_y));\"\n                      green: !lambda \"return zigbee::get_g_from_xy(id(color_x), id(color_y));\"\n                      blue: !lambda \"return zigbee::get_b_from_xy(id(color_x), id(color_y));\"\n    - device_type: TEMPERATURE_SENSOR\n      num: 2\n      clusters:\n        - id: TEMP_MEASUREMENT\n          attributes:\n            - attribute_id: 0x0\n              type: S16\n              report: true\n              value: 100\n              device: temp\n              scale: 100\n  on_join:\n    then:\n      - logger.log: \"Joined network\"\n\nlight:\n  - platform: esp32_rmt_led_strip\n    rgb_order: GRB\n    pin: 8\n    num_leds: 1\n    #rmt_channel: 0\n    id: light_1\n    chipset: ws2812\n    #bit0_high: 100ns  # rmt clk freq seems to be different on H2. Fixed in esphome 2025.02\n    #bit0_low: 300ns\n    #bit1_high: 300ns\n    #bit1_low: 100ns\n\nbinary_sensor:\n  - platform: gpio\n    pin:\n      number: 9\n      mode:\n        input: true\n        pullup: true\n      inverted: true\n    id: button_1\n    on_press:\n      then:\n        - zigbee.report: zb\n    on_click:\n      min_length: 5s\n      max_length: 20s\n      then:\n        - zigbee.reset: zb\n"
  },
  {
    "path": "example_esp32h2_basic.yaml",
    "content": "esphome:\n  name: zb-example-h2\n\nexternal_components:\n  - source: components\n    components: [zigbee]\n\nesp32:\n  board: esp32-h2-devkitm-1\n  partitions: partitions_zb.csv\n  framework:\n    type: esp-idf\n\n# Enable logging\nlogger:\n  hardware_uart: UART0\n\nsensor:\n  - platform: internal_temperature\n    name: \"Internal Temperature\"\n    id: \"temp\"\n    filters:\n      - delta: 0.1\n\nzigbee:\n  id: \"zb\"\n  components: all\n  on_join:\n    then:\n      - logger.log: \"Joined network\"\n\nlight:\n  - platform: esp32_rmt_led_strip\n    rgb_order: GRB\n    pin: 8\n    num_leds: 1\n    id: light_1\n    name: \"my_light\"\n    chipset: ws2812\n\nbinary_sensor:\n  - platform: gpio\n    pin:\n      number: 9\n      mode:\n        input: true\n        pullup: true\n      inverted: true\n    id: button_1\n    name: \"button1\"\n    #on_press:\n    #  then:\n    #    - zigbee.report: zb\n    on_click:\n      min_length: 5s\n      max_length: 20s\n      then:\n        - zigbee.reset: zb\n"
  },
  {
    "path": "example_time.yml",
    "content": "esphome:\n  name: timesync-demo\n\nexternal_components:\n  - source: components\n    components: [zigbee]\n\nesp32:\n  board: esp32-c6-devkitc-1\n  variant: esp32c6\n  flash_size: 8MB\n  partitions: partitions_zb.csv\n  framework:\n    type: esp-idf\n    sdkconfig_options:\n      CONFIG_ESPTOOLPY_FLASHSIZE_8MB: y\n\nlogger:\n\nzigbee:\n  id: \"zb\"\n  endpoints:\n    # We need at least one endpoint defined\n    - device_type: TEMPERATURE_SENSOR\n      num: 1\n      clusters:\n        - id: TEMP_MEASUREMENT\n          attributes:\n            - attribute_id: 0x0\n              type: S16\n              report: true\n              id: zb_temp\n              value: 0\n  on_join:\n    then:\n      - logger.log: \"Joined network\"\n\ntime:\n  - platform: zigbee\n    id: the_time\n    timezone: Europe/London\n    on_time_sync:\n      then:\n        - logger.log: \"Synchronized system clock\"\n    on_time:\n      - seconds: /10\n        then:\n          - logger.log: \"Tick-tock, every 10 seconds\"\n"
  },
  {
    "path": "partitions_zb.csv",
    "content": "otadata,  data, ota,     ,        0x2000,\nphy_init, data, phy,     ,        0x1000,\napp0,     app,  ota_0,   ,        0x1B0000,\napp1,     app,  ota_1,   ,        0x1B0000,\nnvs,      data, nvs,     ,        0x6D000,\nzb_storage, data, fat,   , 16K,\nzb_fct,     data, fat,   , 1K,\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[build-system]\nrequires = [\"setuptools==69.2.0\", \"wheel~=0.43.0\"]\nbuild-backend = \"setuptools.build_meta\"\n\n[project]\nname        = \"esphome\"\nlicense     = {text = \"MIT\"}\ndescription = \"Make creating custom firmwares for ESP32/ESP8266 super easy.\"\nreadme      = \"README.md\"\nauthors     = [\n  {name = \"The ESPHome Authors\", email = \"esphome@nabucasa.com\"}\n]\nkeywords    = [\"home\", \"automation\"]\nclassifiers = [\n    \"Environment :: Console\",\n    \"Intended Audience :: Developers\",\n    \"Intended Audience :: End Users/Desktop\",\n    \"License :: OSI Approved :: MIT License\",\n    \"Programming Language :: C++\",\n    \"Programming Language :: Python :: 3\",\n    \"Topic :: Home Automation\",\n]\nrequires-python = \">=3.9.0\"\n\ndynamic = [\"dependencies\", \"optional-dependencies\", \"version\"]\n\n[project.urls]\n\"Documentation\"           = \"https://esphome.io\"\n\"Source Code\"             = \"https://github.com/esphome/esphome\"\n\"Bug Tracker\"             = \"https://github.com/esphome/issues/issues\"\n\"Feature Request Tracker\" = \"https://github.com/esphome/feature-requests/issues\"\n\"Discord\"                 = \"https://discord.gg/KhAMKrd\"\n\"Forum\"                   = \"https://community.home-assistant.io/c/esphome\"\n\"Twitter\"                 = \"https://twitter.com/esphome_\"\n\n[project.scripts]\nesphome = \"esphome.__main__:main\"\n\n[tool.setuptools]\nplatforms            = [\"any\"]\nzip-safe             = false\ninclude-package-data = true\n\n[tool.setuptools.dynamic]\ndependencies = {file = [\"requirements.txt\"]}\noptional-dependencies.dev = { file = [\"requirements_dev.txt\"] }\noptional-dependencies.test = { file = [\"requirements_test.txt\"] }\noptional-dependencies.displays = { file = [\"requirements_optional.txt\"] }\nversion = {attr = \"esphome.const.__version__\"}\n\n[tool.setuptools.packages.find]\ninclude = [\"esphome*\"]\n\n[tool.black]\ntarget-version = [\"py39\", \"py310\"]\nexclude = 'generated'\n\n[tool.pytest.ini_options]\ntestpaths = [\n  \"tests\",\n]\naddopts = [\n  \"--cov=esphome\",\n  \"--cov-branch\",\n]\n\n[tool.pylint.MAIN]\npy-version = \"3.9\"\nignore = [\n  \"api_pb2.py\",\n]\npersistent = false\n\n[tool.pylint.REPORTS]\nscore = false\n\n[tool.pylint.\"MESSAGES CONTROL\"]\ndisable = [\n  \"format\",\n  \"missing-docstring\",\n  \"fixme\",\n  \"unused-argument\",\n  \"global-statement\",\n  \"too-few-public-methods\",\n  \"too-many-lines\",\n  \"too-many-locals\",\n  \"too-many-ancestors\",\n  \"too-many-branches\",\n  \"too-many-statements\",\n  \"too-many-arguments\",\n  \"too-many-return-statements\",\n  \"too-many-instance-attributes\",\n  \"duplicate-code\",\n  \"invalid-name\",\n  \"cyclic-import\",\n  \"redefined-builtin\",\n  \"undefined-loop-variable\",\n  \"useless-object-inheritance\",\n  \"stop-iteration-return\",\n  \"import-outside-toplevel\",\n  # Broken\n  \"unsupported-membership-test\",\n  \"unsubscriptable-object\",\n]\n\n[tool.pylint.FORMAT]\nexpected-line-ending-format = \"LF\"\n\n[tool.ruff]\nrequired-version = \">=0.5.0\"\n\n[tool.ruff.lint]\nselect = [\n  \"E\", # pycodestyle\n  \"F\", # pyflakes/autoflake\n  \"I\", # isort\n  \"PL\", # pylint\n  \"UP\", # pyupgrade\n]\n\nignore = [\n  \"E501\", # line too long\n  \"PLR0911\", # Too many return statements ({returns} > {max_returns})\n  \"PLR0912\", # Too many branches ({branches} > {max_branches})\n  \"PLR0913\", # Too many arguments to function call ({c_args} > {max_args})\n  \"PLR0915\", # Too many statements ({statements} > {max_statements})\n  \"PLR2004\", # Magic value used in comparison, consider replacing {value} with a constant variable\n  \"PLW2901\", # Outer {outer_kind} variable {name} overwritten by inner {inner_kind} target\n]\n\n[tool.ruff.lint.isort]\nforce-sort-within-sections = true\nknown-first-party = [\n  \"esphome\",\n]\ncombine-as-imports = true\nsplit-on-trailing-comma = false\n"
  }
]