├── .clang-format ├── .github ├── FUNDING.yml ├── actions │ └── restore-python │ │ └── action.yml └── workflows │ ├── ci.yaml │ └── matchers │ ├── ci-custom.json │ ├── clang-tidy.json │ ├── gcc.json │ ├── lint-python.json │ └── python.json ├── .gitignore ├── .yamllint ├── LICENSE ├── README.md ├── coordinator-example.yaml ├── docs ├── .gitkeep ├── flashing.md └── router.md ├── images ├── 001.jpg ├── 002.jpg ├── 003.jpg ├── 004.jpg ├── 005.jpg ├── 006.jpg ├── 007.jpg ├── 008.jpg ├── 009.jpg ├── 010.jpg ├── zb-gw03-v1_3.jpg ├── zha-paired-router.png └── zigbee2mqtt-serial-settings.png ├── packages ├── button_pairing_mode.yaml ├── button_zigbee_reset.yaml ├── core.yaml ├── ethernet.yaml ├── green_led.yaml ├── i2c.yaml ├── mdns.yaml ├── status_led.yaml └── wifi.yaml └── router-example.yaml /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | AccessModifierOffset: -1 3 | AlignAfterOpenBracket: Align 4 | AlignConsecutiveAssignments: false 5 | AlignConsecutiveDeclarations: false 6 | AlignEscapedNewlines: DontAlign 7 | AlignOperands: true 8 | AlignTrailingComments: true 9 | AllowAllParametersOfDeclarationOnNextLine: true 10 | AllowShortBlocksOnASingleLine: false 11 | AllowShortCaseLabelsOnASingleLine: false 12 | AllowShortFunctionsOnASingleLine: All 13 | AllowShortIfStatementsOnASingleLine: false 14 | AllowShortLoopsOnASingleLine: false 15 | AlwaysBreakAfterReturnType: None 16 | AlwaysBreakBeforeMultilineStrings: false 17 | AlwaysBreakTemplateDeclarations: MultiLine 18 | BinPackArguments: true 19 | BinPackParameters: true 20 | BraceWrapping: 21 | AfterClass: false 22 | AfterControlStatement: false 23 | AfterEnum: false 24 | AfterFunction: false 25 | AfterNamespace: false 26 | AfterObjCDeclaration: false 27 | AfterStruct: false 28 | AfterUnion: false 29 | AfterExternBlock: false 30 | BeforeCatch: false 31 | BeforeElse: false 32 | IndentBraces: false 33 | SplitEmptyFunction: true 34 | SplitEmptyRecord: true 35 | SplitEmptyNamespace: true 36 | BreakBeforeBinaryOperators: None 37 | BreakBeforeBraces: Attach 38 | BreakBeforeInheritanceComma: false 39 | BreakInheritanceList: BeforeColon 40 | BreakBeforeTernaryOperators: true 41 | BreakConstructorInitializersBeforeComma: false 42 | BreakConstructorInitializers: BeforeColon 43 | BreakAfterJavaFieldAnnotations: false 44 | BreakStringLiterals: true 45 | ColumnLimit: 120 46 | CommentPragmas: "^ IWYU pragma:" 47 | CompactNamespaces: false 48 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 49 | ConstructorInitializerIndentWidth: 4 50 | ContinuationIndentWidth: 4 51 | Cpp11BracedListStyle: true 52 | DerivePointerAlignment: true 53 | DisableFormat: false 54 | ExperimentalAutoDetectBinPacking: false 55 | FixNamespaceComments: true 56 | ForEachMacros: 57 | - foreach 58 | - Q_FOREACH 59 | - BOOST_FOREACH 60 | IncludeBlocks: Preserve 61 | IncludeCategories: 62 | - Regex: '^' 63 | Priority: 2 64 | - Regex: '^<.*\.h>' 65 | Priority: 1 66 | - Regex: "^<.*" 67 | Priority: 2 68 | - Regex: ".*" 69 | Priority: 3 70 | IncludeIsMainRegex: "([-_](test|unittest))?$" 71 | IndentCaseLabels: true 72 | IndentPPDirectives: None 73 | IndentWidth: 2 74 | IndentWrappedFunctionNames: false 75 | KeepEmptyLinesAtTheStartOfBlocks: false 76 | MacroBlockBegin: "" 77 | MacroBlockEnd: "" 78 | MaxEmptyLinesToKeep: 1 79 | NamespaceIndentation: None 80 | PenaltyBreakAssignment: 2 81 | PenaltyBreakBeforeFirstCallParameter: 1 82 | PenaltyBreakComment: 300 83 | PenaltyBreakFirstLessLess: 120 84 | PenaltyBreakString: 1000 85 | PenaltyBreakTemplateDeclaration: 10 86 | PenaltyExcessCharacter: 1000000 87 | PenaltyReturnTypeOnItsOwnLine: 2000 88 | PointerAlignment: Right 89 | RawStringFormats: 90 | - Language: Cpp 91 | Delimiters: 92 | - cc 93 | - CC 94 | - cpp 95 | - Cpp 96 | - CPP 97 | - "c++" 98 | - "C++" 99 | CanonicalDelimiter: "" 100 | BasedOnStyle: google 101 | - Language: TextProto 102 | Delimiters: 103 | - pb 104 | - PB 105 | - proto 106 | - PROTO 107 | EnclosingFunctions: 108 | - EqualsProto 109 | - EquivToProto 110 | - PARSE_PARTIAL_TEXT_PROTO 111 | - PARSE_TEST_PROTO 112 | - PARSE_TEXT_PROTO 113 | - ParseTextOrDie 114 | - ParseTextProtoOrDie 115 | CanonicalDelimiter: "" 116 | BasedOnStyle: google 117 | ReflowComments: true 118 | SortIncludes: false 119 | SortUsingDeclarations: false 120 | SpaceAfterCStyleCast: true 121 | SpaceAfterTemplateKeyword: false 122 | SpaceBeforeAssignmentOperators: true 123 | SpaceBeforeCpp11BracedList: false 124 | SpaceBeforeCtorInitializerColon: true 125 | SpaceBeforeInheritanceColon: true 126 | SpaceBeforeParens: ControlStatements 127 | SpaceBeforeRangeBasedForLoopColon: true 128 | SpaceInEmptyParentheses: false 129 | SpacesBeforeTrailingComments: 2 130 | SpacesInAngles: false 131 | SpacesInContainerLiterals: false 132 | SpacesInCStyleCastParentheses: false 133 | SpacesInParentheses: false 134 | SpacesInSquareBrackets: false 135 | Standard: Auto 136 | TabWidth: 2 137 | UseTab: Never 138 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | buy_me_a_coffee: syssi 2 | -------------------------------------------------------------------------------- /.github/actions/restore-python/action.yml: -------------------------------------------------------------------------------- 1 | name: Restore Python 2 | inputs: 3 | python-version: 4 | description: Python version to restore 5 | required: true 6 | type: string 7 | cache-key: 8 | description: Cache key to use 9 | required: true 10 | type: string 11 | outputs: 12 | python-version: 13 | description: Python version restored 14 | value: ${{ steps.python.outputs.python-version }} 15 | runs: 16 | using: "composite" 17 | steps: 18 | - name: Set up Python ${{ inputs.python-version }} 19 | id: python 20 | uses: actions/setup-python@v5.6.0 21 | with: 22 | python-version: ${{ inputs.python-version }} 23 | - name: Restore Python virtual environment 24 | id: cache-venv 25 | uses: actions/cache/restore@v4.2.3 26 | with: 27 | path: venv 28 | # yamllint disable-line rule:line-length 29 | key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ inputs.cache-key }} 30 | - name: Create Python virtual environment 31 | shell: bash 32 | run: | 33 | python -m venv venv 34 | source venv/bin/activate 35 | python --version 36 | cd esphome 37 | pip install -r requirements.txt -r requirements_test.txt 38 | pip install -e . 39 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: # yamllint disable-line rule:truthy 4 | push: 5 | branches: 6 | - main 7 | 8 | pull_request: 9 | schedule: 10 | - cron: 0 12 * * * 11 | 12 | permissions: 13 | contents: read 14 | 15 | env: 16 | FORCE_COLOR: 1 17 | DEFAULT_PYTHON: "3.10" 18 | PYUPGRADE_TARGET: "--py310-plus" 19 | 20 | concurrency: 21 | # yamllint disable-line rule:line-length 22 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 23 | cancel-in-progress: true 24 | 25 | jobs: 26 | yamllint: 27 | runs-on: ubuntu-24.04 28 | steps: 29 | - uses: actions/checkout@v4 30 | - name: Run yamllint 31 | uses: frenck/action-yamllint@v1 32 | with: 33 | config: .yamllint 34 | 35 | bundle: 36 | name: Bundle external component and ESPHome 37 | runs-on: ubuntu-24.04 38 | outputs: 39 | repo-hash: ${{ github.sha }} 40 | steps: 41 | - name: Check out this project 42 | uses: actions/checkout@v4.1.7 43 | 44 | - name: Check out code from ESPHome project 45 | uses: actions/checkout@v4.1.7 46 | with: 47 | repository: esphome/esphome 48 | ref: dev 49 | path: esphome 50 | 51 | - name: Copy external component into the esphome project 52 | run: | 53 | cd esphome 54 | ln -sf ../venv venv 55 | 56 | - name: Archive prepared repository 57 | uses: pyTooling/upload-artifact@v4 58 | with: 59 | name: bundle 60 | path: . 61 | include-hidden-files: true 62 | retention-days: 1 63 | 64 | common: 65 | name: Create common environment 66 | runs-on: ubuntu-24.04 67 | needs: bundle 68 | outputs: 69 | cache-key: ${{ steps.cache-key.outputs.key }} 70 | steps: 71 | - name: Download prepared repository 72 | uses: pyTooling/download-artifact@v4 73 | with: 74 | name: bundle 75 | path: . 76 | - name: Update index to make "git diff-index" happy 77 | run: git update-index -q --really-refresh 78 | 79 | - name: Generate cache-key 80 | id: cache-key 81 | run: echo key="${{ hashFiles('esphome/requirements.txt', 'esphome/requirements_test.txt') }}" >> $GITHUB_OUTPUT 82 | - name: Set up Python ${{ env.DEFAULT_PYTHON }} 83 | id: python 84 | uses: actions/setup-python@v5.6.0 85 | with: 86 | python-version: ${{ env.DEFAULT_PYTHON }} 87 | - name: Restore Python virtual environment 88 | id: cache-venv 89 | uses: actions/cache@v4.2.3 90 | with: 91 | path: venv 92 | # yamllint disable-line rule:line-length 93 | key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ steps.cache-key.outputs.key }} 94 | 95 | - name: Create Python virtual environment 96 | if: steps.cache-venv.outputs.cache-hit != 'true' 97 | run: | 98 | python -m venv venv 99 | . venv/bin/activate 100 | python --version 101 | cd esphome 102 | pip install -r requirements.txt -r requirements_test.txt 103 | pip install -e . 104 | 105 | esphome-config: 106 | name: Validate example configurations 107 | runs-on: ubuntu-24.04 108 | needs: 109 | - bundle 110 | - common 111 | steps: 112 | - name: Download prepared repository 113 | uses: pyTooling/download-artifact@v4 114 | with: 115 | name: bundle 116 | path: . 117 | 118 | - name: Restore Python 119 | uses: ./.github/actions/restore-python 120 | with: 121 | python-version: ${{ env.DEFAULT_PYTHON }} 122 | cache-key: ${{ needs.common.outputs.cache-key }} 123 | 124 | - name: Validate example configurations 125 | run: | 126 | . venv/bin/activate 127 | for YAML in *example.yaml; do 128 | if [ "${{ github.event_name }}" = "pull_request" ]; then 129 | sed -i "s#ref: main#ref: ${{ github.event.pull_request.head.ref }}#" $YAML 130 | fi 131 | esphome config $YAML >/dev/null 132 | done 133 | 134 | esphome-compile: 135 | name: Build example configurations 136 | runs-on: ubuntu-24.04 137 | needs: 138 | - bundle 139 | - common 140 | steps: 141 | - name: Download prepared repository 142 | uses: pyTooling/download-artifact@v4 143 | with: 144 | name: bundle 145 | path: . 146 | 147 | - name: Restore Python 148 | uses: ./.github/actions/restore-python 149 | with: 150 | python-version: ${{ env.DEFAULT_PYTHON }} 151 | cache-key: ${{ needs.common.outputs.cache-key }} 152 | 153 | - name: Compile example configurations 154 | run: | 155 | . venv/bin/activate 156 | for YAML in *example.yaml; do 157 | if [ "${{ github.event_name }}" = "pull_request" ]; then 158 | sed -i "s#ref: main#ref: ${{ github.event.pull_request.head.ref }}#" $YAML 159 | fi 160 | esphome compile $YAML >/dev/null 161 | done 162 | -------------------------------------------------------------------------------- /.github/workflows/matchers/ci-custom.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "ci-custom", 5 | "pattern": [ 6 | { 7 | "regexp": "^ERROR (.*):(\\d+):(\\d+) - (.*)$", 8 | "file": 1, 9 | "line": 2, 10 | "column": 3, 11 | "message": 4 12 | } 13 | ] 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.github/workflows/matchers/clang-tidy.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "clang-tidy", 5 | "pattern": [ 6 | { 7 | "regexp": "^(.*):(\\d+):(\\d+):\\s+(error):\\s+(.*) \\[([a-z0-9,\\-]+)\\]\\s*$", 8 | "file": 1, 9 | "line": 2, 10 | "column": 3, 11 | "severity": 4, 12 | "message": 5 13 | } 14 | ] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /.github/workflows/matchers/gcc.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "gcc", 5 | "severity": "error", 6 | "pattern": [ 7 | { 8 | "regexp": "^(.*):(\\d+):(\\d+):\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$", 9 | "file": 1, 10 | "line": 2, 11 | "column": 3, 12 | "severity": 4, 13 | "message": 5 14 | } 15 | ] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.github/workflows/matchers/lint-python.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "flake8", 5 | "severity": "error", 6 | "pattern": [ 7 | { 8 | "regexp": "^(.*):(\\d+) - ([EFCDNW]\\d{3}.*)$", 9 | "file": 1, 10 | "line": 2, 11 | "message": 3 12 | } 13 | ] 14 | }, 15 | { 16 | "owner": "pylint", 17 | "severity": "error", 18 | "pattern": [ 19 | { 20 | "regexp": "^(.*):(\\d+) - (\\[[EFCRW]\\d{4}\\(.*\\),.*\\].*)$", 21 | "file": 1, 22 | "line": 2, 23 | "message": 3 24 | } 25 | ] 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/matchers/python.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "python", 5 | "pattern": [ 6 | { 7 | "regexp": "^\\s*File\\s\\\"(.*)\\\",\\sline\\s(\\d+),\\sin\\s(.*)$", 8 | "file": 1, 9 | "line": 2 10 | }, 11 | { 12 | "regexp": "^\\s*raise\\s(.*)\\(\\'(.*)\\'\\)$", 13 | "message": 2 14 | } 15 | ] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | secrets.yaml 3 | .esphome/ 4 | **/.pioenvs/ 5 | **/.piolibdeps/ 6 | **/lib/ 7 | **/src/ 8 | **/partitions.csv 9 | 10 | __pycache__/ 11 | *.py[cod] 12 | *$py.class 13 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | extends: default 2 | 3 | yaml-files: 4 | - '*.yaml' 5 | - '*.yml' 6 | - '.yamllint' 7 | 8 | ignore: | 9 | .clang-format 10 | .esphome/ 11 | tests/.esphome/ 12 | 13 | rules: 14 | braces: 15 | level: error 16 | min-spaces-inside: 0 17 | max-spaces-inside: 1 18 | min-spaces-inside-empty: -1 19 | max-spaces-inside-empty: -1 20 | brackets: 21 | level: error 22 | min-spaces-inside: 0 23 | max-spaces-inside: 0 24 | min-spaces-inside-empty: -1 25 | max-spaces-inside-empty: -1 26 | colons: 27 | level: error 28 | max-spaces-before: 0 29 | max-spaces-after: 1 30 | commas: 31 | level: error 32 | max-spaces-before: 0 33 | min-spaces-after: 1 34 | max-spaces-after: 1 35 | comments: 36 | level: error 37 | require-starting-space: true 38 | min-spaces-from-content: 2 39 | comments-indentation: disable 40 | document-end: 41 | level: error 42 | present: false 43 | document-start: 44 | level: error 45 | present: false 46 | empty-lines: 47 | level: error 48 | max: 2 49 | max-start: 0 50 | max-end: 1 51 | hyphens: 52 | level: error 53 | max-spaces-after: 1 54 | indentation: 55 | level: error 56 | spaces: 2 57 | indent-sequences: true 58 | check-multi-line-strings: false 59 | key-duplicates: 60 | level: error 61 | line-length: disable 62 | new-line-at-end-of-file: 63 | level: error 64 | new-lines: 65 | level: error 66 | type: unix 67 | trailing-spaces: 68 | level: error 69 | truthy: 70 | level: error 71 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esphome-zb-gw03 2 | 3 | ![GitHub actions](https://github.com/syssi/esphome-zb-gw03/actions/workflows/ci.yaml/badge.svg) 4 | ![GitHub stars](https://img.shields.io/github/stars/syssi/esphome-zb-gw03) 5 | ![GitHub forks](https://img.shields.io/github/forks/syssi/esphome-zb-gw03) 6 | ![GitHub watchers](https://img.shields.io/github/watchers/syssi/esphome-zb-gw03) 7 | [!["Buy Me A Coffee"](https://img.shields.io/badge/buy%20me%20a%20coffee-donate-yellow.svg)](https://www.buymeacoffee.com/syssi) 8 | 9 | ESPHome custom firmware for ZB-GW03 eWeLink Ethernet Zigbee Gateway 10 | 11 | ## Compatible hardware 12 | 13 | ZB-GW03 Zigbee to LAN bridge/gateway based on Espressif ESP32 and a Silicon Labs EFR32MG21 Zigbee radio (CoolKit-Technologies "SM-011 V1.0" module). 14 | 15 | ### Supported devices 16 | 17 | * ZB-GW03 eWeLink Ethernet Zigbee Gateway (ZB-GW03-V1.0, ZB-GW03-V1.2, ZB-GW03-V1.3, ZB-GW03-V1.4), also sold rebranded as: 18 | * Eachen eWeLink ZigBee Smart Hub 19 | * https://ewelink.eachen.cc/product/eachen-ewelink-zigbee-bridge-pro-zbbridge-pro/ 20 | * https://ewelinkcommunity.net/device-lists/zigbee/eachen-zb-gw03/ 21 | * SmartWise Zigbee Bridge Pro 22 | * https://www.okosabbotthon.hu/smartwise-zigbee-bridge-pro 23 | 24 | ## Requirements 25 | 26 | * [ESPHome 2024.6.0](https://github.com/esphome/esphome/releases) 27 | 28 | ## Quick start guides 29 | 30 | * [Flash ESPHome and use the ZB-GW03 as Zigbee Coordinator](docs/flashing.md) 31 | * [How to convert from Zigbee Coordinator into a Zigbee Router device](docs/router.md) 32 | 33 | ## Known issues 34 | 35 | None. 36 | 37 | ## References 38 | 39 | * https://templates.blakadder.com/ewelink_ZB-GW03 40 | * https://github.com/arendst/Tasmota/discussions/12764 41 | * https://github.com/arendst/Tasmota/discussions/14168 42 | * https://github.com/arendst/Tasmota/discussions/11536 43 | * https://github.com/arendst/Tasmota/tree/development/tools/fw_SonoffZigbeeBridge_ezsp 44 | * https://github.com/xsp1989/zigbeeFirmware 45 | * https://github.com/tube0013/tube_gateways 46 | * https://github.com/oxan/esphome-stream-server 47 | * https://github.com/thegroove/esphome-serial-server 48 | * https://github.com/thegroove/esphome-zbbridge 49 | * https://github.com/thegroove/esphome-zeroconf 50 | * https://github.com/thegroove/esphome-zha-ezsp-zeroconf 51 | * [https://github.com/CoolKit-Technologies/DevDocs/blob/master/Zigbee/SM-011应用指导书.md](https://github.com/CoolKit-Technologies/DevDocs/blob/master/Zigbee/SM-011%E5%BA%94%E7%94%A8%E6%8C%87%E5%AF%BC%E4%B9%A6.md) 52 | * https://github.com/zigpy/zigpy/discussions/586 53 | * [https://community.home-assistant.io/t/zb-gw03-ewelink-ethernet-zigbee-gateway...](https://community.home-assistant.io/t/zb-gw03-ewelink-ethernet-zigbee-gateway-now-hacked-with-tasmota-zbbridge-so-can-be-used-via-mqtt-or-as-a-remote-zigbee-adapter-with-home-assistant-zha/341223) 54 | * https://github.com/vahempio/Tasmota-for-eWeLink 55 | * https://thehelpfulidiot.com/a-wired-sonoff-zigbee-alternative 56 | * https://thehelpfulidiot.com/update-a-wired-sonoff-zigbee-alternative 57 | * https://thehelpfulidiot.com/update-2-a-wired-sonoff-zigbee-alternative 58 | * https://templates.blakadder.com/sonoff_ZBBridge.html 59 | * https://www.digiblur.com/2020/07/how-to-use-sonoff-zigbee-bridge-with.html 60 | * https://www.digiblur.com/2021/03/zigbee2mqtt-with-sonoff-zigbee-bridge.html 61 | * https://www.digiblur.com/2022/02/convert-sonoff-zigbee-bridge-to-router-repeater.html 62 | -------------------------------------------------------------------------------- /coordinator-example.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: zb-gw03-coordinator 3 | device_description: "ZB-GW03 eWeLink Ethernet Zigbee Gateway based Zigbee Coordinator" 4 | device_role: "coordinator" 5 | 6 | packages: 7 | zb-gw03: 8 | url: https://github.com/syssi/esphome-zb-gw03 9 | ref: main 10 | files: 11 | - packages/core.yaml 12 | - packages/ethernet.yaml 13 | - packages/status_led.yaml 14 | - packages/green_led.yaml 15 | - packages/i2c.yaml 16 | - packages/button_zigbee_reset.yaml 17 | - packages/mdns.yaml 18 | refresh: 0s 19 | -------------------------------------------------------------------------------- /docs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syssi/esphome-zb-gw03/0dd8042033f31cbea2a641a6ad8e546fcbf72167/docs/.gitkeep -------------------------------------------------------------------------------- /docs/flashing.md: -------------------------------------------------------------------------------- 1 | # Flashing guide 2 | 3 | ![GitHub actions](https://github.com/syssi/esphome-zb-gw03/actions/workflows/ci.yaml/badge.svg) 4 | ![GitHub stars](https://img.shields.io/github/stars/syssi/esphome-zb-gw03) 5 | ![GitHub forks](https://img.shields.io/github/forks/syssi/esphome-zb-gw03) 6 | ![GitHub watchers](https://img.shields.io/github/watchers/syssi/esphome-zb-gw03) 7 | [!["Buy Me A Coffee"](https://img.shields.io/badge/buy%20me%20a%20coffee-donate-yellow.svg)](https://www.buymeacoffee.com/syssi) 8 | 9 | Table of contents: 10 | * [Warning](#warning) 11 | * [Tools needed](#tools-needed) 12 | * [Opening the device, to expose the PCB](#opening-the-case-to-expose-the-pcb) 13 | * [Solder wires to the board](#solder-wires-to-the-board) 14 | * [Connect the wires to your serial to USB adapter](#connect-the-wires-to-your-serial-to-usb-adapter) 15 | * [Make a backup of the current firmware](#make-a-backup-of-the-current-firmware) 16 | * [How to restore the backed up firmware](#how-to-restore-the-backed-up-firmware) 17 | * [Flash new ESPHome firmware](#flash-esphome-firmware) 18 | * [Flash the ZigBee module](#flash-the-zigbee-module) 19 | * [Troubleshooting flash](#troubleshooting-flash) 20 | 21 | ## Warning 22 | 23 | We have writen these instructions with care, but we will give absolutely no warranty. Perhaps you 24 | will destroy your lamp and your computer. 25 | 26 | ## Tools needed 27 | 28 | * Something flat f.e. a guitar clip to open the case 29 | * Soldering Iron 30 | * A serial to USB adapter (for example FTDI) that can provide 3.3V RX/TX signals 31 | * Some wires 32 | 33 | 34 | ## Opening the case, to expose the PCB 35 | 36 | *Tip: you can click on all images from below to view them in full size.* 37 | 38 | Detach the bottom lid from the rest of the device, exposing the PCB. This might take a bit of force. Just pull it off bit by bit, until it pops loose. 39 | 40 | 41 | 42 | 43 | 44 | 45 | ## Solder wires to the board 46 | 47 | The wires will be connected to the debug pads that are shown in the following image. Solder the wires to the `RX`, `TX`, `GND`, `3.3V` and `GPIO0` debug pads. 48 | 49 | 50 | 51 | ## Connect the wires to your serial to USB adapter 52 | 53 | Make sure that your adapter uses 3.3V for the RX/TX pins that you will connect to the device. Some of 54 | these adapters allow you to switch between 3.3V and 5V using a switch or a jumper. Do not use an 55 | adapter that only provides 5V output. Reason for this, is that the ESP32 chip works at 3.3V. I have 56 | seen the chips accept 5V serial input (I did flash the lamp successfully like that), but I am not 57 | sure if they are actually 5V tolerant. Better safe than sorry in such case! 58 | 59 | The wires must be connected as follows: 60 | 61 | | Soldering point| Serial USB Adapter name | 62 | | -------------- |:------------------------:| 63 | | GND | GND | 64 | | TX | RX | 65 | | RX | TX | 66 | | 3.3V | 3.3V | 67 | 68 | To be able to flash the device, `GPIO0` must be connected to ground (`GND`) while the device boots up. 69 | Flashing will *not* work if you connect these wires *after* the device has already been booted up. 70 | In the following images, you will see one solution, using male dupont wires. 71 | 72 | 73 | 74 | You can now connect the serial to USB adapter to your computer. Pay special attention to the 75 | cross-over of the TX/RX pair (TX connects to RX and vice versa). 76 | 77 | 78 | 79 | Because `GPIO0` is connected to `GND`, the device should start up in download mode. 80 | 81 | ## Make a backup of the current firmware 82 | 83 | Backing up the firmware makes it possible to revert to the original firmware, in case you have 84 | problems with the ESPHome firmware. The backup can be created using "esptool". Installation 85 | instructures can be found here: 86 | 87 | https://github.com/espressif/esptool/blob/master/README.md#installation--dependencies 88 | 89 | Here's an example on how to backup the original firmware by the esptool read_flash command: 90 | 91 | ``` 92 | $ python esptool.py -p /dev/ttyUSB0 read_flash 0x0 0x400000 oem-firmware.bin 93 | esptool.py v3.2 94 | Serial port /dev/ttyUSB0 95 | Connecting...... 96 | Detecting chip type... Unsupported detection protocol, switching and trying again... 97 | Connecting... 98 | Detecting chip type... ESP32 99 | Chip is ESP32-D0WDQ6-V3 (revision 3) 100 | Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None 101 | Crystal is 40MHz 102 | MAC: 94:b9:7e:d3:fc:64 103 | Uploading stub... 104 | Running stub... 105 | Stub running... 106 | 4194304 (100 %) 107 | 4194304 (100 %) 108 | Read 4194304 bytes at 0x0 in 392.6 seconds (85.5 kbit/s)... 109 | Hard resetting via RTS pin... 110 | ``` 111 | 112 | `/dev/ttyUSB0` is the port of the USB adaper on Linux. You can find what port is used by the adapter 113 | by running `dmesg` after plugging in the USB device. On Windows this is often `COM1`, `COM2` or 114 | `COM3`. 115 | 116 | ## How to restore the backed up firmware 117 | 118 | In case you need to rollback to the gateway's original firmware at some point, here's an example of how 119 | to restore the original firmware, by fully flashing it back onto the device. 120 | 121 | First, bring the device into the download mode (`GPIO0` is connected to `GND`), then start the esptool write_flash command: 122 | 123 | ``` 124 | python esptool.py -p /dev/ttyUSB0 write_flash 0x0 oem-firmware.bin 125 | ``` 126 | 127 | Be patient after the upload reaches 100%. The output is silent while esptool tool is verifying that 128 | the firmware was uploaded correctly. 129 | 130 | ## Flash ESPHome firmware 131 | 132 | Setup an ESPHome Project (see [README.md](../README.md)), compile the firmware for the gateway and 133 | flash the firmware to the device: 134 | 135 | ``` 136 | $ esphome run coordinator-example.yaml 137 | INFO Reading configuration coordinator-example.yaml... 138 | WARNING GPIO4 is a Strapping PIN and should be avoided. 139 | Attaching external pullup/down resistors to strapping pins can cause unexpected failures. 140 | See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins 141 | WARNING GPIO2 is a Strapping PIN and should be avoided. 142 | Attaching external pullup/down resistors to strapping pins can cause unexpected failures. 143 | See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins 144 | WARNING GPIO15 is a Strapping PIN and should be avoided. 145 | Attaching external pullup/down resistors to strapping pins can cause unexpected failures. 146 | See https://esphome.io/guides/faq.html#why-am-i-getting-a-warning-about-strapping-pins 147 | INFO Generating C++ source... 148 | INFO Compiling app... 149 | Processing example (board: esp-wrover-kit; framework: arduino; platform: platformio/espressif32 @ 3.3.2) 150 | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 151 | HARDWARE: ESP32 240MHz, 320KB RAM, 4MB Flash 152 | LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf 153 | Dependency Graph 154 | |-- 1.2.2 155 | |-- 1.0 156 | |-- 1.0 157 | |-- 1.0.1 158 | |-- 1.0 159 | Compiling .pioenvs/example/src/main.cpp.o 160 | Linking .pioenvs/example/firmware.elf 161 | RAM: [= ] 12.9% (used 42344 bytes from 327680 bytes) 162 | Flash: [===== ] 47.8% (used 876550 bytes from 1835008 bytes) 163 | Building .pioenvs/example/firmware.bin 164 | ====================================================================== [SUCCESS] Took 6.93 seconds ====================================================================== 165 | INFO Successfully compiled program. 166 | Found multiple options, please choose one: 167 | [1] /dev/ttyUSB0 (FT232R USB UART - FT232R USB UART) 168 | [2] Over The Air (example.local) 169 | (number): 1 170 | esptool.py v3.2 171 | Serial port /dev/ttyUSB0 172 | Connecting..... 173 | Chip is ESP32-D0WDQ6-V3 (revision 3) 174 | Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None 175 | Crystal is 40MHz 176 | MAC: 94:b9:7e:d3:fc:64 177 | Uploading stub... 178 | Running stub... 179 | Stub running... 180 | Changing baud rate to 460800 181 | Changed. 182 | Configuring flash size... 183 | Auto-detected Flash size: 4MB 184 | Flash will be erased from 0x00010000 to 0x000e6fff... 185 | Flash will be erased from 0x00001000 to 0x00005fff... 186 | Flash will be erased from 0x00008000 to 0x00008fff... 187 | Flash will be erased from 0x0000e000 to 0x0000ffff... 188 | Compressed 876656 bytes to 495708... 189 | Wrote 876656 bytes (495708 compressed) at 0x00010000 in 12.4 seconds (effective 567.3 kbit/s)... 190 | Hash of data verified. 191 | Compressed 17104 bytes to 11191... 192 | Wrote 17104 bytes (11191 compressed) at 0x00001000 in 0.6 seconds (effective 219.5 kbit/s)... 193 | Hash of data verified. 194 | Compressed 3072 bytes to 144... 195 | Wrote 3072 bytes (144 compressed) at 0x00008000 in 0.1 seconds (effective 307.7 kbit/s)... 196 | Hash of data verified. 197 | Compressed 8192 bytes to 47... 198 | Wrote 8192 bytes (47 compressed) at 0x0000e000 in 0.1 seconds (effective 455.4 kbit/s)... 199 | Hash of data verified. 200 | 201 | Leaving... 202 | Hard resetting via RTS pin... 203 | INFO Successfully uploaded program. 204 | INFO Starting log output from /dev/ttyUSB0 with baud rate 115200 205 | ``` 206 | 207 | From here on, it is possible to flash the device OTA (over the air, which means that the firmware is 208 | uploaded over WiFi) from ESPHome. Therefore, it is now time to tuck away or remove those soldered 209 | wires. 210 | 211 | ## Add the ESPHome node to Home Assistant 212 | 213 | * Add the ESPHome integration 214 | * As soon the new ESPHome device is online it should be discovered automatically 215 | * Goto the integration page and configure the new ESPHome device 216 | * The new device plus some entites are available now 217 | 218 | ## Flash the Zigbee module 219 | 220 | In Home Assistant on the ESPHome device panel, select the device corresponding to the zb-gw03. Then turn "download mode" switch ON and toggle "zigbee reset" switch once. 221 | The device will go into Download Mode. 222 | 223 | Now make sure to have the necessary CLI tools to be able to flash the Zigbee firmware: 224 | 225 | ``` 226 | # Install required tools (telnet & sx) 227 | $ apt-get install telnet lrzsz # for Debian based Linux distributions 228 | # or 229 | $ brew install telnet lrzsz # for macOS 230 | ``` 231 | 232 | Then use telnet to turn on firmware uploading, by launching a telnet session against the current IP address of the zb-gw03 (replace 192.168.132.225 with your IP) and selecting option `1` when prompted: 233 | ``` 234 | $ telnet 192.168.132.225 6638 235 | 236 | Trying 192.168.132.225... 237 | Connected to 192.168.132.225. 238 | Escape character is '^]'. 239 | 240 | 241 | Gecko Bootloader v1.9.1.04 242 | 1. upload gbl 243 | 2. run 244 | 3. ebl info 245 | BL > 246 | Gecko Bootloader v1.9.1.04 247 | 1. upload gbl 248 | 2. run 249 | 3. ebl info 250 | BL > 1 251 | 252 | begin upload 253 | C 254 | ``` 255 | 256 | Once the `begin upload` mode shows up, you can open a new terminal and issue the following command to upload. Note that if you are using macOS, you must use `lsx` instead of `sx`, with the same flags. You can find the firmware file to upload at [this GitHub repo](https://github.com/xsp1989/zigbeeFirmware/tree/master/firmware/ZigbeeBridge_SM-011-signed): 257 | ``` 258 | $ sx -vv -X -b --tcp-client 192.168.132.225:6638 ncp-uart-sw_7.4.3.0_115200.ota 259 | connecting to [192.168.132.225] <6638> 260 | 261 | Sending ncp-uart-sw_7.4.3.0_115200.ota, xxxx blocks: Give your local XMODEM receive command now. 262 | Bytes Sent: 247752 BPS:3841 263 | 264 | Transfer complete 265 | ``` 266 | 267 | Once the transfer has completed you can close the previously opened telnet session at the other terminal (CTRL+C) followed by `quit`: 268 | 269 | ``` 270 | C^] 271 | telnet> quit 272 | Connection closed. 273 | ``` 274 | 275 | Now in Home Assistant on the ESPHome device panel, turn "download mode" switch OFF and toggle "zigbee reset" again. The Zigbee module will boot into the new firmware now. 276 | 277 | ## Connect the device to Home Assistant 278 | 279 | There are a few ways with which to integrate your ESPHome devices into Home Assistant, 2 popular systems are ZHA and Zigbee2MQTT. 280 | 281 | Note: If you're struggling to resolve `*.local` addresses i.e. zb-gw03.local then you may need to use the device IP Address instead. 282 | 283 | #### ZHA (Zigbee Home Automation) 284 | 285 | * Goto the integration page of Home Assistant 286 | * Use the "Add integration" button 287 | * Search for "ZHA" 288 | * Choose "Enter Manually" at the serial port selection dialog 289 | * Choose "EZSP" as type of your Zigbee radio 290 | * The serial device path is `socket://esphome_node_name.local:6638` (f.e. `socket://zb-gw03.local:6638` if you use the `coordinator-example.yaml`). Enter `115200` baud as port speed and the "data flow control" is `software` 291 | 292 | #### Zigbee2MQTT 293 | 294 | Configuration can be done via the Zigbee2MQTT UI, the `configuration.yaml` file or if you are using the Home Assistant extension for Zigbee2MQTT the UI allows you to edit the yaml configuration file. 295 | 296 | If you're using the Zigbee2MQTT UI: 297 | 298 | * Goto Settings -> Settings -> Serial 299 | * Enter the Port which should be `tcp://esphome_node_name.local:6638` (f.e. `tcp://zb-gw03.local:6638` if you use the `coordinator-example.yaml`) 300 | * From the Adapter dropdown select `ezsp` 301 | * Baud rate already has a default of `115200` therefore it should need to be entered 302 | 303 | 304 | 305 | If you're using the `configuration.yaml` either in the data directory or via the UI of the Home Assistant extension then you will need to ensure that the `serial` section has the following properties: 306 | 307 | ``` 308 | serial: 309 | port: tcp://esphome_node_name.local:6638 310 | adapter: ezsp 311 | ``` 312 | 313 | 314 | -------------------------------------------------------------------------------- /docs/router.md: -------------------------------------------------------------------------------- 1 | # How to convert from Zigbee Coordinator into a Zigbee Router device 2 | 3 | ![GitHub actions](https://github.com/syssi/esphome-zb-gw03/actions/workflows/ci.yaml/badge.svg) 4 | ![GitHub stars](https://img.shields.io/github/stars/syssi/esphome-zb-gw03) 5 | ![GitHub forks](https://img.shields.io/github/forks/syssi/esphome-zb-gw03) 6 | ![GitHub watchers](https://img.shields.io/github/watchers/syssi/esphome-zb-gw03) 7 | [!["Buy Me A Coffee"](https://img.shields.io/badge/buy%20me%20a%20coffee-donate-yellow.svg)](https://www.buymeacoffee.com/syssi) 8 | 9 | #### 1. Make sure Home Assistant (ZHA) doesn't use the device as coordinator 10 | 11 | #### 2. Flash the `router-example.yaml` to your ZB-GW03 12 | 13 | ``` 14 | esphome run router-example.yaml 15 | ``` 16 | 17 | #### 3. Install the xmodem file transfer tool lrzsz 18 | 19 | ``` 20 | apt-get install lrzsz 21 | ``` 22 | 23 | #### 4. Download the Zigbee router firmware image 24 | 25 | ``` 26 | wget https://github.com/digiblur/Tasmota/raw/development/zigbee_router/efr32mg21_zigbee_router_signed-6.7.10.gbl.ota 27 | ``` 28 | 29 | #### 5. Boot the Zigbee module into the download mode 30 | 31 | - Turn `on` the `Download Mode` switch entity via Home Assistant 32 | - Turn `on` the `Zigbee Reset` switch entity via Home Assistant (goes to "off" automatically) 33 | - Turn `off` the `Download Mode` 34 | 35 | #### 6. Telnet to the bootloader of the Zigbee module and select "upload gbl" 36 | 37 | - `telnet 192.168.132.230 6638` 38 | - Press `return` 39 | - Press `1` and `return` to select "upload gbl" 40 | 41 | ``` 42 | $ telnet 192.168.132.230 6638 43 | Trying 192.168.132.230... 44 | Connected to 192.168.132.230. 45 | Escape character is '^]'. 46 | 47 | Gecko Bootloader v1.9.1.04 48 | 1. upload gbl 49 | 2. run 50 | 3. ebl info 51 | BL > 52 | Gecko Bootloader v1.9.1.04 53 | 1. upload gbl 54 | 2. run 55 | 3. ebl info 56 | BL > 1 57 | 58 | begin upload 59 | ``` 60 | 61 | #### 7. Go to a second terminal and upload the new firmware. You don't need to cancel the telnet session 62 | 63 | ``` 64 | $ sx -vv -X -b --tcp-client 192.168.132.230:6638 efr32mg21_zigbee_router_signed-6.7.10.gbl.ota 65 | connecting to [192.168.132.230] <6638> 66 | 67 | Sending efr32mg21_zigbee_router_signed-6.7.10.gbl.ota, 1843 blocks: Give your local XMODEM receive command now. 68 | Bytes Sent: 236032 BPS:3556 69 | 70 | Transfer complete 71 | ``` 72 | 73 | #### 8. Restart the Zigbee module via Home Asisstant or Reboot the whole device 74 | 75 | Press the `Zigbee Reset` switch or `Restart` switch. 76 | 77 | #### 9. Enable the "Pairing Mode" 78 | 79 | Primary method is to go to the ESPHome device in Home Assistant and turn the `Enable Pairing Mode` switch `on`. The switch goes back to `off` after a few seconds. This is fine! 80 | 81 | Alternative method to enable pairing mode is to use a paper clip to press the physical reset button of the device which is inside a hole located next to the USB power plug. 82 | 83 | #### 10. The Router can be paired now with your Zigbee Coordinator 84 | 85 | If you use ZHA go to your Zigbee Coordinator Device and press "Add device". 86 | 87 | ![Paired Zigbee router](../images/zha-paired-router.png "Paired Zigbee router") 88 | -------------------------------------------------------------------------------- /images/001.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syssi/esphome-zb-gw03/0dd8042033f31cbea2a641a6ad8e546fcbf72167/images/001.jpg -------------------------------------------------------------------------------- /images/002.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syssi/esphome-zb-gw03/0dd8042033f31cbea2a641a6ad8e546fcbf72167/images/002.jpg -------------------------------------------------------------------------------- /images/003.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syssi/esphome-zb-gw03/0dd8042033f31cbea2a641a6ad8e546fcbf72167/images/003.jpg -------------------------------------------------------------------------------- /images/004.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syssi/esphome-zb-gw03/0dd8042033f31cbea2a641a6ad8e546fcbf72167/images/004.jpg -------------------------------------------------------------------------------- /images/005.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syssi/esphome-zb-gw03/0dd8042033f31cbea2a641a6ad8e546fcbf72167/images/005.jpg -------------------------------------------------------------------------------- /images/006.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syssi/esphome-zb-gw03/0dd8042033f31cbea2a641a6ad8e546fcbf72167/images/006.jpg -------------------------------------------------------------------------------- /images/007.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syssi/esphome-zb-gw03/0dd8042033f31cbea2a641a6ad8e546fcbf72167/images/007.jpg -------------------------------------------------------------------------------- /images/008.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syssi/esphome-zb-gw03/0dd8042033f31cbea2a641a6ad8e546fcbf72167/images/008.jpg -------------------------------------------------------------------------------- /images/009.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syssi/esphome-zb-gw03/0dd8042033f31cbea2a641a6ad8e546fcbf72167/images/009.jpg -------------------------------------------------------------------------------- /images/010.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syssi/esphome-zb-gw03/0dd8042033f31cbea2a641a6ad8e546fcbf72167/images/010.jpg -------------------------------------------------------------------------------- /images/zb-gw03-v1_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syssi/esphome-zb-gw03/0dd8042033f31cbea2a641a6ad8e546fcbf72167/images/zb-gw03-v1_3.jpg -------------------------------------------------------------------------------- /images/zha-paired-router.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syssi/esphome-zb-gw03/0dd8042033f31cbea2a641a6ad8e546fcbf72167/images/zha-paired-router.png -------------------------------------------------------------------------------- /images/zigbee2mqtt-serial-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/syssi/esphome-zb-gw03/0dd8042033f31cbea2a641a6ad8e546fcbf72167/images/zigbee2mqtt-serial-settings.png -------------------------------------------------------------------------------- /packages/button_pairing_mode.yaml: -------------------------------------------------------------------------------- 1 | # Don't use the button component as long as ESPHome 2021.9.3 2 | # is required because of the buggy esphome-stream-server 3 | # 4 | # button: 5 | # - platform: template 6 | # name: "${name} Enable Pairing Mode" 7 | # on_press: 8 | # - switch.turn_on: download_mode 9 | # - delay: 5s 10 | # - switch.turn_off: download_mode 11 | 12 | switch: 13 | - platform: template 14 | id: zigbee_pairing_mode 15 | name: "${name} Toggle pairing mode" 16 | turn_on_action: 17 | - switch.turn_on: download_mode 18 | - delay: 5s 19 | - switch.turn_off: download_mode 20 | 21 | binary_sensor: 22 | - platform: gpio 23 | name: "${name} Enable Pairing Mode" 24 | internal: true 25 | pin: 26 | number: GPIO34 27 | inverted: true 28 | on_press: 29 | - switch.turn_on: download_mode 30 | - delay: 5s 31 | - switch.turn_off: download_mode 32 | -------------------------------------------------------------------------------- /packages/button_zigbee_reset.yaml: -------------------------------------------------------------------------------- 1 | binary_sensor: 2 | - platform: gpio 3 | name: "${name} Zigbee Reset Button" 4 | pin: 5 | number: GPIO34 6 | inverted: true 7 | on_press: 8 | - switch.turn_off: download_mode 9 | - switch.turn_on: zigbee_reset 10 | -------------------------------------------------------------------------------- /packages/core.yaml: -------------------------------------------------------------------------------- 1 | # GPIO15 LedLink (red led) 2 | # GPIO14 Led_i 1 (green led) 3 | # GPIO16 ETH POWER 4 | # GPIO18 ETH MDIO 5 | # GPIO23 ETH MDC 6 | # GPIO34 Button 1 7 | # GPIO12 Zigbee RST 2 8 | # GPIO13 Zigbee RST 1 9 | # GPIO02 Zigbee Tx 10 | # GPIO04 Zigbee Rx 11 | # GPIO32 I2C SCL 1 12 | # GPIO33 I2C SDA 1 13 | 14 | substitutions: 15 | name: zb-gw03 16 | # Gets overridden by mdns.yaml to avoid a breaking change 17 | device_role: "router" 18 | 19 | esphome: 20 | name: ${name} 21 | comment: ${device_description} 22 | min_version: 2024.6.0 23 | project: 24 | name: "syssi.esphome-zb-gw03" 25 | version: 2.0.1 26 | on_boot: 27 | priority: 600 28 | then: 29 | - switch.turn_off: download_mode 30 | - switch.turn_on: zigbee_reset 31 | - lambda: |- 32 | if("${device_role}" == "coordinator") { 33 | id(mdns0).add_extra_service({ 34 | "_zigbee-coordinator", 35 | "_tcp", 36 | 6638, 37 | { 38 | {"name", "ZB-GW03"}, 39 | {"serial_number", get_mac_address()}, 40 | {"radio_type", "ezsp"} 41 | } 42 | }); 43 | } 44 | 45 | esp32: 46 | board: esp-wrover-kit 47 | framework: 48 | type: esp-idf 49 | 50 | external_components: 51 | - source: github://oxan/esphome-stream-server@master 52 | 53 | ota: 54 | platform: esphome 55 | 56 | logger: 57 | level: DEBUG 58 | 59 | api: 60 | 61 | mdns: 62 | id: mdns0 63 | 64 | uart: 65 | id: uart_zigbee 66 | rx_pin: GPIO4 67 | tx_pin: GPIO2 68 | baud_rate: 115200 69 | 70 | stream_server: 71 | uart_id: uart_zigbee 72 | port: 6638 73 | 74 | switch: 75 | - platform: restart 76 | name: "${name} Restart" 77 | 78 | - platform: template 79 | id: zigbee_reset 80 | name: "${name} Zigbee Reset" 81 | turn_on_action: 82 | - switch.turn_on: zigbee_rst 83 | - delay: 10ms 84 | - switch.turn_off: zigbee_rst 85 | 86 | # SM-011 V1.0 nRST 87 | - platform: gpio 88 | id: zigbee_rst 89 | pin: GPIO13 90 | name: "${name} Zigbee nRST" 91 | inverted: true 92 | internal: true 93 | restore_mode: ALWAYS_OFF 94 | 95 | # SM-011 V1.0 PA00 96 | - platform: gpio 97 | id: download_mode 98 | pin: GPIO12 99 | name: "${name} Zigbee Download Mode" 100 | inverted: true 101 | restore_mode: ALWAYS_OFF 102 | -------------------------------------------------------------------------------- /packages/ethernet.yaml: -------------------------------------------------------------------------------- 1 | ethernet: 2 | type: LAN8720 3 | mdc_pin: GPIO23 4 | mdio_pin: GPIO18 5 | clk_mode: GPIO17_OUT 6 | phy_addr: 1 7 | power_pin: GPIO16 8 | -------------------------------------------------------------------------------- /packages/green_led.yaml: -------------------------------------------------------------------------------- 1 | light: 2 | - platform: monochromatic 3 | name: "${name} green led" 4 | output: output0 5 | 6 | output: 7 | - platform: ledc 8 | pin: GPIO14 9 | id: output0 10 | inverted: true 11 | -------------------------------------------------------------------------------- /packages/i2c.yaml: -------------------------------------------------------------------------------- 1 | i2c: 2 | - id: i2c_bus 3 | sda: GPIO33 4 | scl: GPIO32 5 | scan: true 6 | -------------------------------------------------------------------------------- /packages/mdns.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | device_role: "coordinator" 3 | -------------------------------------------------------------------------------- /packages/status_led.yaml: -------------------------------------------------------------------------------- 1 | status_led: 2 | pin: 3 | # red led 4 | number: GPIO15 5 | inverted: true 6 | -------------------------------------------------------------------------------- /packages/wifi.yaml: -------------------------------------------------------------------------------- 1 | # Using WiFi instead of ethernet isn't recommended 2 | # See https://github.com/syssi/esphome-zb-gw03/issues/49 3 | 4 | wifi: 5 | ssid: !secret wifi_ssid 6 | password: !secret wifi_password 7 | -------------------------------------------------------------------------------- /router-example.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: zb-gw03-router 3 | device_description: "ZB-GW03 eWeLink Ethernet Zigbee Gateway based Zigbee Router" 4 | device_role: "router" 5 | 6 | packages: 7 | zb-gw03: 8 | url: https://github.com/syssi/esphome-zb-gw03 9 | ref: main 10 | files: 11 | - packages/core.yaml 12 | - packages/ethernet.yaml 13 | - packages/status_led.yaml 14 | - packages/green_led.yaml 15 | - packages/button_pairing_mode.yaml 16 | refresh: 0s 17 | --------------------------------------------------------------------------------