├── .clang-format ├── .gitattributes ├── .github ├── actions │ └── restore-python │ │ └── action.yml └── workflows │ ├── ci.yaml │ └── matchers │ ├── ci-custom.json │ ├── clang-tidy.json │ ├── esphome-config.json │ ├── gcc.json │ ├── lint-python.json │ └── python.json ├── .gitignore ├── .pre-commit-config.yaml ├── .yamllint ├── LICENSE ├── README.md ├── bluesmart-charger-esp32-example.yaml ├── bluesmart-charger-esp8266-example.yaml ├── checks.json ├── components └── victron │ ├── __init__.py │ ├── binary_sensor.py │ ├── sensor.py │ ├── text_sensor.py │ ├── victron.cpp │ └── victron.h ├── debug-esp32-example.yaml ├── debug-esp8266-example.yaml ├── docs └── smartsolar-mppt-example-pdus.txt ├── images ├── circuit.jpg └── circuit_thumbnail.jpg ├── multi-rs-esp8266-example.yaml ├── phoenix-charger-esp8266-example.yaml ├── phoenix-inverter-esp8266-example.yaml ├── setup.cfg ├── smartshunt-esp8266-example.yaml ├── smartsolar-mppt-esp8266-example-advanced.yaml ├── smartsolar-mppt-esp8266-example-multiple-uarts.yaml ├── smartsolar-mppt-esp8266-example.yaml ├── test-esp32.sh ├── test-esp8266.sh └── tests ├── .gitignore ├── emulator-all-keys.yaml ├── fake-bluesmart-ip22-0xa332.yaml ├── fake-bluesmart-ip65-0xa30a.yaml ├── fake-bmv600.yaml ├── fake-bmv700.yaml ├── fake-bmv710.yaml ├── fake-bmv712.yaml ├── fake-multi-rs-solar.yaml ├── fake-phoenix-inverter.yaml ├── fake-smartsolar-mppt-150-70-rev3.yaml └── fake-smartsolar-mppt.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 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.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 | cp -r ../components/* esphome/components/ 55 | git config user.name "ci" 56 | git config user.email "ci@github.com" 57 | git add . 58 | git commit -a -m "Add external component" 59 | ln -sf ../venv venv 60 | 61 | - name: Archive prepared repository 62 | uses: pyTooling/upload-artifact@v4 63 | with: 64 | name: bundle 65 | path: . 66 | include-hidden-files: true 67 | retention-days: 1 68 | 69 | common: 70 | name: Create common environment 71 | runs-on: ubuntu-24.04 72 | needs: bundle 73 | outputs: 74 | cache-key: ${{ steps.cache-key.outputs.key }} 75 | steps: 76 | - name: Download prepared repository 77 | uses: pyTooling/download-artifact@v4 78 | with: 79 | name: bundle 80 | path: . 81 | - name: Update index to make "git diff-index" happy 82 | run: git update-index -q --really-refresh 83 | 84 | - name: Generate cache-key 85 | id: cache-key 86 | run: echo key="${{ hashFiles('esphome/requirements.txt', 'esphome/requirements_test.txt') }}" >> $GITHUB_OUTPUT 87 | - name: Set up Python ${{ env.DEFAULT_PYTHON }} 88 | id: python 89 | uses: actions/setup-python@v5.6.0 90 | with: 91 | python-version: ${{ env.DEFAULT_PYTHON }} 92 | - name: Restore Python virtual environment 93 | id: cache-venv 94 | uses: actions/cache@v4.2.3 95 | with: 96 | path: venv 97 | # yamllint disable-line rule:line-length 98 | key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ steps.cache-key.outputs.key }} 99 | 100 | - name: Create Python virtual environment 101 | if: steps.cache-venv.outputs.cache-hit != 'true' 102 | run: | 103 | python -m venv venv 104 | . venv/bin/activate 105 | python --version 106 | cd esphome 107 | pip install -r requirements.txt -r requirements_test.txt 108 | pip install -e . 109 | 110 | ruff: 111 | name: Check ruff 112 | runs-on: ubuntu-24.04 113 | needs: 114 | - bundle 115 | - common 116 | defaults: 117 | run: 118 | working-directory: esphome 119 | steps: 120 | - name: Download prepared repository 121 | uses: pyTooling/download-artifact@v4 122 | with: 123 | name: bundle 124 | path: . 125 | 126 | - name: Update index to make "git diff-index" happy 127 | run: git update-index -q --really-refresh 128 | 129 | - name: Restore Python 130 | uses: ./.github/actions/restore-python 131 | with: 132 | python-version: ${{ env.DEFAULT_PYTHON }} 133 | cache-key: ${{ needs.common.outputs.cache-key }} 134 | - name: Run Ruff 135 | run: | 136 | . venv/bin/activate 137 | ruff format esphome tests 138 | - name: Suggested changes 139 | run: script/ci-suggest-changes 140 | if: always() 141 | 142 | flake8: 143 | name: Check flake8 144 | runs-on: ubuntu-24.04 145 | needs: 146 | - bundle 147 | - common 148 | defaults: 149 | run: 150 | working-directory: esphome 151 | steps: 152 | - name: Download prepared repository 153 | uses: pyTooling/download-artifact@v4 154 | with: 155 | name: bundle 156 | path: . 157 | - name: Update index to make "git diff-index" happy 158 | run: git update-index -q --really-refresh 159 | 160 | - name: Restore Python 161 | uses: ./.github/actions/restore-python 162 | with: 163 | python-version: ${{ env.DEFAULT_PYTHON }} 164 | cache-key: ${{ needs.common.outputs.cache-key }} 165 | - name: Run flake8 166 | run: | 167 | . venv/bin/activate 168 | flake8 esphome 169 | - name: Suggested changes 170 | run: script/ci-suggest-changes 171 | if: always() 172 | 173 | pylint: 174 | name: Check pylint 175 | runs-on: ubuntu-24.04 176 | needs: 177 | - bundle 178 | - common 179 | defaults: 180 | run: 181 | working-directory: esphome 182 | steps: 183 | - name: Download prepared repository 184 | uses: pyTooling/download-artifact@v4 185 | with: 186 | name: bundle 187 | path: . 188 | - name: Update index to make "git diff-index" happy 189 | run: git update-index -q --really-refresh 190 | 191 | - name: Restore Python 192 | uses: ./.github/actions/restore-python 193 | with: 194 | python-version: ${{ env.DEFAULT_PYTHON }} 195 | cache-key: ${{ needs.common.outputs.cache-key }} 196 | - name: Run pylint 197 | run: | 198 | . venv/bin/activate 199 | pylint -f parseable --persistent=n esphome 200 | - name: Suggested changes 201 | run: script/ci-suggest-changes 202 | if: always() 203 | 204 | pyupgrade: 205 | name: Check pyupgrade 206 | runs-on: ubuntu-24.04 207 | needs: 208 | - bundle 209 | - common 210 | defaults: 211 | run: 212 | working-directory: esphome 213 | steps: 214 | - name: Download prepared repository 215 | uses: pyTooling/download-artifact@v4 216 | with: 217 | name: bundle 218 | path: . 219 | - name: Update index to make "git diff-index" happy 220 | run: git update-index -q --really-refresh 221 | 222 | - name: Restore Python 223 | uses: ./.github/actions/restore-python 224 | with: 225 | python-version: ${{ env.DEFAULT_PYTHON }} 226 | cache-key: ${{ needs.common.outputs.cache-key }} 227 | - name: Run pyupgrade 228 | run: | 229 | . venv/bin/activate 230 | pyupgrade ${{ env.PYUPGRADE_TARGET }} `find esphome -name "*.py" -type f` 231 | - name: Suggested changes 232 | run: script/ci-suggest-changes 233 | if: always() 234 | 235 | ci-custom: 236 | name: Run script/ci-custom 237 | runs-on: ubuntu-24.04 238 | needs: 239 | - bundle 240 | - common 241 | defaults: 242 | run: 243 | working-directory: esphome 244 | steps: 245 | - name: Download prepared repository 246 | uses: pyTooling/download-artifact@v4 247 | with: 248 | name: bundle 249 | path: . 250 | - name: Update index to make "git diff-index" happy 251 | run: git update-index -q --really-refresh 252 | 253 | - name: Restore Python 254 | uses: ./.github/actions/restore-python 255 | with: 256 | python-version: ${{ env.DEFAULT_PYTHON }} 257 | cache-key: ${{ needs.common.outputs.cache-key }} 258 | - name: Register matcher 259 | run: echo "::add-matcher::.github/workflows/matchers/ci-custom.json" 260 | 261 | - name: Do not suggest to move consts 262 | run: | 263 | sed -i 's#if len(uses) < 3:#if len(uses) < 8:#' script/ci-custom.py 264 | git update-index --assume-unchanged script/ci-custom.py 265 | 266 | - name: Run script/ci-custom 267 | run: | 268 | . ../venv/bin/activate 269 | script/ci-custom.py 270 | 271 | clang-format: 272 | name: Check clang-format 273 | runs-on: ubuntu-24.04 274 | needs: 275 | - bundle 276 | - common 277 | defaults: 278 | run: 279 | working-directory: esphome 280 | steps: 281 | - name: Download prepared repository 282 | uses: pyTooling/download-artifact@v4 283 | with: 284 | name: bundle 285 | path: . 286 | - name: Update index to make "git diff-index" happy 287 | run: git update-index -q --really-refresh 288 | 289 | - name: Restore Python 290 | uses: ./.github/actions/restore-python 291 | with: 292 | python-version: ${{ env.DEFAULT_PYTHON }} 293 | cache-key: ${{ needs.common.outputs.cache-key }} 294 | - name: Install clang-format 295 | run: | 296 | . venv/bin/activate 297 | pip install clang-format -c requirements_dev.txt 298 | - name: Run clang-format 299 | run: | 300 | . venv/bin/activate 301 | script/clang-format -i 302 | - name: Suggested changes 303 | run: script/ci-suggest-changes 304 | if: always() 305 | 306 | clang-tidy: 307 | name: ${{ matrix.name }} 308 | runs-on: ubuntu-24.04 309 | needs: 310 | - bundle 311 | - common 312 | defaults: 313 | run: 314 | working-directory: esphome 315 | strategy: 316 | fail-fast: false 317 | max-parallel: 2 318 | matrix: 319 | include: 320 | - id: clang-tidy 321 | name: Run script/clang-tidy for ESP8266 322 | options: --environment esp8266-arduino-tidy --grep USE_ESP8266 323 | pio_cache_key: tidyesp8266 324 | - id: clang-tidy 325 | name: Run script/clang-tidy for ESP32 Arduino 1/4 326 | options: --environment esp32-arduino-tidy --split-num 4 --split-at 1 327 | pio_cache_key: tidyesp32 328 | - id: clang-tidy 329 | name: Run script/clang-tidy for ESP32 Arduino 2/4 330 | options: --environment esp32-arduino-tidy --split-num 4 --split-at 2 331 | pio_cache_key: tidyesp32 332 | - id: clang-tidy 333 | name: Run script/clang-tidy for ESP32 Arduino 3/4 334 | options: --environment esp32-arduino-tidy --split-num 4 --split-at 3 335 | pio_cache_key: tidyesp32 336 | - id: clang-tidy 337 | name: Run script/clang-tidy for ESP32 Arduino 4/4 338 | options: --environment esp32-arduino-tidy --split-num 4 --split-at 4 339 | pio_cache_key: tidyesp32 340 | - id: clang-tidy 341 | name: Run script/clang-tidy for ESP32 IDF 342 | options: --environment esp32-idf-tidy --grep USE_ESP_IDF 343 | pio_cache_key: tidyesp32-idf 344 | 345 | steps: 346 | - name: Download prepared repository 347 | uses: pyTooling/download-artifact@v4 348 | with: 349 | name: bundle 350 | path: . 351 | - name: Update index to make "git diff-index" happy 352 | run: git update-index -q --really-refresh 353 | 354 | - name: Restore Python 355 | uses: ./.github/actions/restore-python 356 | with: 357 | python-version: ${{ env.DEFAULT_PYTHON }} 358 | cache-key: ${{ needs.common.outputs.cache-key }} 359 | 360 | - name: Cache platformio 361 | if: github.ref == 'refs/heads/dev' 362 | uses: actions/cache@v4.2.3 363 | with: 364 | path: ~/.platformio 365 | key: platformio-${{ matrix.pio_cache_key }} 366 | 367 | - name: Cache platformio 368 | if: github.ref != 'refs/heads/dev' 369 | uses: actions/cache/restore@v4.2.3 370 | with: 371 | path: ~/.platformio 372 | key: platformio-${{ matrix.pio_cache_key }} 373 | 374 | - name: Register problem matchers 375 | run: | 376 | echo "::add-matcher::.github/workflows/matchers/gcc.json" 377 | echo "::add-matcher::.github/workflows/matchers/clang-tidy.json" 378 | 379 | - name: Run 'pio run --list-targets -e esp32-idf-tidy' 380 | if: matrix.name == 'Run script/clang-tidy for ESP32 IDF' 381 | run: | 382 | . venv/bin/activate 383 | mkdir -p .temp 384 | pio run --list-targets -e esp32-idf-tidy 385 | 386 | - name: Run clang-tidy 387 | run: | 388 | . venv/bin/activate 389 | script/clang-tidy --all-headers --fix ${{ matrix.options }} ../components 390 | env: 391 | # Also cache libdeps, store them in a ~/.platformio subfolder 392 | PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps 393 | 394 | - name: Suggested changes 395 | run: script/ci-suggest-changes 396 | # yamllint disable-line rule:line-length 397 | if: always() 398 | 399 | esphome-config: 400 | name: Validate example configurations 401 | runs-on: ubuntu-24.04 402 | needs: 403 | - bundle 404 | - common 405 | steps: 406 | - name: Download prepared repository 407 | uses: pyTooling/download-artifact@v4 408 | with: 409 | name: bundle 410 | path: . 411 | 412 | - name: Restore Python 413 | uses: ./.github/actions/restore-python 414 | with: 415 | python-version: ${{ env.DEFAULT_PYTHON }} 416 | cache-key: ${{ needs.common.outputs.cache-key }} 417 | 418 | - name: Register matcher 419 | run: echo "::add-matcher::.github/workflows/matchers/esphome-config.json" 420 | 421 | - name: Validate example configurations 422 | run: | 423 | . venv/bin/activate 424 | echo -e "wifi_ssid: ssid\nwifi_password: password\nmqtt_host: host\nmqtt_username: username\nmqtt_password: password" > secrets.yaml 425 | for YAML in $(grep -l '^esphome:$' *.yaml); do 426 | esphome -s external_components_source components config $YAML >/dev/null 427 | done 428 | 429 | - name: Validate test configurations 430 | run: | 431 | . venv/bin/activate 432 | echo -e "wifi_ssid: ssid\nwifi_password: password\nmqtt_host: host\nmqtt_username: username\nmqtt_password: password" > tests/secrets.yaml 433 | for YAML in $(grep -l '^esphome:$' tests/*.yaml); do 434 | esphome -s external_components_source ../components config $YAML >/dev/null 435 | done 436 | 437 | esphome-compile: 438 | name: Build example configurations 439 | runs-on: ubuntu-24.04 440 | needs: 441 | - bundle 442 | - common 443 | steps: 444 | - name: Download prepared repository 445 | uses: pyTooling/download-artifact@v4 446 | with: 447 | name: bundle 448 | path: . 449 | 450 | - name: Restore Python 451 | uses: ./.github/actions/restore-python 452 | with: 453 | python-version: ${{ env.DEFAULT_PYTHON }} 454 | cache-key: ${{ needs.common.outputs.cache-key }} 455 | 456 | - name: Write secrets.yaml 457 | shell: bash 458 | run: 'echo -e "wifi_ssid: ssid\nwifi_password: password\nmqtt_host: host\nmqtt_username: username\nmqtt_password: password" > secrets.yaml' 459 | - run: | 460 | . venv/bin/activate 461 | esphome -s external_components_source components compile bluesmart-charger-esp32-example.yaml 462 | # esphome -s external_components_source components config bluesmart-charger-esp8266-example.yaml 463 | - run: | 464 | . venv/bin/activate 465 | esphome -s external_components_source components compile smartsolar-mppt-esp8266-example.yaml 466 | - run: | 467 | . venv/bin/activate 468 | esphome -s external_components_source components compile smartsolar-mppt-esp8266-example-multiple-uarts.yaml 469 | - run: | 470 | . venv/bin/activate 471 | esphome -s external_components_source components compile smartsolar-mppt-esp8266-example-advanced.yaml 472 | - run: | 473 | . venv/bin/activate 474 | esphome -s external_components_source components compile smartshunt-esp8266-example.yaml 475 | - run: | 476 | . venv/bin/activate 477 | esphome -s external_components_source components compile phoenix-charger-esp8266-example.yaml 478 | - run: | 479 | . venv/bin/activate 480 | esphome -s external_components_source components compile phoenix-inverter-esp8266-example.yaml 481 | - run: | 482 | . venv/bin/activate 483 | esphome -s external_components_source components compile debug-esp32-example.yaml 484 | -------------------------------------------------------------------------------- /.github/workflows/matchers/ci-custom.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "ci-custom", 5 | "pattern": [ 6 | { 7 | "regexp": "^(.*):(\\d+):(\\d+):\\s+lint:\\s+(.*)$", 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/esphome-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "esphome-config", 5 | "severity": "warning", 6 | "pattern": [ 7 | { 8 | "regexp": "^WARNING Using `([^`]+)` is deprecated and will be removed(.*)$", 9 | "message": 1 10 | } 11 | ] 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.github/workflows/matchers/gcc.json: -------------------------------------------------------------------------------- 1 | { 2 | "problemMatcher": [ 3 | { 4 | "owner": "gcc", 5 | "severity": "error", 6 | "pattern": [ 7 | { 8 | "regexp": "^src/(.*):(\\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": "black", 5 | "severity": "error", 6 | "pattern": [ 7 | { 8 | "regexp": "^(.*): (Please format this file with the black formatter)", 9 | "file": 1, 10 | "message": 2 11 | } 12 | ] 13 | }, 14 | { 15 | "owner": "flake8", 16 | "severity": "error", 17 | "pattern": [ 18 | { 19 | "regexp": "^(.*):(\\d+): ([EFCDNW]\\d{3}.*)$", 20 | "file": 1, 21 | "line": 2, 22 | "message": 3 23 | } 24 | ] 25 | }, 26 | { 27 | "owner": "pylint", 28 | "severity": "error", 29 | "pattern": [ 30 | { 31 | "regexp": "^(.*):(\\d+): (\\[[EFCRW]\\d{4}\\(.*\\),.*\\].*)$", 32 | "file": 1, 33 | "line": 2, 34 | "message": 3 35 | } 36 | ] 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /.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 | .vscode/ 3 | secrets.yaml 4 | .esphome/ 5 | **/.pioenvs/ 6 | **/.piolibdeps/ 7 | **/lib/ 8 | **/src/ 9 | **/partitions.csv 10 | 11 | __pycache__/ 12 | *.py[cod] 13 | *$py.class 14 | .DS_Store 15 | .DS_Store? 16 | ._* 17 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # See https://pre-commit.com for more information 2 | # See https://pre-commit.com/hooks.html for more hooks 3 | # See https://github.com/rytilahti/python-miio/blob/master/.pre-commit-config.yaml 4 | repos: 5 | - repo: https://github.com/pre-commit/mirrors-isort 6 | rev: v5.10.1 7 | hooks: 8 | - id: isort 9 | - repo: https://github.com/astral-sh/ruff-pre-commit 10 | # Ruff version. 11 | rev: v0.5.5 12 | hooks: 13 | # Run the linter. 14 | - id: ruff 15 | args: [--fix] 16 | # Run the formatter. 17 | - id: ruff-format 18 | - repo: https://github.com/psf/black-pre-commit-mirror 19 | rev: 24.4.2 20 | hooks: 21 | - id: black 22 | args: 23 | - --safe 24 | - --quiet 25 | - repo: https://github.com/PyCQA/flake8 26 | rev: 7.1.0 27 | hooks: 28 | - id: flake8 29 | additional_dependencies: 30 | - flake8-docstrings==1.5.0 31 | - pydocstyle==5.1.1 32 | - repo: https://github.com/asottile/pyupgrade 33 | rev: v3.19.1 34 | hooks: 35 | - id: pyupgrade 36 | args: [--py310-plus] 37 | - repo: https://github.com/adrienverge/yamllint.git 38 | rev: v1.35.1 39 | hooks: 40 | - id: yamllint 41 | - repo: https://github.com/pre-commit/mirrors-clang-format 42 | rev: v13.0.1 43 | hooks: 44 | - id: clang-format 45 | types_or: [c, c++] 46 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | extends: default 2 | 3 | yaml-files: 4 | - '*.yaml' 5 | - '*.yml' 6 | - '.yamllint' 7 | 8 | ignore: | 9 | /.cache/ 10 | esphome/**/*.pio* 11 | 12 | rules: 13 | braces: 14 | level: error 15 | min-spaces-inside: 0 16 | max-spaces-inside: 1 17 | min-spaces-inside-empty: -1 18 | max-spaces-inside-empty: -1 19 | brackets: 20 | level: error 21 | min-spaces-inside: 0 22 | max-spaces-inside: 0 23 | min-spaces-inside-empty: -1 24 | max-spaces-inside-empty: -1 25 | colons: 26 | level: error 27 | max-spaces-before: 0 28 | max-spaces-after: 1 29 | commas: 30 | level: error 31 | max-spaces-before: 0 32 | min-spaces-after: 1 33 | max-spaces-after: 1 34 | comments: 35 | level: error 36 | require-starting-space: true 37 | min-spaces-from-content: 2 38 | comments-indentation: disable 39 | document-end: 40 | level: error 41 | present: false 42 | document-start: 43 | level: error 44 | present: false 45 | empty-lines: 46 | level: error 47 | max: 2 48 | max-start: 0 49 | max-end: 1 50 | hyphens: 51 | level: error 52 | max-spaces-after: 1 53 | indentation: 54 | level: error 55 | spaces: 2 56 | indent-sequences: true 57 | check-multi-line-strings: false 58 | key-duplicates: 59 | level: error 60 | line-length: disable 61 | new-line-at-end-of-file: 62 | level: error 63 | new-lines: 64 | level: error 65 | type: unix 66 | trailing-spaces: 67 | level: error 68 | truthy: 69 | level: error 70 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 KinDR007 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VictronMPPT-ESPHOME 2 | 3 | ESPHome component to monitor a Victron device _(MPPT, SmarShunt, BMV, Phoenix Inverter ... )_ via ve.direct / UART TTL 4 | 5 | ## Supported devices 6 | 7 | All Victron devices providing a ve.direct port. 8 | 9 | ## Tested devices 10 | 11 | * Victron SmartSolar MPPT 75/10 rev2 (0xA074) 12 | * Victron SmartSolar MPPT 75/15 13 | * Victron SmartSolar MPPT 100/15 14 | * Victron SmartSolar MPPT 100/20 15 | * Victron BlueSolar MPPT 100/30 16 | * Victron SmartSolar MPPT 150/35 17 | * Victron SmartSolar MPPT 150/45 18 | * Victron SmartSolar MPPT 150/60 19 | * Victron SmartSolar MPPT VE.Can 150/100 rev2 (Using VE.Direct port) 20 | * Victron SmartSolar MPPT 250/70 21 | * Victron Phoenix Inverter 12/500 22 | * Victron Blue Smart IP65 Charger 12|25 23 | 24 | * Victron SmartShunt 500A/50mV 25 | 26 | ## Requirements 27 | 28 | * [ESPHome 2024.6.0 or higher](https://github.com/esphome/esphome/releases). 29 | * Generic ESP32 or ESP8266 board 30 | 31 | ## Schematics 32 | 33 | Attention: [The TX voltage of the VE.Direct interface depends on the product: Some are 5V, others 3.3V!](https://www.victronenergy.com/live/vedirect_protocol:faq#q4is_the_vedirect_interface_33_or_5v) 34 | 35 | Please measure the voltage between TX and GND. In case of a logic level of 5V you should add a voltage divider or logic level converter between the ESP and the Victron device. 36 | 37 | ``` 38 | UART-TTL 39 | ┌────────────────┐ ┌──────────────────┐ 40 | │ GND o│<-------------->│o GND │ 41 | │ Victron TX o│--------------->│o D7 ESP32/ │ 42 | │ Charger RX o│ │ ESP8266 │<-- GND 43 | │ 5V o│ │ │<-- 3.3V 44 | └────────────────┘ └──────────────────┘ 45 | 46 | # UART-TTL jack (JST-PH 2.0mm pinch) 47 | ┌─── ─────── ────┐ 48 | │ │ 49 | │ O O O O │ 50 | │GND RX TX VCC │ 51 | └────────────────┘ 52 | ``` 53 | 54 | If you are unsure about to pin order please measure the voltage between GND and VCC (5V). If you measure a positive voltage you know the position of VCC and GND! 55 | 56 | ### JST-PH jack (direct connection noninsulated) 57 | 58 | | Pin | Purpose | ESP8266 pin | ESP32 pin | 59 | | :-----: | :----------- | :------------- |:------------- | 60 | | **1** | **GND** | **GND** |**GND** | 61 | | 2 | RX | | | 62 | | **3** | **TX** | D7 (**RX**) | GPIO16 (**RX**) | 63 | | 4 | 5V | | | 64 | 65 | 66 | 67 | 68 | 69 | note: _JST-PH connector on this image has lock downwards_ 70 | image 71 | 72 | 73 | According to the [VE.direct FAQ](https://www.victronenergy.com/live/vedirect_protocol:faq), the 5V/3.3 pin has a strict current limitation, allowing only 10mA on average with occasional 20mA bursts lasting up to 5 milliseconds. ESP devices like the ESP8266 and ESP32 draw significantly more current than this limit, typically several hundred milliamperes during startup and an additional 100-150mA when transmitting, which would immediately overload and permanently damage the Victron device's power circuit. Therefore, powering an ESP directly from these pins is not possible. 74 | 75 | 76 | 77 | note: ~~_Level shifter (5V<->3.3V)between esp32 and mppt is not necessary_ (tested on esp32 nodemcu v1 and MPPT 75/115 , MPPT 150/35 and smartshunt 500A)~~ 78 | 79 | ## Safe connection (insulated) 80 | use `ADUM 1201` for galvanic isolation between ESP and Victron Devices (or make an insulated cable for Ve.direct) 81 | 82 | |Ve.Direct image _downwards_|Ve.Direct image **upward** | Purpose (wire color)|connect|ADUM1201 #2| ADUM1201 #1 |connect| ESP8266 pin| ESP32 pin|USB TTL cable| 83 | |:-----| :----- | :----------- |:-----|:------------- |:------------- |:-----|:----- |:-----|:-----| 84 | | _1_ |**1** | **GND** (black) |<- ->| **GND2** | **GND1** |<- ->| **GND** |**GND**|**GND**| 85 | | _2_ |**2** | **RX** (red) |<- ->| **VOB** | **VIB** |<- ->| D8 (**TX**) | GPIO17 (**TX**)|**TX**| 86 | | _3_ |**3** | **TX** (white) |<- ->| **VIA**| **VOA** |<- ->|D7 (**RX**) | GPIO16 (**RX**)|**RX**| 87 | | _4_ |**4** | **5V** or **3V3** (yellow) |<- ->| **VDD2** | **VDD1** |<- ->| **3V3 or 5V** | **3V3 or 5V** |**3V3 or 5V**| 88 | 89 | ![adum1201_](https://github.com/KinDR007/VictronMPPT-ESPHOME/assets/61905170/9c7cf60c-fb19-404a-9c6e-7a17bc463ead) 90 | 91 | 92 | [Buy on Ebay](https://www.ebay.com/itm/176094311751?var=475546476315&norover=1&mkevt=1&mkrid=711-168613-066356-2&mkcid=2&itemid=475546476315_176094311751&targetid=295222477609&device=c&mktype=pla&googleloc=1003785&poi=&campaignid=20797276730&mkgroupid=155163397599&rlsatarget=pla-295222477609&abcId=&merchantid=119648210&gad_source=1&gbraid=0AAAAAD_QDh8nutO0oonh8hgwFzBcKUcJc&gclid=Cj0KCQjw-ai0BhDPARIsAB6hmP6RN5aHtdBKIpl4EERR_dzQcCZcNqUsNd9KJGAJuTSsmuLnHL7lLs4aAnRtEALw_wcB) 93 | 94 | [Buy on Aliexpress](https://www.aliexpress.com/item/1005005362881990.html?spm=a2g0o.productlist.main.55.22b97SeK7SeKfK&algo_pvid=8aa6f818-a16f-465d-a50d-d974fbbda803&algo_exp_id=8aa6f818-a16f-465d-a50d-d974fbbda803-27&pdp_npi=4%40dis%21CZK%2153.02%2129.24%21%21%212.23%211.23%21%40210387a117203472445525945efb01%2112000032744813603%21sea%21CZ%210%21AB&curPageLogUid=lG8tIpV8MVs7&utparam-url=scene%3Asearch%7Cquery_from%3A) 95 | 96 | 97 | 98 | --- 99 | 100 | ## Installation 101 | 102 | You can install this component with [ESPHome external components feature](https://esphome.io/components/external_components.html) like this: 103 | ```yaml 104 | external_components: 105 | - source: github://KinDR007/VictronMPPT-ESPHOME@main 106 | 107 | uart: 108 | id: uart_0 109 | tx_pin: D8 # Not used! The communication is read-only 110 | rx_pin: D7 111 | baud_rate: 19200 112 | rx_buffer_size: 256 113 | 114 | victron: 115 | id: victron0 116 | uart_id: uart_0 117 | 118 | sensor: 119 | - platform: victron 120 | victron_id: victron0 121 | panel_voltage: 122 | name: "Panel voltage" 123 | battery_voltage: 124 | name: "Battery voltage" 125 | battery_current: 126 | name: "Battery current" 127 | ``` 128 | 129 | or just use the `esp8266-example.yaml` as proof of concept: 130 | 131 | ```bash 132 | # Install esphome 133 | pip3 install esphome 134 | 135 | # Clone this external component 136 | git clone https://github.com/KinDR007/VictronMPPT-ESPHOME.git 137 | cd VictronMPPT-ESPHOME 138 | 139 | # Create a secret.yaml containing some setup specific secrets 140 | cat > secrets.yaml < // std::min 4 | #include "esphome/core/helpers.h" 5 | 6 | namespace esphome { 7 | namespace victron { 8 | 9 | static const char *const TAG = "victron"; 10 | 11 | static const uint8_t OFF_REASONS_SIZE = 16; 12 | static const char *const OFF_REASONS[OFF_REASONS_SIZE] = { 13 | "No input power", // 0000 0000 0000 0001 14 | "Switched off (power switch)", // 0000 0000 0000 0010 15 | "Switched off (device mode register)", // 0000 0000 0000 0100 16 | "Remote input", // 0000 0000 0000 1000 17 | "Protection active", // 0000 0000 0001 0000 18 | "Paygo", // 0000 0000 0010 0000 19 | "BMS", // 0000 0000 0100 0000 20 | "Engine shutdown detection", // 0000 0000 1000 0000 21 | "Analysing input voltage", // 0000 0001 0000 0000 22 | "Unknown: Bit 10", // 0000 0010 0000 0000 23 | "Unknown: Bit 11", // 0000 0100 0000 0000 24 | "Unknown: Bit 12", // 0000 1000 0000 0000 25 | "Unknown: Bit 13", // 0001 0000 0000 0000 26 | "Unknown: Bit 14", // 0010 0000 0000 0000 27 | "Unknown: Bit 15", // 0100 0000 0000 0000 28 | "Unknown: Bit 16", // 1000 0000 0000 0000 29 | }; 30 | 31 | void VictronComponent::dump_config() { // NOLINT(google-readability-function-size,readability-function-size) 32 | ESP_LOGCONFIG(TAG, "Victron:"); 33 | LOG_BINARY_SENSOR(" ", "Load state", load_state_binary_sensor_); 34 | LOG_BINARY_SENSOR(" ", "Relay state", relay_state_binary_sensor_); 35 | LOG_SENSOR(" ", "Max Power Yesterday", max_power_yesterday_sensor_); 36 | LOG_SENSOR(" ", "Max Power Today", max_power_today_sensor_); 37 | LOG_SENSOR(" ", "Yield Total", yield_total_sensor_); 38 | LOG_SENSOR(" ", "Yield Yesterday", yield_yesterday_sensor_); 39 | LOG_SENSOR(" ", "Yield Today", yield_today_sensor_); 40 | LOG_SENSOR(" ", "Panel Voltage", panel_voltage_sensor_); 41 | LOG_SENSOR(" ", "Panel Power", panel_power_sensor_); 42 | LOG_SENSOR(" ", "Battery Voltage", battery_voltage_sensor_); 43 | LOG_SENSOR(" ", "Battery Voltage 2", battery_voltage_2_sensor_); 44 | LOG_SENSOR(" ", "Battery Voltage 3", battery_voltage_3_sensor_); 45 | LOG_SENSOR(" ", "Battery Current", battery_current_sensor_); 46 | LOG_SENSOR(" ", "Battery Current", battery_current_2_sensor_); 47 | LOG_SENSOR(" ", "Battery Current", battery_current_3_sensor_); 48 | LOG_SENSOR(" ", "AC Out Voltage", ac_out_voltage_sensor_); 49 | LOG_SENSOR(" ", "AC Out Current", ac_out_current_sensor_); 50 | LOG_SENSOR(" ", "Load Current", load_current_sensor_); 51 | LOG_SENSOR(" ", "Day Number", day_number_sensor_); 52 | LOG_SENSOR(" ", "Charging Mode ID", charging_mode_id_sensor_); 53 | LOG_SENSOR(" ", "Error Code", error_code_sensor_); 54 | LOG_SENSOR(" ", "Warning Code", warning_code_sensor_); 55 | LOG_SENSOR(" ", "Tracking Mode ID", tracking_mode_id_sensor_); 56 | LOG_SENSOR(" ", "Device Mode ID", device_mode_id_sensor_); 57 | LOG_SENSOR(" ", "Off Reason Bitmask", off_reason_bitmask_sensor_); 58 | LOG_TEXT_SENSOR(" ", "Charging Mode", charging_mode_text_sensor_); 59 | LOG_TEXT_SENSOR(" ", "Error Text", error_text_sensor_); 60 | LOG_TEXT_SENSOR(" ", "Warning Text", warning_text_sensor_); 61 | LOG_TEXT_SENSOR(" ", "Tracking Mode", tracking_mode_text_sensor_); 62 | LOG_TEXT_SENSOR(" ", "Device Mode", device_mode_text_sensor_); 63 | LOG_TEXT_SENSOR(" ", "Firmware Version", firmware_version_text_sensor_); 64 | LOG_TEXT_SENSOR(" ", "Firmware Version 24bit", firmware_version_24bit_text_sensor_); 65 | LOG_TEXT_SENSOR(" ", "Device Type", device_type_text_sensor_); 66 | LOG_TEXT_SENSOR(" ", "Off Reason", off_reason_text_sensor_); 67 | 68 | LOG_SENSOR(" ", "Battery Temperature ", battery_temperature_sensor_); 69 | LOG_SENSOR(" ", "Instantaneous Power", instantaneous_power_sensor_); 70 | LOG_SENSOR(" ", "Consumed Amp Hours", consumed_amp_hours_sensor_); 71 | LOG_SENSOR(" ", "State Of Charge", state_of_charge_sensor_); 72 | LOG_SENSOR(" ", "Time To Go", time_to_go_sensor_); 73 | LOG_SENSOR(" ", "Depth Of The Deepest Discharge", depth_of_the_deepest_discharge_sensor_); 74 | LOG_SENSOR(" ", "Depth Of The Last Discharge", depth_of_the_last_discharge_sensor_); 75 | LOG_SENSOR(" ", "Number Of Charge Cycles", number_of_charge_cycles_sensor_); 76 | LOG_SENSOR(" ", "Number Of Full Discharges", number_of_full_discharges_sensor_); 77 | LOG_SENSOR(" ", "Min Battery Voltage", min_battery_voltage_sensor_); 78 | LOG_SENSOR(" ", "Max Battery Voltage", max_battery_voltage_sensor_); 79 | LOG_SENSOR(" ", "Last Full Charge", last_full_charge_sensor_); 80 | LOG_SENSOR(" ", "Amount Of Discharged Energy", amount_of_discharged_energy_sensor_); 81 | LOG_SENSOR(" ", "Amount Of Charged Energy", amount_of_charged_energy_sensor_); 82 | LOG_TEXT_SENSOR(" ", "Alarm Condition Active", alarm_condition_active_text_sensor_); 83 | LOG_TEXT_SENSOR(" ", "Alarm Reason", alarm_reason_text_sensor_); 84 | LOG_TEXT_SENSOR(" ", "Model Description", model_description_text_sensor_); 85 | 86 | check_uart_settings(19200); 87 | } 88 | 89 | void VictronComponent::loop() { 90 | const uint32_t now = millis(); 91 | if ((state_ > 0) && (now - last_transmission_ >= 200)) { 92 | // last transmission too long ago. Reset RX index. 93 | ESP_LOGW(TAG, "Last transmission too long ago"); 94 | state_ = 0; 95 | } 96 | 97 | if (!available()) 98 | return; 99 | 100 | last_transmission_ = now; 101 | while (available()) { 102 | uint8_t c; 103 | read_byte(&c); 104 | if (state_ == 0) { 105 | if (c == '\r' || c == '\n') { 106 | continue; 107 | } 108 | label_.clear(); 109 | value_.clear(); 110 | state_ = 1; 111 | } 112 | if (state_ == 1) { 113 | // Start of a ve.direct hex frame 114 | if (c == ':') { 115 | state_ = 3; 116 | continue; 117 | } 118 | if (c == '\t') { 119 | state_ = 2; 120 | } else { 121 | label_.push_back(c); 122 | } 123 | continue; 124 | } 125 | if (state_ == 2) { 126 | if (label_ == "Checksum") { 127 | state_ = 0; 128 | // The checksum is used as end of frame indicator 129 | if (now - this->last_publish_ >= this->throttle_) { 130 | this->last_publish_ = now; 131 | this->publishing_ = true; 132 | } else { 133 | this->publishing_ = false; 134 | } 135 | continue; 136 | } 137 | if (c == '\r' || c == '\n') { 138 | if (this->publishing_) { 139 | handle_value_(); 140 | } 141 | state_ = 0; 142 | } else { 143 | value_.push_back(c); 144 | } 145 | } 146 | // Discard ve.direct hex frame 147 | if (state_ == 3) { 148 | if (c == '\r' || c == '\n') { 149 | state_ = 0; 150 | } 151 | } 152 | } 153 | } 154 | 155 | static std::string charging_mode_text(int value) { 156 | switch (value) { 157 | case 0: 158 | return "Off"; 159 | case 1: 160 | return "Low power"; 161 | case 2: 162 | return "Fault"; 163 | case 3: 164 | return "Bulk"; 165 | case 4: 166 | return "Absorption"; 167 | case 5: 168 | return "Float"; 169 | case 6: 170 | return "Storage"; 171 | case 7: 172 | return "Equalize (manual)"; 173 | case 9: 174 | return "Inverting"; 175 | case 11: 176 | return "Power supply"; 177 | case 245: 178 | return "Starting-up"; 179 | case 246: 180 | return "Repeated absorption"; 181 | case 247: 182 | return "Auto equalize / Recondition"; 183 | case 248: 184 | return "BatterySafe"; 185 | case 252: 186 | return "External control"; 187 | default: 188 | return "Unknown"; 189 | } 190 | } 191 | 192 | static std::string error_code_text(int value) { 193 | switch (value) { 194 | case 0: 195 | return "No error"; 196 | case 2: 197 | return "Battery voltage too high"; 198 | case 17: 199 | return "Charger temperature too high"; 200 | case 18: 201 | return "Charger over current"; 202 | case 19: 203 | return "Charger current reversed"; 204 | case 20: 205 | return "Bulk time limit exceeded"; 206 | case 21: 207 | return "Current sensor issue"; 208 | case 26: 209 | return "Terminals overheated"; 210 | case 28: 211 | return "Converter issue"; 212 | case 33: 213 | return "Input voltage too high (solar panel)"; 214 | case 34: 215 | return "Input current too high (solar panel)"; 216 | case 38: 217 | return "Input shutdown (excessive battery voltage)"; 218 | case 39: 219 | return "Input shutdown (due to current flow during off mode)"; 220 | case 65: 221 | return "Lost communication with one of devices"; 222 | case 66: 223 | return "Synchronised charging device configuration issue"; 224 | case 67: 225 | return "BMS connection lost"; 226 | case 68: 227 | return "Network misconfigured"; 228 | case 116: 229 | return "Factory calibration data lost"; 230 | case 117: 231 | return "Invalid/incompatible firmware"; 232 | case 119: 233 | return "User settings invalid"; 234 | default: 235 | return "Unknown"; 236 | } 237 | } 238 | 239 | static std::string warning_code_text(int value) { 240 | switch (value) { 241 | case 0: 242 | return "No warning"; 243 | case 1: 244 | return "Low Voltage"; 245 | case 2: 246 | return "High Voltage"; 247 | case 4: 248 | return "Low SOC"; 249 | case 8: 250 | return "Low Starter Voltage"; 251 | case 16: 252 | return "High Starter Voltage"; 253 | case 32: 254 | return "Low Temperature"; 255 | case 64: 256 | return "High Temperature"; 257 | case 128: 258 | return "Mid Voltage"; 259 | case 256: 260 | return "Overload"; 261 | case 512: 262 | return "DC-ripple"; 263 | case 1024: 264 | return "Low V AC out"; 265 | case 2048: 266 | return "High V AC out"; 267 | default: 268 | return "Multiple warnings"; 269 | } 270 | } 271 | 272 | static std::string tracking_mode_text(int value) { 273 | switch (value) { 274 | case 0: 275 | return "Off"; 276 | case 1: 277 | return "Limited"; 278 | case 2: 279 | return "Active"; 280 | default: 281 | return "Unknown"; 282 | } 283 | } 284 | 285 | static std::string device_mode_text(int value) { 286 | switch (value) { 287 | case 0: 288 | return "Off"; 289 | case 2: 290 | return "On"; 291 | case 4: 292 | return "Off"; 293 | case 5: 294 | return "Eco"; 295 | default: 296 | return "Unknown"; 297 | } 298 | } 299 | 300 | static std::string dc_monitor_mode_text(int value) { 301 | switch (value) { 302 | case -9: 303 | return "Solar charger"; 304 | case -8: 305 | return "Wind turbine"; 306 | case -7: 307 | return "Shaft generator"; 308 | case -6: 309 | return "Alternator"; 310 | case -5: 311 | return "Fuel cell"; 312 | case -4: 313 | return "Water generator"; 314 | case -3: 315 | return "DC/DC charger"; 316 | case -2: 317 | return "AC charger"; 318 | case -1: 319 | return "Generic source"; 320 | case 0: 321 | return "Battery monitor (BMV)"; 322 | case 1: 323 | return "Generic load"; 324 | case 2: 325 | return "Electric drive"; 326 | case 3: 327 | return "Fridge"; 328 | case 4: 329 | return "Water pump"; 330 | case 5: 331 | return "Bilge pump"; 332 | case 6: 333 | return "DC system"; 334 | case 7: 335 | return "Inverter"; 336 | case 8: 337 | return "Water heater"; 338 | default: 339 | return "Unknown"; 340 | } 341 | } 342 | 343 | static std::string device_type_text(int value) { 344 | switch (value) { 345 | case 0x203: 346 | return "BMV-700"; 347 | case 0x204: 348 | return "BMV-702"; 349 | case 0x205: 350 | return "BMV-700H"; 351 | case 0x0300: 352 | return "BlueSolar MPPT 70|15"; 353 | case 0xA040: 354 | return "BlueSolar MPPT 75|50"; 355 | case 0xA041: 356 | return "BlueSolar MPPT 150|35"; 357 | case 0xA042: 358 | return "BlueSolar MPPT 75|15"; 359 | case 0xA043: 360 | return "BlueSolar MPPT 100|15"; 361 | case 0xA044: 362 | return "BlueSolar MPPT 100|30"; 363 | case 0xA045: 364 | return "BlueSolar MPPT 100|50"; 365 | case 0xA046: 366 | return "BlueSolar MPPT 150|70"; 367 | case 0xA047: 368 | return "BlueSolar MPPT 150|100"; 369 | case 0xA049: 370 | return "BlueSolar MPPT 100|50 rev2"; 371 | case 0xA04A: 372 | return "BlueSolar MPPT 100|30 rev2"; 373 | case 0xA04B: 374 | return "BlueSolar MPPT 150|35 rev2"; 375 | case 0xA04C: 376 | return "BlueSolar MPPT 75|10"; 377 | case 0xA04D: 378 | return "BlueSolar MPPT 150|45"; 379 | case 0xA04E: 380 | return "BlueSolar MPPT 150|60"; 381 | case 0xA04F: 382 | return "BlueSolar MPPT 150|85"; 383 | case 0xA050: 384 | return "SmartSolar MPPT 250|100"; 385 | case 0xA051: 386 | return "SmartSolar MPPT 150|100"; 387 | case 0xA052: 388 | return "SmartSolar MPPT 150|85"; 389 | case 0xA053: 390 | return "SmartSolar MPPT 75|15"; 391 | case 0xA075: 392 | return "SmartSolar MPPT 75|15 rev2"; 393 | case 0xA054: 394 | return "SmartSolar MPPT 75|10"; 395 | case 0xA074: 396 | return "SmartSolar MPPT 75|10 rev2"; 397 | case 0xA055: 398 | return "SmartSolar MPPT 100|15"; 399 | case 0xA056: 400 | return "SmartSolar MPPT 100|30"; 401 | case 0xA073: 402 | return "SmartSolar MPPT 150|45 rev3"; 403 | case 0xA057: 404 | return "SmartSolar MPPT 100|50"; 405 | case 0xA058: 406 | return "SmartSolar MPPT 150|35"; 407 | case 0xA059: 408 | return "SmartSolar MPPT 150|100 rev2"; 409 | case 0xA05A: 410 | return "SmartSolar MPPT 150|85 rev2"; 411 | case 0xA05B: 412 | return "SmartSolar MPPT 250|70"; 413 | case 0xA05C: 414 | return "SmartSolar MPPT 250|85"; 415 | case 0xA05D: 416 | return "SmartSolar MPPT 250|60"; 417 | case 0xA05E: 418 | return "SmartSolar MPPT 250|45"; 419 | case 0xA05F: 420 | return "SmartSolar MPPT 100|20"; 421 | case 0xA060: 422 | return "SmartSolar MPPT 100|20 48V"; 423 | case 0xA061: 424 | return "SmartSolar MPPT 150|45"; 425 | case 0xA062: 426 | return "SmartSolar MPPT 150|60"; 427 | case 0xA063: 428 | return "SmartSolar MPPT 150|70"; 429 | case 0xA064: 430 | return "SmartSolar MPPT 250|85 rev2"; 431 | case 0xA065: 432 | return "SmartSolar MPPT 250|100 rev2"; 433 | case 0xA066: 434 | return "BlueSolar MPPT 100|20"; 435 | case 0xA067: 436 | return "BlueSolar MPPT 100|20 48V"; 437 | case 0xA068: 438 | return "SmartSolar MPPT 250|60 rev2"; 439 | case 0xA069: 440 | return "SmartSolar MPPT 250|70 rev2"; 441 | case 0xA06A: 442 | return "SmartSolar MPPT 150|45 rev2"; 443 | case 0xA06B: 444 | return "SmartSolar MPPT 150|60 rev2"; 445 | case 0xA06C: 446 | return "SmartSolar MPPT 150|70 rev2"; 447 | case 0xA06D: 448 | return "SmartSolar MPPT 150|85 rev3"; 449 | case 0xA06E: 450 | return "SmartSolar MPPT 150|100 rev3"; 451 | case 0xA06F: 452 | return "BlueSolar MPPT 150|45 rev2"; 453 | case 0xA070: 454 | return "BlueSolar MPPT 150|60 rev2"; 455 | case 0xA071: 456 | return "BlueSolar MPPT 150|70 rev2"; 457 | case 0xA07D: 458 | return "BlueSolar MPPT 75|15 rev3"; 459 | case 0xA102: 460 | return "SmartSolar MPPT VE.Can 150/70"; 461 | case 0xA103: 462 | return "SmartSolar MPPT VE.Can 150/45"; 463 | case 0xA104: 464 | return "SmartSolar MPPT VE.Can 150/60"; 465 | case 0xA105: 466 | return "SmartSolar MPPT VE.Can 150/85"; 467 | case 0xA106: 468 | return "SmartSolar MPPT VE.Can 150/100"; 469 | case 0xA107: 470 | return "SmartSolar MPPT VE.Can 250/45"; 471 | case 0xA108: 472 | return "SmartSolar MPPT VE.Can 250/60"; 473 | case 0xA109: 474 | return "SmartSolar MPPT VE.Can 250/70"; 475 | case 0xA10A: 476 | return "SmartSolar MPPT VE.Can 250/85"; 477 | case 0xA10B: 478 | return "SmartSolar MPPT VE.Can 250/100"; 479 | case 0xA10C: 480 | return "SmartSolar MPPT VE.Can 150/70 rev2"; 481 | case 0xA10D: 482 | return "SmartSolar MPPT VE.Can 150/85 rev2"; 483 | case 0xA10E: 484 | return "SmartSolar MPPT VE.Can 150/100 rev2"; 485 | case 0xA10F: 486 | return "BlueSolar MPPT VE.Can 150/100"; 487 | case 0xA112: 488 | return "BlueSolar MPPT VE.Can 250/70"; 489 | case 0xA113: 490 | return "BlueSolar MPPT VE.Can 250/100"; 491 | case 0xA114: 492 | return "SmartSolar MPPT VE.Can 250/70 rev2"; 493 | case 0xA115: 494 | return "SmartSolar MPPT VE.Can 250/100 rev2"; 495 | case 0xA116: 496 | return "SmartSolar MPPT VE.Can 250/85 rev2"; 497 | case 0xA201: 498 | return "Phoenix Inverter 12V 250VA 230V"; 499 | case 0xA202: 500 | return "Phoenix Inverter 24V 250VA 230V"; 501 | case 0xA204: 502 | return "Phoenix Inverter 48V 250VA 230V"; 503 | case 0xA211: 504 | return "Phoenix Inverter 12V 375VA 230V"; 505 | case 0xA212: 506 | return "Phoenix Inverter 24V 375VA 230V"; 507 | case 0xA214: 508 | return "Phoenix Inverter 48V 375VA 230V"; 509 | case 0xA221: 510 | return "Phoenix Inverter 12V 500VA 230V"; 511 | case 0xA222: 512 | return "Phoenix Inverter 24V 500VA 230V"; 513 | case 0xA224: 514 | return "Phoenix Inverter 48V 500VA 230V"; 515 | case 0xA231: 516 | return "Phoenix Inverter 12V 250VA 230V"; 517 | case 0xA232: 518 | return "Phoenix Inverter 24V 250VA 230V"; 519 | case 0xA234: 520 | return "Phoenix Inverter 48V 250VA 230V"; 521 | case 0xA239: 522 | return "Phoenix Inverter 12V 250VA 120V"; 523 | case 0xA23A: 524 | return "Phoenix Inverter 24V 250VA 120V"; 525 | case 0xA23C: 526 | return "Phoenix Inverter 48V 250VA 120V"; 527 | case 0xA241: 528 | return "Phoenix Inverter 12V 375VA 230V"; 529 | case 0xA242: 530 | return "Phoenix Inverter 24V 375VA 230V"; 531 | case 0xA244: 532 | return "Phoenix Inverter 48V 375VA 230V"; 533 | case 0xA249: 534 | return "Phoenix Inverter 12V 375VA 120V"; 535 | case 0xA24A: 536 | return "Phoenix Inverter 24V 375VA 120V"; 537 | case 0xA24C: 538 | return "Phoenix Inverter 48V 375VA 120V"; 539 | case 0xA251: 540 | return "Phoenix Inverter 12V 500VA 230V"; 541 | case 0xA252: 542 | return "Phoenix Inverter 24V 500VA 230V"; 543 | case 0xA254: 544 | return "Phoenix Inverter 48V 500VA 230V"; 545 | case 0xA259: 546 | return "Phoenix Inverter 12V 500VA 120V"; 547 | case 0xA25A: 548 | return "Phoenix Inverter 24V 500VA 120V"; 549 | case 0xA25C: 550 | return "Phoenix Inverter 48V 500VA 120V"; 551 | case 0xA261: 552 | return "Phoenix Inverter 12V 800VA 230V"; 553 | case 0xA262: 554 | return "Phoenix Inverter 24V 800VA 230V"; 555 | case 0xA264: 556 | return "Phoenix Inverter 48V 800VA 230V"; 557 | case 0xA269: 558 | return "Phoenix Inverter 12V 800VA 120V"; 559 | case 0xA26A: 560 | return "Phoenix Inverter 24V 800VA 120V"; 561 | case 0xA26C: 562 | return "Phoenix Inverter 48V 800VA 120V"; 563 | case 0xA271: 564 | return "Phoenix Inverter 12V 1200VA 230V"; 565 | case 0xA272: 566 | return "Phoenix Inverter 24V 1200VA 230V"; 567 | case 0xA274: 568 | return "Phoenix Inverter 48V 1200VA 230V"; 569 | case 0xA279: 570 | case 0xA2F9: 571 | return "Phoenix Inverter 12V 1200VA 120V"; 572 | case 0xA27A: 573 | return "Phoenix Inverter 24V 1200VA 120V"; 574 | case 0xA27C: 575 | return "Phoenix Inverter 48V 1200VA 120V"; 576 | case 0xA281: 577 | return "Phoenix Inverter 12V 1600VA 230V"; 578 | case 0xA282: 579 | return "Phoenix Inverter 24V 1600VA 230V"; 580 | case 0xA284: 581 | return "Phoenix Inverter 48V 1600VA 230V"; 582 | case 0xA291: 583 | return "Phoenix Inverter 12V 2000VA 230V"; 584 | case 0xA292: 585 | return "Phoenix Inverter 24V 2000VA 230V"; 586 | case 0xA294: 587 | return "Phoenix Inverter 48V 2000VA 230V"; 588 | case 0xA2A1: 589 | return "Phoenix Inverter 12V 3000VA 230V"; 590 | case 0xA2A2: 591 | return "Phoenix Inverter 24V 3000VA 230V"; 592 | case 0xA2A4: 593 | return "Phoenix Inverter 48V 3000VA 230V"; 594 | case 0xA30A: 595 | return "Blue Smart IP65 Charger 12|25"; 596 | case 0xA332: 597 | return "Blue Smart IP22 Charger 24|8"; 598 | case 0xA334: 599 | return "Blue Smart IP22 Charger 24|12"; 600 | case 0xA336: 601 | return "Blue Smart IP22 Charger 24|16"; 602 | case 0xA340: 603 | return "Phoenix Smart IP43 Charger 12|50 (1+1)"; 604 | case 0xA341: 605 | return "Phoenix Smart IP43 Charger 12|50 (3)"; 606 | case 0xA342: 607 | return "Phoenix Smart IP43 Charger 24|25 (1+1)"; 608 | case 0xA343: 609 | return "Phoenix Smart IP43 Charger 24|25 (3)"; 610 | case 0xA344: 611 | return "Phoenix Smart IP43 Charger 12|30 (1+1)"; 612 | case 0xA345: 613 | return "Phoenix Smart IP43 Charger 12|30 (3)"; 614 | case 0xA346: 615 | return "Phoenix Smart IP43 Charger 24|16 (1+1)"; 616 | case 0xA347: 617 | return "Phoenix Smart IP43 Charger 24|16 (3)"; 618 | case 0xA381: 619 | return "BMV-712 Smart"; 620 | case 0xA382: 621 | return "BMV-710H Smart"; 622 | case 0xA383: 623 | return "BMV-712 Smart Rev2"; 624 | case 0xA389: 625 | return "SmartShunt 500A/50mV"; 626 | case 0xA38A: 627 | return "SmartShunt 1000A/50mV"; 628 | case 0xA38B: 629 | return "SmartShunt 2000A/50mV"; 630 | case 0xA442: 631 | return "Multi RS Solar 48V 6000VA 230V"; 632 | default: 633 | return "Unknown"; 634 | } 635 | } 636 | 637 | static std::string off_reason_text(uint32_t mask) { 638 | bool first = true; 639 | std::string value_list = ""; 640 | 641 | if (mask) { 642 | for (uint8_t i = 0; i < OFF_REASONS_SIZE; i++) { 643 | if (mask & (1 << i)) { 644 | if (first) { 645 | first = false; 646 | } else { 647 | value_list.append(";"); 648 | } 649 | value_list.append(OFF_REASONS[i]); 650 | } 651 | } 652 | } 653 | 654 | return value_list; 655 | } 656 | 657 | void VictronComponent::handle_value_() { 658 | int value; 659 | 660 | if (label_ == "V") { 661 | this->publish_state_(battery_voltage_sensor_, atoi(value_.c_str()) / 1000.0f); // NOLINT(cert-err34-c) 662 | return; 663 | } 664 | 665 | if (label_ == "V2") { 666 | // mV to V 667 | this->publish_state_(battery_voltage_2_sensor_, atoi(value_.c_str()) / 1000.0f); // NOLINT(cert-err34-c) 668 | return; 669 | } 670 | 671 | if (label_ == "V3") { 672 | // mV to V 673 | this->publish_state_(battery_voltage_3_sensor_, atoi(value_.c_str()) / 1000.0f); // NOLINT(cert-err34-c) 674 | return; 675 | } 676 | 677 | if (label_ == "VS") { 678 | // mV to V 679 | this->publish_state_(auxiliary_battery_voltage_sensor_, atoi(value_.c_str()) / 1000.0f); // NOLINT(cert-err34-c) 680 | return; 681 | } 682 | 683 | if (label_ == "VM") { 684 | // mV to V 685 | this->publish_state_(midpoint_voltage_of_the_battery_bank_sensor_, 686 | atoi(value_.c_str()) / 1000.0f); // NOLINT(cert-err34-c) 687 | return; 688 | } 689 | 690 | if (label_ == "DM") { 691 | // Per mill to % 692 | this->publish_state_(midpoint_deviation_of_the_battery_bank_sensor_, 693 | atoi(value_.c_str()) * 0.10f); // NOLINT(cert-err34-c) 694 | return; 695 | } 696 | 697 | if (label_ == "VPV") { 698 | // mV to V 699 | this->publish_state_(panel_voltage_sensor_, atoi(value_.c_str()) / 1000.0f); // NOLINT(cert-err34-c) 700 | return; 701 | } 702 | 703 | if (label_ == "PPV") { 704 | this->publish_state_(panel_power_sensor_, atoi(value_.c_str())); // NOLINT(cert-err34-c) 705 | return; 706 | } 707 | 708 | if (label_ == "I") { 709 | // mA to A 710 | this->publish_state_(battery_current_sensor_, atoi(value_.c_str()) / 1000.0f); // NOLINT(cert-err34-c) 711 | return; 712 | } 713 | 714 | if (label_ == "I2") { 715 | // mA to A 716 | this->publish_state_(battery_current_2_sensor_, atoi(value_.c_str()) / 1000.0f); // NOLINT(cert-err34-c) 717 | return; 718 | } 719 | 720 | if (label_ == "I3") { 721 | // mA to A 722 | this->publish_state_(battery_current_3_sensor_, atoi(value_.c_str()) / 1000.0f); // NOLINT(cert-err34-c) 723 | return; 724 | } 725 | 726 | if (label_ == "IL") { 727 | this->publish_state_(load_current_sensor_, atoi(value_.c_str()) / 1000.0f); // NOLINT(cert-err34-c) 728 | return; 729 | } 730 | 731 | if (label_ == "LOAD") { 732 | this->publish_state_(load_state_binary_sensor_, value_ == "ON" || value_ == "On"); 733 | return; 734 | } 735 | 736 | if (label_ == "T") { 737 | if (value_ == "---") { 738 | this->publish_state_(battery_temperature_sensor_, NAN); 739 | return; 740 | } 741 | 742 | this->publish_state_(battery_temperature_sensor_, atoi(value_.c_str())); // NOLINT(cert-err34-c) 743 | return; 744 | } 745 | 746 | if (label_ == "P") { 747 | this->publish_state_(instantaneous_power_sensor_, atoi(value_.c_str())); // NOLINT(cert-err34-c) 748 | return; 749 | } 750 | 751 | if (label_ == "CE") { 752 | // mAh -> Ah 753 | this->publish_state_(consumed_amp_hours_sensor_, atoi(value_.c_str()) / 1000.0f); // NOLINT(cert-err34-c) 754 | return; 755 | } 756 | 757 | if (label_ == "SOC") { 758 | // Per mill to % 759 | this->publish_state_(state_of_charge_sensor_, atoi(value_.c_str()) * 0.10f); // NOLINT(cert-err34-c) 760 | return; 761 | } 762 | 763 | if (label_ == "TTG") { 764 | this->publish_state_(time_to_go_sensor_, atoi(value_.c_str())); // NOLINT(cert-err34-c) 765 | return; 766 | } 767 | 768 | if (label_ == "Alarm") { 769 | this->publish_state_(alarm_condition_active_text_sensor_, value_); 770 | return; 771 | } 772 | 773 | if (label_ == "Relay") { 774 | this->publish_state_(relay_state_binary_sensor_, value_ == "ON" || value_ == "On"); 775 | return; 776 | } 777 | 778 | if (label_ == "AR") { 779 | this->publish_state_(alarm_reason_text_sensor_, error_code_text(atoi(value_.c_str()))); // NOLINT(cert-err34-c) 780 | return; 781 | } 782 | 783 | if (label_ == "OR") { 784 | auto off_reason_bitmask = parse_hex(value_.substr(2, value_.size() - 2)); 785 | if (off_reason_bitmask) { 786 | this->publish_state_(off_reason_bitmask_sensor_, *off_reason_bitmask); 787 | this->publish_state_(off_reason_text_sensor_, off_reason_text(*off_reason_bitmask)); 788 | } 789 | return; 790 | } 791 | 792 | if (label_ == "H1") { 793 | // mAh -> Ah 794 | this->publish_state_(depth_of_the_deepest_discharge_sensor_, 795 | atoi(value_.c_str()) / 1000.0); // NOLINT(cert-err34-c) 796 | return; 797 | } 798 | 799 | if (label_ == "H2") { 800 | // mAh -> Ah 801 | this->publish_state_(depth_of_the_last_discharge_sensor_, atoi(value_.c_str()) / 1000.0f); // NOLINT(cert-err34-c) 802 | return; 803 | } 804 | 805 | if (label_ == "H3") { 806 | // mAh -> Ah 807 | this->publish_state_(depth_of_the_average_discharge_sensor_, 808 | atoi(value_.c_str()) / 1000.0); // NOLINT(cert-err34-c) 809 | return; 810 | } 811 | 812 | if (label_ == "H4") { 813 | this->publish_state_(number_of_charge_cycles_sensor_, atoi(value_.c_str())); // NOLINT(cert-err34-c) 814 | return; 815 | } 816 | 817 | if (label_ == "H5") { 818 | this->publish_state_(number_of_full_discharges_sensor_, atoi(value_.c_str())); // NOLINT(cert-err34-c) 819 | return; 820 | } 821 | 822 | if (label_ == "H6") { 823 | if (value_ == "---") { 824 | this->publish_state_(cumulative_amp_hours_drawn_sensor_, NAN); 825 | return; 826 | } 827 | 828 | // mAh -> Ah 829 | this->publish_state_(cumulative_amp_hours_drawn_sensor_, atoi(value_.c_str()) / 1000.0f); // NOLINT(cert-err34-c) 830 | return; 831 | } 832 | 833 | if (label_ == "H7") { 834 | // mV to V 835 | this->publish_state_(min_battery_voltage_sensor_, atoi(value_.c_str()) / 1000.0f); // NOLINT(cert-err34-c) 836 | return; 837 | } 838 | 839 | if (label_ == "H8") { 840 | // mV to V 841 | this->publish_state_(max_battery_voltage_sensor_, atoi(value_.c_str()) / 1000.0f); // NOLINT(cert-err34-c) 842 | return; 843 | } 844 | 845 | if (label_ == "H9") { 846 | if (value_ == "---") { 847 | this->publish_state_(last_full_charge_sensor_, NAN); 848 | return; 849 | } 850 | 851 | // sec -> min 852 | this->publish_state_(last_full_charge_sensor_, (float) atoi(value_.c_str()) / 60.0f); // NOLINT(cert-err34-c) 853 | return; 854 | } 855 | 856 | if (label_ == "H10") { 857 | if (value_ == "---") { 858 | this->publish_state_(number_of_automatic_synchronizations_sensor_, NAN); 859 | return; 860 | } 861 | 862 | this->publish_state_(number_of_automatic_synchronizations_sensor_, atoi(value_.c_str())); // NOLINT(cert-err34-c) 863 | return; 864 | } 865 | 866 | if (label_ == "H11") { 867 | this->publish_state_(number_of_low_main_voltage_alarms_sensor_, atoi(value_.c_str())); // NOLINT(cert-err34-c) 868 | return; 869 | } 870 | 871 | if (label_ == "H12") { 872 | this->publish_state_(number_of_high_main_voltage_alarms_sensor_, atoi(value_.c_str())); // NOLINT(cert-err34-c) 873 | return; 874 | } 875 | 876 | if (label_ == "H13") { 877 | this->publish_state_(number_of_low_auxiliary_voltage_alarms_sensor_, atoi(value_.c_str())); // NOLINT(cert-err34-c) 878 | return; 879 | } 880 | 881 | if (label_ == "H14") { 882 | this->publish_state_(number_of_high_auxiliary_voltage_alarms_sensor_, 883 | atoi(value_.c_str())); // NOLINT(cert-err34-c) 884 | return; 885 | } 886 | 887 | if (label_ == "H15") { 888 | // mV to V 889 | this->publish_state_(min_auxiliary_battery_voltage_sensor_, 890 | atoi(value_.c_str()) / 1000.0f); // NOLINT(cert-err34-c) 891 | return; 892 | } 893 | 894 | if (label_ == "H16") { 895 | // mV to V 896 | this->publish_state_(max_auxiliary_battery_voltage_sensor_, 897 | atoi(value_.c_str()) / 1000.0f); // NOLINT(cert-err34-c) 898 | return; 899 | } 900 | 901 | // "H17" 0.01 kWh Amount of discharged energy (BMV) / Amount of produced energy (DC monitor) 902 | if (label_ == "H17") { 903 | // Wh 904 | this->publish_state_(amount_of_discharged_energy_sensor_, atoi(value_.c_str()) * 10.0f); // NOLINT(cert-err34-c) 905 | return; 906 | } 907 | 908 | // "H18" 0.01 kWh Amount of charged energy (BMV) / Amount of consumed energy (DC monitor) 909 | if (label_ == "H18") { 910 | // Wh 911 | this->publish_state_(amount_of_charged_energy_sensor_, atoi(value_.c_str()) * 10.0f); // NOLINT(cert-err34-c) 912 | return; 913 | } 914 | 915 | if (label_ == "H19") { 916 | this->publish_state_(yield_total_sensor_, atoi(value_.c_str()) * 10.0f); // NOLINT(cert-err34-c) 917 | return; 918 | } 919 | 920 | if (label_ == "H20") { 921 | this->publish_state_(yield_today_sensor_, atoi(value_.c_str()) * 10.0f); // NOLINT(cert-err34-c) 922 | return; 923 | } 924 | 925 | if (label_ == "H21") { 926 | this->publish_state_(max_power_today_sensor_, atoi(value_.c_str())); // NOLINT(cert-err34-c) 927 | return; 928 | } 929 | 930 | if (label_ == "H22") { 931 | this->publish_state_(yield_yesterday_sensor_, atoi(value_.c_str()) * 10.0f); // NOLINT(cert-err34-c) 932 | return; 933 | } 934 | 935 | if (label_ == "H23") { 936 | this->publish_state_(max_power_yesterday_sensor_, atoi(value_.c_str())); // NOLINT(cert-err34-c) 937 | return; 938 | } 939 | 940 | if (label_ == "ERR") { 941 | value = atoi(value_.c_str()); // NOLINT(cert-err34-c) 942 | this->publish_state_(error_code_sensor_, value); 943 | this->publish_state_(error_text_sensor_, error_code_text(value)); 944 | return; 945 | } 946 | 947 | if (label_ == "CS") { 948 | value = atoi(value_.c_str()); // NOLINT(cert-err34-c) 949 | this->publish_state_(charging_mode_id_sensor_, (float) value); 950 | this->publish_state_(charging_mode_text_sensor_, charging_mode_text(value)); 951 | return; 952 | } 953 | 954 | // "BMV" Model description (deprecated) 955 | if (label_ == "BMV") { 956 | this->publish_state_(model_description_text_sensor_, value_); 957 | return; 958 | } 959 | 960 | if (label_ == "FW") { 961 | this->publish_state_once_(firmware_version_text_sensor_, value_.insert(value_.size() - 2, ".")); 962 | return; 963 | } 964 | 965 | if (label_ == "FWE") { 966 | if (this->firmware_version_24bit_text_sensor_ == nullptr || this->firmware_version_24bit_text_sensor_->has_state()) 967 | return; 968 | 969 | if (value_.size() > 4) { 970 | std::string release_type = value_.substr(value_.size() - 2, 2); 971 | std::string version_number = value_.substr(0, value_.size() - 2); 972 | version_number = version_number.insert(version_number.size() - 2, "."); 973 | release_type = (release_type == "FF") ? "-official" : "-beta-" + release_type; 974 | 975 | this->publish_state_once_(firmware_version_24bit_text_sensor_, version_number + release_type); 976 | return; 977 | } 978 | 979 | this->publish_state_once_(firmware_version_24bit_text_sensor_, value_); 980 | return; 981 | } 982 | 983 | if (label_ == "PID") { 984 | this->publish_state_once_(device_type_text_sensor_, device_type_text(strtol(value_.c_str(), nullptr, 0))); 985 | return; 986 | } 987 | 988 | if (label_ == "SER#") { 989 | this->publish_state_once_(serial_number_text_sensor_, value_); 990 | return; 991 | } 992 | 993 | if (label_ == "HC#") { 994 | this->publish_state_once_(hardware_revision_text_sensor_, value_); 995 | return; 996 | } 997 | 998 | if (label_ == "HSDS") { 999 | this->publish_state_(day_number_sensor_, atoi(value_.c_str())); // NOLINT(cert-err34-c) 1000 | return; 1001 | } 1002 | 1003 | if (label_ == "MODE") { 1004 | value = atoi(value_.c_str()); // NOLINT(cert-err34-c) 1005 | this->publish_state_(device_mode_id_sensor_, (float) value); 1006 | this->publish_state_(device_mode_text_sensor_, device_mode_text(value)); 1007 | return; 1008 | } 1009 | 1010 | if (label_ == "AC_OUT_V") { 1011 | this->publish_state_(ac_out_voltage_sensor_, atoi(value_.c_str()) / 100.0f); // NOLINT(cert-err34-c) 1012 | return; 1013 | } 1014 | 1015 | if (label_ == "AC_OUT_I") { 1016 | this->publish_state_(ac_out_current_sensor_, std::max(0.0f, atoi(value_.c_str()) / 10.0f)); // NOLINT(cert-err34-c) 1017 | return; 1018 | } 1019 | 1020 | if (label_ == "AC_OUT_S") { 1021 | this->publish_state_(ac_out_apparent_power_sensor_, atoi(value_.c_str())); // NOLINT(cert-err34-c) 1022 | return; 1023 | } 1024 | 1025 | if (label_ == "WARN") { 1026 | value = atoi(value_.c_str()); // NOLINT(cert-err34-c) 1027 | this->publish_state_(warning_code_sensor_, value); 1028 | this->publish_state_(warning_text_sensor_, warning_code_text(value)); 1029 | return; 1030 | } 1031 | 1032 | if (label_ == "MPPT") { 1033 | value = atoi(value_.c_str()); // NOLINT(cert-err34-c) 1034 | this->publish_state_(tracking_mode_id_sensor_, (float) value); 1035 | this->publish_state_(tracking_mode_text_sensor_, tracking_mode_text(value)); 1036 | return; 1037 | } 1038 | 1039 | if (label_ == "MON") { 1040 | value = atoi(value_.c_str()); // NOLINT(cert-err34-c) 1041 | this->publish_state_(dc_monitor_mode_id_sensor_, (float) value); 1042 | this->publish_state_(dc_monitor_mode_text_sensor_, dc_monitor_mode_text(value)); 1043 | return; 1044 | } 1045 | 1046 | ESP_LOGD(TAG, "Unhandled property: %s %s", label_.c_str(), value_.c_str()); 1047 | } 1048 | 1049 | void VictronComponent::publish_state_(binary_sensor::BinarySensor *binary_sensor, const bool &state) { 1050 | if (binary_sensor == nullptr) 1051 | return; 1052 | 1053 | binary_sensor->publish_state(state); 1054 | } 1055 | 1056 | void VictronComponent::publish_state_(sensor::Sensor *sensor, float value) { 1057 | if (sensor == nullptr) 1058 | return; 1059 | 1060 | sensor->publish_state(value); 1061 | } 1062 | 1063 | void VictronComponent::publish_state_(text_sensor::TextSensor *text_sensor, const std::string &state) { 1064 | if (text_sensor == nullptr) 1065 | return; 1066 | 1067 | text_sensor->publish_state(state); 1068 | } 1069 | 1070 | void VictronComponent::publish_state_once_(text_sensor::TextSensor *text_sensor, const std::string &state) { 1071 | if (text_sensor == nullptr) 1072 | return; 1073 | 1074 | if (text_sensor->has_state()) 1075 | return; 1076 | 1077 | text_sensor->publish_state(state); 1078 | } 1079 | 1080 | } // namespace victron 1081 | } // namespace esphome 1082 | -------------------------------------------------------------------------------- /components/victron/victron.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "esphome/core/component.h" 4 | #include "esphome/components/binary_sensor/binary_sensor.h" 5 | #include "esphome/components/sensor/sensor.h" 6 | #include "esphome/components/text_sensor/text_sensor.h" 7 | #include "esphome/components/uart/uart.h" 8 | 9 | namespace esphome { 10 | namespace victron { 11 | 12 | class VictronComponent : public uart::UARTDevice, public Component { 13 | public: 14 | void set_throttle(uint32_t throttle) { this->throttle_ = throttle; } 15 | void set_load_state_binary_sensor(binary_sensor::BinarySensor *load_state_binary_sensor) { 16 | load_state_binary_sensor_ = load_state_binary_sensor; 17 | } 18 | void set_relay_state_binary_sensor(binary_sensor::BinarySensor *relay_state_binary_sensor) { 19 | relay_state_binary_sensor_ = relay_state_binary_sensor; 20 | } 21 | void set_max_power_yesterday_sensor(sensor::Sensor *max_power_yesterday_sensor) { 22 | max_power_yesterday_sensor_ = max_power_yesterday_sensor; 23 | } 24 | void set_max_power_today_sensor(sensor::Sensor *max_power_today_sensor) { 25 | max_power_today_sensor_ = max_power_today_sensor; 26 | } 27 | void set_yield_total_sensor(sensor::Sensor *yield_total_sensor) { yield_total_sensor_ = yield_total_sensor; } 28 | void set_yield_yesterday_sensor(sensor::Sensor *yield_yesterday_sensor) { 29 | yield_yesterday_sensor_ = yield_yesterday_sensor; 30 | } 31 | void set_yield_today_sensor(sensor::Sensor *yield_today_sensor) { yield_today_sensor_ = yield_today_sensor; } 32 | void set_panel_voltage_sensor(sensor::Sensor *panel_voltage_sensor) { panel_voltage_sensor_ = panel_voltage_sensor; } 33 | void set_panel_power_sensor(sensor::Sensor *panel_power_sensor) { panel_power_sensor_ = panel_power_sensor; } 34 | void set_battery_voltage_sensor(sensor::Sensor *battery_voltage_sensor) { 35 | battery_voltage_sensor_ = battery_voltage_sensor; 36 | } 37 | void set_battery_voltage_2_sensor(sensor::Sensor *battery_voltage_2_sensor) { 38 | battery_voltage_2_sensor_ = battery_voltage_2_sensor; 39 | } 40 | void set_battery_voltage_3_sensor(sensor::Sensor *battery_voltage_3_sensor) { 41 | battery_voltage_3_sensor_ = battery_voltage_3_sensor; 42 | } 43 | void set_auxiliary_battery_voltage_sensor(sensor::Sensor *auxiliary_battery_voltage_sensor) { 44 | auxiliary_battery_voltage_sensor_ = auxiliary_battery_voltage_sensor; 45 | } 46 | void set_midpoint_voltage_of_the_battery_bank_sensor(sensor::Sensor *midpoint_voltage_of_the_battery_bank_sensor) { 47 | midpoint_voltage_of_the_battery_bank_sensor_ = midpoint_voltage_of_the_battery_bank_sensor; 48 | } 49 | void set_midpoint_deviation_of_the_battery_bank_sensor( 50 | sensor::Sensor *midpoint_deviation_of_the_battery_bank_sensor) { 51 | midpoint_deviation_of_the_battery_bank_sensor_ = midpoint_deviation_of_the_battery_bank_sensor; 52 | } 53 | void set_battery_current_sensor(sensor::Sensor *battery_current_sensor) { 54 | battery_current_sensor_ = battery_current_sensor; 55 | } 56 | void set_battery_current_2_sensor(sensor::Sensor *battery_current_2_sensor) { 57 | battery_current_2_sensor_ = battery_current_2_sensor; 58 | } 59 | void set_battery_current_3_sensor(sensor::Sensor *battery_current_3_sensor) { 60 | battery_current_3_sensor_ = battery_current_3_sensor; 61 | } 62 | void set_ac_out_voltage_sensor(sensor::Sensor *ac_out_voltage_sensor) { 63 | ac_out_voltage_sensor_ = ac_out_voltage_sensor; 64 | } 65 | void set_ac_out_current_sensor(sensor::Sensor *ac_out_current_sensor) { 66 | ac_out_current_sensor_ = ac_out_current_sensor; 67 | } 68 | void set_ac_out_apparent_power_sensor(sensor::Sensor *ac_out_apparent_power_sensor) { 69 | ac_out_apparent_power_sensor_ = ac_out_apparent_power_sensor; 70 | } 71 | void set_load_current_sensor(sensor::Sensor *load_current_sensor) { load_current_sensor_ = load_current_sensor; } 72 | void set_day_number_sensor(sensor::Sensor *day_number_sensor) { day_number_sensor_ = day_number_sensor; } 73 | void set_charging_mode_id_sensor(sensor::Sensor *charging_mode_id_sensor) { 74 | charging_mode_id_sensor_ = charging_mode_id_sensor; 75 | } 76 | void set_error_code_sensor(sensor::Sensor *error_code_sensor) { error_code_sensor_ = error_code_sensor; } 77 | void set_warning_code_sensor(sensor::Sensor *warning_code_sensor) { warning_code_sensor_ = warning_code_sensor; } 78 | void set_tracking_mode_id_sensor(sensor::Sensor *tracking_mode_id_sensor) { 79 | tracking_mode_id_sensor_ = tracking_mode_id_sensor; 80 | } 81 | void set_device_mode_id_sensor(sensor::Sensor *device_mode_id_sensor) { 82 | device_mode_id_sensor_ = device_mode_id_sensor; 83 | } 84 | void set_dc_monitor_mode_id_sensor(sensor::Sensor *dc_monitor_mode_id_sensor) { 85 | dc_monitor_mode_id_sensor_ = dc_monitor_mode_id_sensor; 86 | } 87 | 88 | void set_charging_mode_text_sensor(text_sensor::TextSensor *charging_mode_text_sensor) { 89 | charging_mode_text_sensor_ = charging_mode_text_sensor; 90 | } 91 | void set_error_text_sensor(text_sensor::TextSensor *error_text_sensor) { error_text_sensor_ = error_text_sensor; } 92 | void set_warning_text_sensor(text_sensor::TextSensor *warning_text_sensor) { 93 | warning_text_sensor_ = warning_text_sensor; 94 | } 95 | void set_tracking_mode_text_sensor(text_sensor::TextSensor *tracking_mode_text_sensor) { 96 | tracking_mode_text_sensor_ = tracking_mode_text_sensor; 97 | } 98 | void set_device_mode_text_sensor(text_sensor::TextSensor *device_mode_text_sensor) { 99 | device_mode_text_sensor_ = device_mode_text_sensor; 100 | } 101 | void set_firmware_version_text_sensor(text_sensor::TextSensor *firmware_version_text_sensor) { 102 | firmware_version_text_sensor_ = firmware_version_text_sensor; 103 | } 104 | void set_firmware_version_24bit_text_sensor(text_sensor::TextSensor *firmware_version_24bit_text_sensor) { 105 | firmware_version_24bit_text_sensor_ = firmware_version_24bit_text_sensor; 106 | } 107 | void set_device_type_text_sensor(text_sensor::TextSensor *device_type_text_sensor) { 108 | device_type_text_sensor_ = device_type_text_sensor; 109 | } 110 | void set_serial_number_text_sensor(text_sensor::TextSensor *serial_number_text_sensor) { 111 | serial_number_text_sensor_ = serial_number_text_sensor; 112 | } 113 | void set_hardware_revision_text_sensor(text_sensor::TextSensor *hardware_revision_text_sensor) { 114 | hardware_revision_text_sensor_ = hardware_revision_text_sensor; 115 | } 116 | void set_dc_monitor_mode_text_sensor(text_sensor::TextSensor *dc_monitor_mode_text_sensor) { 117 | dc_monitor_mode_text_sensor_ = dc_monitor_mode_text_sensor; 118 | } 119 | 120 | void set_battery_temperature_sensor(sensor::Sensor *battery_temperature_sensor) { 121 | battery_temperature_sensor_ = battery_temperature_sensor; 122 | } 123 | void set_instantaneous_power_sensor(sensor::Sensor *instantaneous_power_sensor) { 124 | instantaneous_power_sensor_ = instantaneous_power_sensor; 125 | } 126 | void set_consumed_amp_hours_sensor(sensor::Sensor *consumed_amp_hours_sensor) { 127 | consumed_amp_hours_sensor_ = consumed_amp_hours_sensor; 128 | } 129 | void set_state_of_charge_sensor(sensor::Sensor *state_of_charge_sensor) { 130 | state_of_charge_sensor_ = state_of_charge_sensor; 131 | } 132 | void set_time_to_go_sensor(sensor::Sensor *time_to_go_sensor) { time_to_go_sensor_ = time_to_go_sensor; } 133 | void set_depth_of_the_deepest_discharge_sensor(sensor::Sensor *depth_of_the_deepest_discharge_sensor) { 134 | depth_of_the_deepest_discharge_sensor_ = depth_of_the_deepest_discharge_sensor; 135 | } 136 | void set_depth_of_the_last_discharge_sensor(sensor::Sensor *depth_of_the_last_discharge_sensor) { 137 | depth_of_the_last_discharge_sensor_ = depth_of_the_last_discharge_sensor; 138 | } 139 | void set_depth_of_the_average_discharge_sensor(sensor::Sensor *depth_of_the_average_discharge_sensor) { 140 | depth_of_the_average_discharge_sensor_ = depth_of_the_average_discharge_sensor; 141 | } 142 | void set_number_of_charge_cycles_sensor(sensor::Sensor *number_of_charge_cycles_sensor) { 143 | number_of_charge_cycles_sensor_ = number_of_charge_cycles_sensor; 144 | } 145 | void set_number_of_full_discharges_sensor(sensor::Sensor *number_of_full_discharges_sensor) { 146 | number_of_full_discharges_sensor_ = number_of_full_discharges_sensor; 147 | } 148 | void set_cumulative_amp_hours_drawn_sensor(sensor::Sensor *cumulative_amp_hours_drawn_sensor) { 149 | cumulative_amp_hours_drawn_sensor_ = cumulative_amp_hours_drawn_sensor; 150 | } 151 | void set_min_battery_voltage_sensor(sensor::Sensor *min_battery_voltage_sensor) { 152 | min_battery_voltage_sensor_ = min_battery_voltage_sensor; 153 | } 154 | void set_max_battery_voltage_sensor(sensor::Sensor *max_battery_voltage_sensor) { 155 | max_battery_voltage_sensor_ = max_battery_voltage_sensor; 156 | } 157 | void set_last_full_charge_sensor(sensor::Sensor *last_full_charge_sensor) { 158 | last_full_charge_sensor_ = last_full_charge_sensor; 159 | } 160 | void set_number_of_automatic_synchronizations_sensor(sensor::Sensor *number_of_automatic_synchronizations_sensor) { 161 | number_of_automatic_synchronizations_sensor_ = number_of_automatic_synchronizations_sensor; 162 | } 163 | void set_number_of_low_main_voltage_alarms_sensor(sensor::Sensor *number_of_low_main_voltage_alarms_sensor) { 164 | number_of_low_main_voltage_alarms_sensor_ = number_of_low_main_voltage_alarms_sensor; 165 | } 166 | void set_number_of_high_main_voltage_alarms_sensor(sensor::Sensor *number_of_high_main_voltage_alarms_sensor) { 167 | number_of_high_main_voltage_alarms_sensor_ = number_of_high_main_voltage_alarms_sensor; 168 | } 169 | void set_number_of_low_auxiliary_voltage_alarms_sensor( 170 | sensor::Sensor *number_of_low_auxiliary_voltage_alarms_sensor) { 171 | number_of_low_auxiliary_voltage_alarms_sensor_ = number_of_low_auxiliary_voltage_alarms_sensor; 172 | } 173 | void set_number_of_high_auxiliary_voltage_alarms_sensor( 174 | sensor::Sensor *number_of_high_auxiliary_voltage_alarms_sensor) { 175 | number_of_high_auxiliary_voltage_alarms_sensor_ = number_of_high_auxiliary_voltage_alarms_sensor; 176 | } 177 | void set_min_auxiliary_battery_voltage_sensor(sensor::Sensor *min_auxiliary_battery_voltage_sensor) { 178 | min_auxiliary_battery_voltage_sensor_ = min_auxiliary_battery_voltage_sensor; 179 | } 180 | void set_max_auxiliary_battery_voltage_sensor(sensor::Sensor *max_auxiliary_battery_voltage_sensor) { 181 | max_auxiliary_battery_voltage_sensor_ = max_auxiliary_battery_voltage_sensor; 182 | } 183 | void set_amount_of_discharged_energy_sensor(sensor::Sensor *amount_of_discharged_energy_sensor) { 184 | amount_of_discharged_energy_sensor_ = amount_of_discharged_energy_sensor; 185 | } 186 | void set_amount_of_charged_energy_sensor(sensor::Sensor *amount_of_charged_energy_sensor) { 187 | amount_of_charged_energy_sensor_ = amount_of_charged_energy_sensor; 188 | } 189 | void set_off_reason_bitmask_sensor(sensor::Sensor *off_reason_bitmask_sensor) { 190 | off_reason_bitmask_sensor_ = off_reason_bitmask_sensor; 191 | } 192 | 193 | void set_alarm_condition_active_text_sensor(text_sensor::TextSensor *alarm_condition_active_text_sensor) { 194 | alarm_condition_active_text_sensor_ = alarm_condition_active_text_sensor; 195 | } 196 | void set_alarm_reason_text_sensor(text_sensor::TextSensor *alarm_reason_text_sensor) { 197 | alarm_reason_text_sensor_ = alarm_reason_text_sensor; 198 | } 199 | void set_model_description_text_sensor(text_sensor::TextSensor *model_description_text_sensor) { 200 | model_description_text_sensor_ = model_description_text_sensor; 201 | } 202 | void set_off_reason_text_sensor(text_sensor::TextSensor *off_reason_text_sensor) { 203 | off_reason_text_sensor_ = off_reason_text_sensor; 204 | } 205 | 206 | void dump_config() override; 207 | void loop() override; 208 | 209 | float get_setup_priority() const override { return setup_priority::DATA; } 210 | 211 | protected: 212 | void handle_value_(); 213 | void publish_state_(binary_sensor::BinarySensor *binary_sensor, const bool &state); 214 | void publish_state_(sensor::Sensor *sensor, float value); 215 | void publish_state_(text_sensor::TextSensor *text_sensor, const std::string &state); 216 | void publish_state_once_(text_sensor::TextSensor *text_sensor, const std::string &state); 217 | 218 | binary_sensor::BinarySensor *load_state_binary_sensor_; 219 | binary_sensor::BinarySensor *relay_state_binary_sensor_; 220 | 221 | sensor::Sensor *max_power_yesterday_sensor_{nullptr}; 222 | sensor::Sensor *max_power_today_sensor_{nullptr}; 223 | sensor::Sensor *yield_total_sensor_{nullptr}; 224 | sensor::Sensor *yield_yesterday_sensor_{nullptr}; 225 | sensor::Sensor *yield_today_sensor_{nullptr}; 226 | sensor::Sensor *panel_voltage_sensor_{nullptr}; 227 | sensor::Sensor *panel_power_sensor_{nullptr}; 228 | sensor::Sensor *battery_voltage_sensor_{nullptr}; 229 | sensor::Sensor *battery_voltage_2_sensor_{nullptr}; 230 | sensor::Sensor *battery_voltage_3_sensor_{nullptr}; 231 | sensor::Sensor *auxiliary_battery_voltage_sensor_{nullptr}; 232 | sensor::Sensor *midpoint_voltage_of_the_battery_bank_sensor_{nullptr}; 233 | sensor::Sensor *midpoint_deviation_of_the_battery_bank_sensor_{nullptr}; 234 | sensor::Sensor *battery_current_sensor_{nullptr}; 235 | sensor::Sensor *battery_current_2_sensor_{nullptr}; 236 | sensor::Sensor *battery_current_3_sensor_{nullptr}; 237 | sensor::Sensor *ac_out_voltage_sensor_{nullptr}; 238 | sensor::Sensor *ac_out_current_sensor_{nullptr}; 239 | sensor::Sensor *ac_out_apparent_power_sensor_{nullptr}; 240 | sensor::Sensor *load_current_sensor_{nullptr}; 241 | sensor::Sensor *day_number_sensor_{nullptr}; 242 | sensor::Sensor *device_mode_sensor_{nullptr}; 243 | sensor::Sensor *charging_mode_id_sensor_{nullptr}; 244 | sensor::Sensor *error_code_sensor_{nullptr}; 245 | sensor::Sensor *warning_code_sensor_{nullptr}; 246 | sensor::Sensor *tracking_mode_id_sensor_{nullptr}; 247 | sensor::Sensor *device_mode_id_sensor_{nullptr}; 248 | sensor::Sensor *dc_monitor_mode_id_sensor_{nullptr}; 249 | sensor::Sensor *off_reason_bitmask_sensor_{nullptr}; 250 | text_sensor::TextSensor *charging_mode_text_sensor_{nullptr}; 251 | text_sensor::TextSensor *error_text_sensor_{nullptr}; 252 | text_sensor::TextSensor *warning_text_sensor_{nullptr}; 253 | text_sensor::TextSensor *tracking_mode_text_sensor_{nullptr}; 254 | text_sensor::TextSensor *device_mode_text_sensor_{nullptr}; 255 | text_sensor::TextSensor *firmware_version_text_sensor_{nullptr}; 256 | text_sensor::TextSensor *firmware_version_24bit_text_sensor_{nullptr}; 257 | text_sensor::TextSensor *device_type_text_sensor_{nullptr}; 258 | text_sensor::TextSensor *serial_number_text_sensor_{nullptr}; 259 | text_sensor::TextSensor *hardware_revision_text_sensor_{nullptr}; 260 | text_sensor::TextSensor *dc_monitor_mode_text_sensor_{nullptr}; 261 | text_sensor::TextSensor *off_reason_text_sensor_{nullptr}; 262 | 263 | sensor::Sensor *battery_temperature_sensor_{nullptr}; 264 | sensor::Sensor *instantaneous_power_sensor_{nullptr}; 265 | sensor::Sensor *consumed_amp_hours_sensor_{nullptr}; 266 | sensor::Sensor *state_of_charge_sensor_{nullptr}; 267 | sensor::Sensor *time_to_go_sensor_{nullptr}; 268 | sensor::Sensor *depth_of_the_deepest_discharge_sensor_{nullptr}; 269 | sensor::Sensor *depth_of_the_last_discharge_sensor_{nullptr}; 270 | sensor::Sensor *depth_of_the_average_discharge_sensor_{nullptr}; 271 | sensor::Sensor *number_of_charge_cycles_sensor_{nullptr}; 272 | sensor::Sensor *number_of_full_discharges_sensor_{nullptr}; 273 | sensor::Sensor *cumulative_amp_hours_drawn_sensor_{nullptr}; 274 | sensor::Sensor *min_battery_voltage_sensor_{nullptr}; 275 | sensor::Sensor *max_battery_voltage_sensor_{nullptr}; 276 | sensor::Sensor *last_full_charge_sensor_{nullptr}; 277 | sensor::Sensor *number_of_automatic_synchronizations_sensor_{nullptr}; 278 | sensor::Sensor *number_of_low_main_voltage_alarms_sensor_{nullptr}; 279 | sensor::Sensor *number_of_high_main_voltage_alarms_sensor_{nullptr}; 280 | sensor::Sensor *number_of_low_auxiliary_voltage_alarms_sensor_{nullptr}; 281 | sensor::Sensor *number_of_high_auxiliary_voltage_alarms_sensor_{nullptr}; 282 | sensor::Sensor *min_auxiliary_battery_voltage_sensor_{nullptr}; 283 | sensor::Sensor *max_auxiliary_battery_voltage_sensor_{nullptr}; 284 | sensor::Sensor *amount_of_discharged_energy_sensor_{nullptr}; 285 | sensor::Sensor *amount_of_charged_energy_sensor_{nullptr}; 286 | text_sensor::TextSensor *alarm_condition_active_text_sensor_{nullptr}; 287 | text_sensor::TextSensor *alarm_reason_text_sensor_{nullptr}; 288 | text_sensor::TextSensor *model_description_text_sensor_{nullptr}; 289 | 290 | bool publishing_{true}; 291 | int state_{0}; 292 | std::string label_; 293 | std::string value_; 294 | uint32_t last_transmission_{0}; 295 | uint32_t last_publish_{0}; 296 | uint32_t throttle_{0}; 297 | }; 298 | 299 | } // namespace victron 300 | } // namespace esphome 301 | -------------------------------------------------------------------------------- /debug-esp32-example.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: victron-debug 3 | external_components_source: github://KinDR007/VictronMPPT-ESPHOME@main 4 | tx_pin: GPIO16 5 | rx_pin: GPIO17 6 | 7 | esphome: 8 | name: ${name} 9 | min_version: 2024.6.0 10 | 11 | esp32: 12 | board: wemos_d1_mini32 13 | framework: 14 | type: esp-idf 15 | 16 | external_components: 17 | - source: ${external_components_source} 18 | refresh: 0s 19 | 20 | wifi: 21 | ssid: !secret wifi_ssid 22 | password: !secret wifi_password 23 | 24 | ota: 25 | platform: esphome 26 | 27 | logger: 28 | 29 | api: 30 | reboot_timeout: 0s 31 | 32 | uart: 33 | id: uart_0 34 | baud_rate: 19200 35 | tx_pin: ${tx_pin} 36 | rx_pin: ${rx_pin} 37 | debug: 38 | direction: BOTH 39 | dummy_receiver: false 40 | after: 41 | delimiter: "\n" 42 | sequence: 43 | - lambda: UARTDebug::log_string(direction, bytes); 44 | 45 | victron: 46 | uart_id: uart_0 47 | id: victron0 48 | throttle: 10s 49 | 50 | text_sensor: 51 | - platform: victron 52 | victron_id: victron0 53 | model_description: 54 | name: "${name} model description" 55 | firmware_version: 56 | name: "${name} firmware version" 57 | device_type: 58 | name: "${name} device type" 59 | serial_number: 60 | name: "${name} serial number" 61 | -------------------------------------------------------------------------------- /debug-esp8266-example.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: victron-debug 3 | external_components_source: github://KinDR007/VictronMPPT-ESPHOME@main 4 | tx_pin: D8 5 | rx_pin: D7 6 | 7 | esphome: 8 | name: ${name} 9 | min_version: 2024.6.0 10 | 11 | esp8266: 12 | board: d1_mini 13 | 14 | external_components: 15 | - source: ${external_components_source} 16 | refresh: 0s 17 | 18 | wifi: 19 | ssid: !secret wifi_ssid 20 | password: !secret wifi_password 21 | 22 | ota: 23 | platform: esphome 24 | 25 | logger: 26 | baud_rate: 0 27 | esp8266_store_log_strings_in_flash: false 28 | 29 | api: 30 | reboot_timeout: 0s 31 | 32 | uart: 33 | id: uart_0 34 | baud_rate: 19200 35 | tx_pin: ${tx_pin} 36 | rx_pin: ${rx_pin} 37 | debug: 38 | direction: BOTH 39 | dummy_receiver: false 40 | after: 41 | delimiter: "\n" 42 | sequence: 43 | - lambda: UARTDebug::log_string(direction, bytes); 44 | 45 | victron: 46 | uart_id: uart_0 47 | id: victron0 48 | throttle: 10s 49 | 50 | text_sensor: 51 | - platform: victron 52 | victron_id: victron0 53 | model_description: 54 | name: "${name} model description" 55 | firmware_version: 56 | name: "${name} firmware version" 57 | device_type: 58 | name: "${name} device type" 59 | serial_number: 60 | name: "${name} serial number" 61 | -------------------------------------------------------------------------------- /docs/smartsolar-mppt-example-pdus.txt: -------------------------------------------------------------------------------- 1 | [16:43:13][D][uart_debug:158]: <<< "PID\t0xA053\r\n" 2 | [16:43:13][D][uart_debug:158]: <<< "FW\t156\r\n" 3 | [16:43:13][D][uart_debug:158]: <<< "SER#\tHQ1942K7LJ8\r\n" 4 | [16:43:13][D][uart_debug:158]: <<< "V\t12450\r\n" 5 | [16:43:13][D][uart_debug:158]: <<< "I\t0\r\n" 6 | [16:43:13][D][uart_debug:158]: <<< "VPV\t10\r\n" 7 | [16:43:13][D][uart_debug:158]: <<< "PPV\t0\r\n" 8 | [16:43:13][D][uart_debug:158]: <<< "CS\t0\r\n" 9 | [16:43:13][D][uart_debug:158]: <<< "MPPT\t0\r\n" 10 | [16:43:13][D][uart_debug:158]: <<< "OR\t0x00000001\r\n" 11 | [16:43:13][D][uart_debug:158]: <<< "ERR\t0\r\n" 12 | [16:43:13][D][uart_debug:158]: <<< "LOAD\tON\r\n" 13 | [16:43:13][D][uart_debug:158]: <<< "IL\t0\r\n" 14 | [16:43:13][D][uart_debug:158]: <<< "H19\t3\r\n" 15 | [16:43:13][D][uart_debug:158]: <<< "H20\t0\r\n" 16 | [16:43:13][D][uart_debug:158]: <<< "H21\t24\r\n" 17 | [16:43:13][D][uart_debug:158]: <<< "H22\t1\r\n" 18 | [16:43:13][D][uart_debug:158]: <<< "H23\t15\r\n" 19 | [16:43:13][D][uart_debug:158]: <<< "HSDS\t3\r\n" 20 | [16:43:13][D][uart_debug:158]: <<< "Checksum\t\xE1" 21 | [16:43:14][D][uart_debug:158]: <<< "\r\n" 22 | [16:43:14][D][uart_debug:158]: <<< "PID\t0xA053\r\n" 23 | [16:43:14][D][uart_debug:158]: <<< "FW\t156\r\n" 24 | [16:43:14][D][uart_debug:158]: <<< "SER#\tHQ1942K7LJ8\r\n" 25 | [16:43:14][D][uart_debug:158]: <<< "V\t12450\r\n" 26 | [16:43:14][D][uart_debug:158]: <<< "I\t0\r\n" 27 | [16:43:14][D][uart_debug:158]: <<< "VPV\t10\r\n" 28 | [16:43:14][D][uart_debug:158]: <<< "PPV\t0\r\n" 29 | [16:43:14][D][uart_debug:158]: <<< "CS\t0\r\n" 30 | [16:43:14][D][uart_debug:158]: <<< "MPPT\t0\r\n" 31 | [16:43:14][D][uart_debug:158]: <<< "OR\t0x00000001\r\n" 32 | [16:43:14][D][uart_debug:158]: <<< "ERR\t0\r\n" 33 | [16:43:14][D][uart_debug:158]: <<< "LOAD\tON\r\n" 34 | [16:43:14][D][uart_debug:158]: <<< "IL\t0\r\n" 35 | [16:43:14][D][uart_debug:158]: <<< "H19\t3\r\n" 36 | [16:43:14][D][uart_debug:158]: <<< "H20\t0\r\n" 37 | [16:43:14][D][uart_debug:158]: <<< "H21\t24\r\n" 38 | [16:43:14][D][uart_debug:158]: <<< "H22\t1\r\n" 39 | [16:43:14][D][uart_debug:158]: <<< "H23\t15\r\n" 40 | [16:43:14][D][uart_debug:158]: <<< "HSDS\t3\r\n" 41 | [16:43:14][D][uart_debug:158]: <<< "Checksum\t\xE1" 42 | [16:43:15][D][uart_debug:158]: <<< "\r\n" 43 | [16:43:15][D][uart_debug:158]: <<< "PID\t0xA053\r\n" 44 | [16:43:15][D][uart_debug:158]: <<< "FW\t156\r\n" 45 | [16:43:15][D][uart_debug:158]: <<< "SER#\tHQ1942K7LJ8\r\n" 46 | [16:43:15][D][uart_debug:158]: <<< "V\t12450\r\n" 47 | [16:43:15][D][uart_debug:158]: <<< "I\t0\r\n" 48 | [16:43:15][D][uart_debug:158]: <<< "VPV\t10\r\n" 49 | [16:43:15][D][uart_debug:158]: <<< "PPV\t0\r\n" 50 | [16:43:15][D][uart_debug:158]: <<< "CS\t0\r\n" 51 | [16:43:15][D][uart_debug:158]: <<< "MPPT\t0\r\n" 52 | [16:43:15][D][uart_debug:158]: <<< "OR\t0x00000001\r\n" 53 | [16:43:15][D][uart_debug:158]: <<< "ERR\t0\r\n" 54 | [16:43:15][D][uart_debug:158]: <<< "LOAD\tON\r\n" 55 | [16:43:15][D][uart_debug:158]: <<< "IL\t0\r\n" 56 | [16:43:15][D][uart_debug:158]: <<< "H19\t3\r\n" 57 | [16:43:15][D][uart_debug:158]: <<< "H20\t0\r\n" 58 | [16:43:15][D][uart_debug:158]: <<< "H21\t24\r\n" 59 | [16:43:15][D][uart_debug:158]: <<< "H22\t1\r\n" 60 | [16:43:15][D][uart_debug:158]: <<< "H23\t15\r\n" 61 | [16:43:15][D][uart_debug:158]: <<< "HSDS\t3\r\n" 62 | [16:43:15][D][uart_debug:158]: <<< "Checksum\t\xE1" 63 | [16:43:16][D][uart_debug:158]: <<< "\r\n" 64 | [16:43:16][D][uart_debug:158]: <<< "PID\t0xA053\r\n" 65 | [16:43:16][D][uart_debug:158]: <<< "FW\t156\r\n" 66 | [16:43:16][D][uart_debug:158]: <<< "SER#\tHQ1942K7LJ8\r\n" 67 | [16:43:16][D][uart_debug:158]: <<< "V\t12450\r\n" 68 | [16:43:16][D][uart_debug:158]: <<< "I\t0\r\n" 69 | [16:43:16][D][uart_debug:158]: <<< "VPV\t10\r\n" 70 | [16:43:16][D][uart_debug:158]: <<< "PPV\t0\r\n" 71 | [16:43:16][D][uart_debug:158]: <<< "CS\t0\r\n" 72 | [16:43:16][D][uart_debug:158]: <<< "MPPT\t0\r\n" 73 | [16:43:16][D][uart_debug:158]: <<< "OR\t0x00000001\r\n" 74 | [16:43:16][D][uart_debug:158]: <<< "ERR\t0\r\n" 75 | [16:43:16][D][uart_debug:158]: <<< "LOAD\tON\r\n" 76 | [16:43:16][D][uart_debug:158]: <<< "IL\t0\r\n" 77 | [16:43:16][D][uart_debug:158]: <<< "H19\t3\r\n" 78 | [16:43:16][D][uart_debug:158]: <<< "H20\t0\r\n" 79 | [16:43:16][D][uart_debug:158]: <<< "H21\t24\r\n" 80 | [16:43:16][D][uart_debug:158]: <<< "H22\t1\r\n" 81 | [16:43:16][D][uart_debug:158]: <<< "H23\t15\r\n" 82 | [16:43:16][D][uart_debug:158]: <<< "HSDS\t3\r\n" 83 | [16:43:16][D][uart_debug:158]: <<< "Checksum\t\xE1" 84 | [16:43:17][D][uart_debug:158]: <<< "\r\n" 85 | [16:43:17][D][uart_debug:158]: <<< "PID\t0xA053\r\n" 86 | [16:43:17][D][uart_debug:158]: <<< "FW\t156\r\n" 87 | [16:43:17][D][uart_debug:158]: <<< "SER#\tHQ1942K7LJ8\r\n" 88 | [16:43:17][D][uart_debug:158]: <<< "V\t12450\r\n" 89 | [16:43:17][D][uart_debug:158]: <<< "I\t0\r\n" 90 | [16:43:17][D][uart_debug:158]: <<< "VPV\t10\r\n" 91 | [16:43:17][D][uart_debug:158]: <<< "PPV\t0\r\n" 92 | [16:43:17][D][uart_debug:158]: <<< "CS\t0\r\n" 93 | [16:43:17][D][uart_debug:158]: <<< "MPPT\t0\r\n" 94 | [16:43:17][D][uart_debug:158]: <<< "OR\t0x00000001\r\n" 95 | [16:43:17][D][uart_debug:158]: <<< "ERR\t0\r\n" 96 | [16:43:17][D][uart_debug:158]: <<< "LOAD\tON\r\n" 97 | [16:43:17][D][uart_debug:158]: <<< "IL\t0\r\n" 98 | [16:43:17][D][uart_debug:158]: <<< "H19\t3\r\n" 99 | [16:43:17][D][uart_debug:158]: <<< "H20\t0\r\n" 100 | [16:43:17][D][uart_debug:158]: <<< "H21\t24\r\n" 101 | [16:43:17][D][uart_debug:158]: <<< "H22\t1\r\n" 102 | [16:43:17][D][uart_debug:158]: <<< "H23\t15\r\n" 103 | [16:43:17][D][uart_debug:158]: <<< "HSDS\t3\r\n" 104 | [16:43:17][D][uart_debug:158]: <<< "Checksum\t\xE1" 105 | [16:43:18][D][uart_debug:158]: <<< "\r\n" 106 | [16:43:18][D][uart_debug:158]: <<< "PID\t0xA053\r\n" 107 | [16:43:18][D][uart_debug:158]: <<< "FW\t156\r\n" 108 | [16:43:18][D][uart_debug:158]: <<< "SER#\tHQ1942K7LJ8\r\n" 109 | [16:43:18][D][uart_debug:158]: <<< "V\t12450\r\n" 110 | [16:43:18][D][uart_debug:158]: <<< "I\t0\r\n" 111 | [16:43:18][D][uart_debug:158]: <<< "VPV\t10\r\n" 112 | [16:43:18][D][uart_debug:158]: <<< "PPV\t0\r\n" 113 | [16:43:18][D][uart_debug:158]: <<< "CS\t0\r\n" 114 | [16:43:18][D][uart_debug:158]: <<< "MPPT\t0\r\n" 115 | [16:43:18][D][uart_debug:158]: <<< "OR\t0x00000001\r\n" 116 | [16:43:18][D][uart_debug:158]: <<< "ERR\t0\r\n" 117 | [16:43:18][D][uart_debug:158]: <<< "LOAD\tON\r\n" 118 | [16:43:18][D][uart_debug:158]: <<< "IL\t0\r\n" 119 | [16:43:18][D][uart_debug:158]: <<< "H19\t3\r\n" 120 | [16:43:18][D][uart_debug:158]: <<< "H20\t0\r\n" 121 | [16:43:18][D][uart_debug:158]: <<< "H21\t24\r\n" 122 | [16:43:18][D][uart_debug:158]: <<< "H22\t1\r\n" 123 | [16:43:18][D][uart_debug:158]: <<< "H23\t15\r\n" 124 | [16:43:18][D][uart_debug:158]: <<< "HSDS\t3\r\n" 125 | [16:43:18][D][uart_debug:158]: <<< "Checksum\t\xE1" 126 | [16:43:19][D][uart_debug:158]: <<< "\r\n" 127 | [16:43:19][D][uart_debug:158]: <<< "PID\t0xA053\r\n" 128 | [16:43:19][D][uart_debug:158]: <<< "FW\t156\r\n" 129 | [16:43:19][D][uart_debug:158]: <<< "SER#\tHQ1942K7LJ8\r\n" 130 | [16:43:19][D][uart_debug:158]: <<< "V\t12450\r\n" 131 | [16:43:19][D][uart_debug:158]: <<< "I\t0\r\n" 132 | [16:43:19][D][uart_debug:158]: <<< "VPV\t10\r\n" 133 | [16:43:19][D][uart_debug:158]: <<< "PPV\t0\r\n" 134 | [16:43:19][D][uart_debug:158]: <<< "CS\t0\r\n" 135 | [16:43:19][D][uart_debug:158]: <<< "MPPT\t0\r\n" 136 | [16:43:19][D][uart_debug:158]: <<< "OR\t0x00000001\r\n" 137 | [16:43:19][D][uart_debug:158]: <<< "ERR\t0\r\n" 138 | [16:43:19][D][uart_debug:158]: <<< "LOAD\tON\r\n" 139 | [16:43:19][D][uart_debug:158]: <<< "IL\t0\r\n" 140 | [16:43:19][D][uart_debug:158]: <<< "H19\t3\r\n" 141 | [16:43:19][D][uart_debug:158]: <<< "H20\t0\r\n" 142 | [16:43:19][D][uart_debug:158]: <<< "H21\t24\r\n" 143 | [16:43:19][D][uart_debug:158]: <<< "H22\t1\r\n" 144 | [16:43:19][D][uart_debug:158]: <<< "H23\t15\r\n" 145 | [16:43:19][D][uart_debug:158]: <<< "HSDS\t3\r\n" 146 | [16:43:19][D][uart_debug:158]: <<< "Checksum\t\xE1" 147 | -------------------------------------------------------------------------------- /images/circuit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KinDR007/VictronMPPT-ESPHOME/0e5efa1497a2a6ef4db8348fe82e1507f4ba3e19/images/circuit.jpg -------------------------------------------------------------------------------- /images/circuit_thumbnail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KinDR007/VictronMPPT-ESPHOME/0e5efa1497a2a6ef4db8348fe82e1507f4ba3e19/images/circuit_thumbnail.jpg -------------------------------------------------------------------------------- /multi-rs-esp8266-example.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: multi-rs 3 | external_components_source: github://KinDR007/VictronMPPT-ESPHOME@main 4 | 5 | esphome: 6 | name: ${name} 7 | min_version: 2024.6.0 8 | 9 | esp8266: 10 | board: d1_mini 11 | 12 | external_components: 13 | - source: ${external_components_source} 14 | refresh: 0s 15 | 16 | wifi: 17 | ssid: !secret wifi_ssid 18 | password: !secret wifi_password 19 | 20 | ota: 21 | platform: esphome 22 | 23 | logger: 24 | baud_rate: 0 25 | esp8266_store_log_strings_in_flash: false 26 | 27 | # If you use Home Assistant please remove this `mqtt` section and uncomment the `api` component! 28 | # The native API has many advantages over MQTT: https://esphome.io/components/api.html#advantages-over-mqtt 29 | mqtt: 30 | broker: !secret mqtt_host 31 | username: !secret mqtt_username 32 | password: !secret mqtt_password 33 | id: mqtt_client 34 | 35 | # api: 36 | 37 | uart: 38 | id: uart_0 39 | tx_pin: D8 # Not connected! The communication is read-only 40 | rx_pin: D7 # Connect this this GPIO and GND to the MPPT charger 41 | baud_rate: 19200 42 | rx_buffer_size: 256 43 | 44 | victron: 45 | uart_id: uart_0 46 | id: victron0 47 | throttle: 10s 48 | 49 | sensor: 50 | - platform: victron 51 | victron_id: victron0 52 | charging_mode_id: 53 | name: "${name} charging mode id" 54 | error_code: 55 | name: "${name} error code" 56 | ac_out_current: 57 | name: "${name} ac out current" 58 | id: ac_current 59 | ac_out_voltage: 60 | name: "${name} ac out voltage" 61 | id: ac_voltage 62 | yield_total: 63 | name: "${name} yield total" 64 | yield_today: 65 | name: "${name} yield today" 66 | max_power_today: 67 | name: "${name} max power today" 68 | yield_yesterday: 69 | name: "${name} yield yesterday" 70 | max_power_yesterday: 71 | name: "${name} max power yesterday" 72 | day_number: 73 | name: "${name} day number" 74 | battery_current: 75 | name: "${name} battery current" 76 | panel_power: 77 | name: "${name} panel power" 78 | battery_voltage: 79 | name: "${name} battery voltage" 80 | panel_voltage: 81 | name: "${name} panel voltage" 82 | 83 | - platform: template 84 | name: "${name} ac out power" 85 | # https://github.com/esphome/issues/issues/2793#issuecomment-988257209 86 | lambda: |- 87 | auto val = id(ac_voltage).state * id(ac_current).state; 88 | if (isnan(val)) 89 | return {}; 90 | return val; 91 | update_interval: 10s 92 | 93 | text_sensor: 94 | - platform: victron 95 | victron_id: victron0 96 | device_type: 97 | name: "${name} device type" 98 | serial_number: 99 | name: "${name} serial number" 100 | firmware_version_24bit: 101 | name: "${name} firmware version 24bit" 102 | charging_mode: 103 | name: "${name} charging mode" 104 | error: 105 | name: "${name} error" 106 | device_mode: 107 | name: "${name} device mode" 108 | -------------------------------------------------------------------------------- /phoenix-charger-esp8266-example.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: phoenix-charger 3 | external_components_source: github://KinDR007/VictronMPPT-ESPHOME@main 4 | 5 | esphome: 6 | name: ${name} 7 | min_version: 2024.6.0 8 | 9 | esp8266: 10 | board: d1_mini 11 | 12 | external_components: 13 | - source: ${external_components_source} 14 | refresh: 0s 15 | 16 | wifi: 17 | ssid: !secret wifi_ssid 18 | password: !secret wifi_password 19 | 20 | ota: 21 | platform: esphome 22 | 23 | logger: 24 | baud_rate: 0 25 | esp8266_store_log_strings_in_flash: false 26 | 27 | # If you use Home Assistant please remove this `mqtt` section and uncomment the `api` component! 28 | # The native API has many advantages over MQTT: https://esphome.io/components/api.html#advantages-over-mqtt 29 | mqtt: 30 | broker: !secret mqtt_host 31 | username: !secret mqtt_username 32 | password: !secret mqtt_password 33 | id: mqtt_client 34 | 35 | # api: 36 | 37 | uart: 38 | id: uart_0 39 | tx_pin: D8 # Not connected! The communication is read-only 40 | rx_pin: D7 # Connect this this GPIO and GND to the MPPT charger 41 | baud_rate: 19200 42 | rx_buffer_size: 256 43 | 44 | victron: 45 | uart_id: uart_0 46 | id: victron0 47 | throttle: 10s 48 | 49 | sensor: 50 | - platform: victron 51 | victron_id: victron0 52 | battery_voltage: 53 | name: "${name} battery voltage" 54 | battery_voltage_2: 55 | name: "${name} battery voltage 2" 56 | battery_voltage_3: 57 | name: "${name} battery voltage 3" 58 | battery_current: 59 | name: "${name} battery current" 60 | battery_current_2: 61 | name: "${name} battery current 2" 62 | battery_current_3: 63 | name: "${name} battery current 3" 64 | error_code: 65 | name: "${name} error code" 66 | charging_mode_id: 67 | name: "${name} charging mode id" 68 | device_mode_id: 69 | name: "${name} device mode id" 70 | 71 | text_sensor: 72 | - platform: victron 73 | victron_id: victron0 74 | error: 75 | name: "${name} error" 76 | charging_mode: 77 | name: "${name} charging mode" 78 | device_type: 79 | name: "${name} device type" 80 | serial_number: 81 | name: "${name} serial number" 82 | device_mode: 83 | name: "${name} device mode" 84 | 85 | binary_sensor: 86 | - platform: victron 87 | victron_id: victron0 88 | relay_state: 89 | name: "${name} relay state" 90 | -------------------------------------------------------------------------------- /phoenix-inverter-esp8266-example.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: phoenix-inverter 3 | external_components_source: github://KinDR007/VictronMPPT-ESPHOME@main 4 | 5 | esphome: 6 | name: ${name} 7 | min_version: 2024.6.0 8 | 9 | esp8266: 10 | board: d1_mini 11 | 12 | external_components: 13 | - source: ${external_components_source} 14 | refresh: 0s 15 | 16 | wifi: 17 | ssid: !secret wifi_ssid 18 | password: !secret wifi_password 19 | 20 | ota: 21 | platform: esphome 22 | 23 | logger: 24 | baud_rate: 0 25 | esp8266_store_log_strings_in_flash: false 26 | 27 | # If you use Home Assistant please remove this `mqtt` section and uncomment the `api` component! 28 | # The native API has many advantages over MQTT: https://esphome.io/components/api.html#advantages-over-mqtt 29 | mqtt: 30 | broker: !secret mqtt_host 31 | username: !secret mqtt_username 32 | password: !secret mqtt_password 33 | id: mqtt_client 34 | 35 | # api: 36 | 37 | uart: 38 | id: uart_0 39 | tx_pin: D8 # Not connected! The communication is read-only 40 | rx_pin: D7 # Connect this this GPIO and GND to the MPPT charger 41 | baud_rate: 19200 42 | rx_buffer_size: 256 43 | 44 | victron: 45 | uart_id: uart_0 46 | id: victron0 47 | throttle: 10s 48 | 49 | sensor: 50 | - platform: victron 51 | victron_id: victron0 52 | battery_voltage: 53 | name: "${name} battery voltage" 54 | battery_current: 55 | name: "${name} battery current" 56 | charging_mode_id: 57 | name: "${name} charging mode id" 58 | device_mode_id: 59 | name: "${name} device mode id" 60 | ac_out_voltage: 61 | name: "${name} ac out voltage" 62 | ac_out_current: 63 | name: "${name} ac out current" 64 | ac_out_apparent_power: 65 | name: "${name} ac out apparent power" 66 | warning_code: 67 | name: "${name} warning code" 68 | off_reason_bitmask: 69 | name: "${name} off reason bitmask" 70 | 71 | text_sensor: 72 | - platform: victron 73 | victron_id: victron0 74 | alarm_reason: 75 | name: "${name} alarm reason" 76 | # TODO: "OR" 77 | charging_mode: 78 | name: "${name} charging mode" 79 | firmware_version: 80 | name: "${name} firmware version" 81 | device_type: 82 | name: "${name} device type" 83 | serial_number: 84 | name: "${name} serial number" 85 | device_mode: 86 | name: "${name} device mode" 87 | warning: 88 | name: "${name} warning" 89 | off_reason: 90 | name: "${name} off reason" 91 | 92 | binary_sensor: 93 | - platform: victron 94 | victron_id: victron0 95 | relay_state: 96 | name: "${name} relay state" 97 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 120 3 | # Following 4 for black compatibility 4 | # E501: line too long 5 | # W503: Line break occurred before a binary operator 6 | # E203: Whitespace before ':' 7 | # D202 No blank lines allowed after function docstring 8 | 9 | # TODO fix flake8 10 | # D100 Missing docstring in public module 11 | # D101 Missing docstring in public class 12 | # D102 Missing docstring in public method 13 | # D103 Missing docstring in public function 14 | # D104 Missing docstring in public package 15 | # D105 Missing docstring in magic method 16 | # D107 Missing docstring in __init__ 17 | # D200 One-line docstring should fit on one line with quotes 18 | # D205 1 blank line required between summary line and description 19 | # D209 Multi-line docstring closing quotes should be on a separate line 20 | # D400 First line should end with a period 21 | # D401 First line should be in imperative mood 22 | 23 | ignore = 24 | E501, 25 | W503, 26 | E203, 27 | D202, 28 | 29 | D100, 30 | D101, 31 | D102, 32 | D103, 33 | D104, 34 | D105, 35 | D107, 36 | D200, 37 | D205, 38 | D209, 39 | D400, 40 | D401, 41 | 42 | [isort] 43 | # https://github.com/timothycrosley/isort 44 | # https://github.com/timothycrosley/isort/wiki/isort-Settings 45 | # splits long import on multiple lines indented by 4 spaces 46 | multi_line_output = 3 47 | include_trailing_comma=True 48 | force_grid_wrap=0 49 | use_parentheses=True 50 | line_length=88 51 | indent = " " 52 | # will group `import x` and `from x import` of the same module. 53 | force_sort_within_sections = true 54 | sections = FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER 55 | default_section = THIRDPARTY 56 | known_first_party = custom_components,tests 57 | forced_separate = tests 58 | combine_as_imports = true 59 | -------------------------------------------------------------------------------- /smartshunt-esp8266-example.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: victron-smartshunt 3 | external_components_source: github://KinDR007/VictronMPPT-ESPHOME@main 4 | 5 | esphome: 6 | name: ${name} 7 | min_version: 2024.6.0 8 | 9 | esp8266: 10 | board: d1_mini 11 | 12 | external_components: 13 | - source: ${external_components_source} 14 | refresh: 0s 15 | 16 | wifi: 17 | ssid: !secret wifi_ssid 18 | password: !secret wifi_password 19 | 20 | ota: 21 | platform: esphome 22 | 23 | logger: 24 | baud_rate: 0 25 | esp8266_store_log_strings_in_flash: false 26 | 27 | # If you use Home Assistant please remove this `mqtt` section and uncomment the `api` component! 28 | # The native API has many advantages over MQTT: https://esphome.io/components/api.html#advantages-over-mqtt 29 | mqtt: 30 | broker: !secret mqtt_host 31 | username: !secret mqtt_username 32 | password: !secret mqtt_password 33 | id: mqtt_client 34 | 35 | # api: 36 | 37 | uart: 38 | id: uart_0 39 | tx_pin: D8 # Not connected! The communication is read-only 40 | rx_pin: D7 # Connect this this GPIO and GND to the MPPT charger 41 | baud_rate: 19200 42 | rx_buffer_size: 256 43 | 44 | victron: 45 | uart_id: uart_0 46 | id: victron0 47 | throttle: 10s 48 | 49 | sensor: 50 | - platform: victron 51 | victron_id: victron0 52 | battery_voltage: 53 | name: "${name} battery voltage" 54 | auxiliary_battery_voltage: 55 | name: "${name} auxiliary battery voltage" 56 | midpoint_voltage_of_the_battery_bank: 57 | name: "${name} midpoint voltage of the battery bank" 58 | midpoint_deviation_of_the_battery_bank: 59 | name: "${name} midpoint deviation of the battery bank" 60 | battery_current: 61 | name: "${name} battery current" 62 | battery_temperature: 63 | name: "${name} battery temperature" 64 | instantaneous_power: 65 | name: "${name} instantaneous power" 66 | consumed_amp_hours: 67 | name: "${name} consumed amp hours" 68 | state_of_charge: 69 | name: "${name} state of charge" 70 | time_to_go: 71 | name: "${name} time to go" 72 | depth_of_the_deepest_discharge: 73 | name: "${name} depth of the deepest discharge" 74 | depth_of_the_last_discharge: 75 | name: "${name} depth of the last discharge" 76 | depth_of_the_average_discharge: 77 | name: "${name} depth of the average discharge" 78 | number_of_charge_cycles: 79 | name: "${name} number of charge cycles" 80 | number_of_full_discharges: 81 | name: "${name} number of full discharges" 82 | cumulative_amp_hours_drawn: 83 | name: "${name} cumulative amp hours drawn" 84 | min_battery_voltage: 85 | name: "${name} min battery voltage" 86 | max_battery_voltage: 87 | name: "${name} max battery voltage" 88 | last_full_charge: 89 | name: "${name} last full charge" 90 | number_of_automatic_synchronizations: 91 | name: "${name} number of automatic synchronizations" 92 | number_of_low_main_voltage_alarms: 93 | name: "${name} number of low main voltage alarms" 94 | number_of_high_main_voltage_alarms: 95 | name: "${name} number of high main voltage alarms" 96 | number_of_low_auxiliary_voltage_alarms: 97 | name: "${name} number of low auxiliary voltage alarms" 98 | number_of_high_auxiliary_voltage_alarms: 99 | name: "${name} number of high auxiliary voltage alarms" 100 | min_auxiliary_battery_voltage: 101 | name: "${name} min auxiliary battery voltage" 102 | max_auxiliary_battery_voltage: 103 | name: "${name} max auxiliary battery voltage" 104 | amount_of_discharged_energy: 105 | name: "${name} amount of discharged energy" 106 | amount_of_charged_energy: 107 | name: "${name} amount of charged energy" 108 | dc_monitor_mode_id: 109 | name: "${name} dc monitor mode id" 110 | 111 | text_sensor: 112 | - platform: victron 113 | victron_id: victron0 114 | alarm_condition_active: 115 | name: "${name} alarm condition active" 116 | alarm_reason: 117 | name: "${name} alarm reason" 118 | model_description: 119 | name: "${name} model description" 120 | firmware_version: 121 | name: "${name} firmware version" 122 | device_type: 123 | name: "${name} device type" 124 | serial_number: 125 | name: "${name} serial number" 126 | dc_monitor_mode: 127 | name: "${name} dc monitor mode" 128 | 129 | binary_sensor: 130 | - platform: victron 131 | victron_id: victron0 132 | relay_state: 133 | name: "${name} relay state" 134 | -------------------------------------------------------------------------------- /smartsolar-mppt-esp8266-example-advanced.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | external_components_source: github://KinDR007/VictronMPPT-ESPHOME@main 3 | lower_devicename: "victron" 4 | devicename: "SmartSolar_150_35" 5 | config_version: "v2024.06.21" 6 | wifi_fast_connect: "false" 7 | accuracy: "2" 8 | 9 | esphome: 10 | name: "${lower_devicename}" 11 | min_version: 2024.6.0 12 | 13 | esp8266: 14 | board: d1_mini 15 | 16 | external_components: 17 | - source: ${external_components_source} 18 | refresh: 0s 19 | 20 | wifi: 21 | ssid: !secret wifi_ssid 22 | password: !secret wifi_password 23 | power_save_mode: none 24 | fast_connect: $wifi_fast_connect 25 | 26 | # Enable fallback hotspot (captive portal) in case wifi connection fails 27 | ap: 28 | ssid: "${devicename} Hotspot" 29 | password: !secret wifi_password 30 | 31 | captive_portal: 32 | 33 | ota: 34 | platform: esphome 35 | 36 | logger: 37 | baud_rate: 0 38 | # level: VERY_VERBOSE 39 | esp8266_store_log_strings_in_flash: false 40 | 41 | # If you use Home Assistant please remove this `mqtt` section and uncomment the `api` component! 42 | # The native API has many advantages over MQTT: https://esphome.io/components/api.html#advantages-over-mqtt 43 | mqtt: 44 | broker: !secret mqtt_host 45 | username: !secret mqtt_username 46 | password: !secret mqtt_password 47 | id: mqtt_client 48 | 49 | # api: 50 | 51 | web_server: 52 | port: 80 53 | 54 | time: 55 | - platform: sntp 56 | 57 | uart: 58 | id: uart_bus 59 | tx_pin: D8 # Not connected! The communication is read-only 60 | rx_pin: D7 # Connect this this GPIO and GND to the MPPT charger 61 | baud_rate: 19200 62 | stop_bits: 1 63 | data_bits: 8 64 | parity: NONE 65 | rx_buffer_size: 256 66 | 67 | victron: 68 | id: victron0 69 | uart_id: uart_bus 70 | throttle: 10s 71 | 72 | sensor: 73 | - platform: victron 74 | victron_id: victron0 75 | panel_voltage: 76 | name: "Panel voltage" 77 | id: pv 78 | battery_voltage: 79 | name: "Battery voltage" 80 | id: bv 81 | battery_current: 82 | name: "Battery current" 83 | id: bc 84 | load_current: 85 | name: "Load current" 86 | id: load_current 87 | max_power_yesterday: 88 | name: "Max power yesterday" 89 | id: max_power_yesterday 90 | max_power_today: 91 | name: "Max power today" 92 | id: max_power_today 93 | accuracy_decimals: ${accuracy} 94 | yield_total: 95 | name: "Yield total" 96 | id: yield_total 97 | filters: 98 | # Multiplication factor from W to kW is 0.001 99 | - multiply: 0.001 100 | unit_of_measurement: kWh 101 | accuracy_decimals: ${accuracy} 102 | yield_yesterday: 103 | name: "Yield yesterday" 104 | id: yield_yesterday 105 | filters: 106 | # Multiplication factor from W to kW is 0.001 107 | - multiply: 0.001 108 | unit_of_measurement: kWh 109 | accuracy_decimals: ${accuracy} 110 | yield_today: 111 | name: "Yield today" 112 | id: yt 113 | filters: 114 | # Multiplication factor from W to kW is 0.001 115 | - multiply: 0.001 116 | unit_of_measurement: kWh 117 | accuracy_decimals: ${accuracy} 118 | panel_power: 119 | name: "Panel power" 120 | id: panel_power 121 | day_number: 122 | name: "Day sequence number" 123 | id: day_number 124 | icon: "mdi:calendar-today" 125 | charging_mode_id: 126 | name: "Charger mode id" 127 | error_code: 128 | name: "Error code" 129 | tracking_mode_id: 130 | name: "Tracker mode id" 131 | 132 | - platform: wifi_signal 133 | name: "${devicename} WiFi Signal Sensor" 134 | id: rssi_sensor 135 | update_interval: 15s 136 | 137 | - platform: template 138 | name: "Wifi Quality" 139 | unit_of_measurement: "%" 140 | accuracy_decimals: 0 141 | icon: "mdi:wifi" 142 | update_interval: 15s 143 | lambda: |- 144 | int quality; 145 | const int rssi = id(rssi_sensor).state; 146 | if (rssi <= -100) { 147 | quality = 0; 148 | } else if (rssi >= -50) { 149 | quality = 100; 150 | } else { 151 | quality = 2 * (rssi + 100); 152 | } 153 | return quality; 154 | 155 | - platform: uptime 156 | name: "${devicename} Uptime" 157 | id: uptime_s 158 | update_interval: 5s 159 | 160 | - platform: template # for 1 cell of battery 161 | name: "Bat S1 " # 7S pack 162 | lambda: |- 163 | if ((((id(bv).state) / 7) > 4.22) && (((id(bv).state) / 7) < 3.2)) { 164 | return NAN; 165 | } 166 | return ((id(bv).state) / 7); 167 | 168 | update_interval: 10s 169 | icon: "mdi:battery" 170 | unit_of_measurement: V 171 | device_class: "voltage" 172 | accuracy_decimals: ${accuracy} 173 | - platform: template # Battery working percent 174 | name: "Bat working percent" # cycle batt between 3,7v - 4.17v 175 | lambda: |- 176 | if (((((((((id(bv).state) / 7 )) - 3.700) / (4.170 - 3.700)) * 100)) >= 115) && ((((((((id(bv).state) / 7 )) - 3.700) / (4.170 - 3.700)) * 100)) <= -25)) { 177 | return NAN; 178 | } 179 | return ((((((id(bv).state) / 7 )) - 3.700) / (4.170 - 3.700) ) * 100); 180 | 181 | update_interval: 15s 182 | icon: "mdi:battery" 183 | unit_of_measurement: "%" 184 | device_class: "voltage" 185 | accuracy_decimals: ${accuracy} 186 | 187 | - platform: template # FVE percent 188 | name: "FVE percent" # 500Wp max power of my FVE grid 189 | lambda: |- 190 | if (((((id(panel_power).state) / 500) * 100) >= 120) && ((((id(panel_power).state) / 500) * 100) < 0)) { 191 | return NAN; 192 | } 193 | return (((id(panel_power).state) / 500) * 100); 194 | icon: "mdi:gauge" 195 | unit_of_measurement: "%" 196 | device_class: "power" 197 | accuracy_decimals: ${accuracy} 198 | update_interval: 15s 199 | 200 | - platform: template # median generated day energy 201 | name: "Day median generated " # 500Wp max power of my FVE grid 202 | lambda: |- 203 | return (id(yield_total).state) / (id(day_number).state); 204 | update_interval: 15s 205 | icon: "mdi:gauge" 206 | unit_of_measurement: "kWh" 207 | device_class: "power" 208 | accuracy_decimals: 3 209 | 210 | text_sensor: 211 | - platform: victron 212 | victron_id: victron0 213 | charging_mode: 214 | name: "Charging Mode" 215 | error: 216 | name: "Error" 217 | tracking_mode: 218 | name: "Tracking Mode" 219 | firmware_version: 220 | name: "Firmware Version" 221 | device_type: 222 | name: "Device Type" 223 | 224 | - platform: template 225 | name: "${devicename} Config Version" 226 | icon: mdi:information-outline 227 | lambda: |- 228 | return {"${config_version}"}; 229 | - platform: version 230 | name: "${devicename} Esphome Version" 231 | icon: mdi:information-outline 232 | - platform: wifi_info 233 | ip_address: 234 | name: "${devicename} IP Address" 235 | icon: mdi:ip 236 | ssid: 237 | name: "${devicename} Connected SSID" 238 | icon: mdi:wifi 239 | bssid: 240 | name: "${devicename} Connected BSSID" 241 | mac_address: 242 | name: "${devicename} Mac Wifi Address" 243 | - platform: template 244 | name: "${devicename} Uptime (formatted)" 245 | lambda: |- 246 | uint32_t dur = id(uptime_s).state; 247 | int dys = 0; 248 | int hrs = 0; 249 | int mnts = 0; 250 | if (dur > 86399) { 251 | dys = trunc(dur / 86400); 252 | dur = dur - (dys * 86400); 253 | } 254 | if (dur > 3599) { 255 | hrs = trunc(dur / 3600); 256 | dur = dur - (hrs * 3600); 257 | } 258 | if (dur > 59) { 259 | mnts = trunc(dur / 60); 260 | dur = dur - (mnts * 60); 261 | } 262 | char buffer[17]; 263 | sprintf(buffer, "%ud %02uh %02um %02us", dys, hrs, mnts, dur); 264 | return {buffer}; 265 | icon: mdi:clock-start 266 | update_interval: 5s 267 | 268 | switch: 269 | - platform: restart 270 | icon: mdi:reload-alert 271 | name: "${devicename} Restart" 272 | -------------------------------------------------------------------------------- /smartsolar-mppt-esp8266-example-multiple-uarts.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: victron 3 | device0: victron0 4 | device1: victron1 5 | external_components_source: github://KinDR007/VictronMPPT-ESPHOME@main 6 | 7 | esphome: 8 | name: ${name} 9 | min_version: 2024.6.0 10 | 11 | esp8266: 12 | board: d1_mini 13 | 14 | external_components: 15 | - source: ${external_components_source} 16 | refresh: 0s 17 | 18 | wifi: 19 | ssid: !secret wifi_ssid 20 | password: !secret wifi_password 21 | 22 | ota: 23 | platform: esphome 24 | 25 | logger: 26 | baud_rate: 0 27 | esp8266_store_log_strings_in_flash: false 28 | 29 | # If you use Home Assistant please remove this `mqtt` section and uncomment the `api` component! 30 | # The native API has many advantages over MQTT: https://esphome.io/components/api.html#advantages-over-mqtt 31 | mqtt: 32 | broker: !secret mqtt_host 33 | username: !secret mqtt_username 34 | password: !secret mqtt_password 35 | id: mqtt_client 36 | 37 | # api: 38 | 39 | uart: 40 | - id: uart_0 41 | tx_pin: D8 # Not connected! The communication is read-only 42 | rx_pin: D7 # Connect this this GPIO and GND to the first MPPT charger 43 | baud_rate: 19200 44 | rx_buffer_size: 256 45 | - id: uart_1 46 | tx_pin: D6 # Not connected! The communication is read-only 47 | rx_pin: D5 # Connect this this GPIO and GND to the second MPPT charger 48 | baud_rate: 19200 49 | rx_buffer_size: 256 50 | 51 | victron: 52 | - id: victron0 53 | uart_id: uart_0 54 | throttle: 10s 55 | - id: victron1 56 | uart_id: uart_1 57 | throttle: 10s 58 | 59 | sensor: 60 | - platform: victron 61 | victron_id: victron0 62 | max_power_yesterday: 63 | name: "${device0} max power yesterday" 64 | panel_voltage: 65 | name: "${device0} panel voltage" 66 | panel_power: 67 | name: "${device0} panel power" 68 | battery_current: 69 | name: "${device0} panel current" 70 | load_current: 71 | name: "${device0} load current" 72 | 73 | - platform: victron 74 | victron_id: victron1 75 | max_power_yesterday: 76 | name: "${device1} max power yesterday" 77 | panel_voltage: 78 | name: "${device1} panel voltage" 79 | panel_power: 80 | name: "${device1} panel power" 81 | battery_current: 82 | name: "${device1} panel current" 83 | load_current: 84 | name: "${device1} load current" 85 | 86 | text_sensor: 87 | - platform: victron 88 | victron_id: victron0 89 | charging_mode: 90 | name: "${device0} charging mode" 91 | error: 92 | name: "${device0} error" 93 | tracking_mode: 94 | name: "${device0} tracking mode" 95 | 96 | - platform: victron 97 | victron_id: victron1 98 | charging_mode: 99 | name: "${device1} charging mode" 100 | error: 101 | name: "${device1} error" 102 | tracking_mode: 103 | name: "${device1} tracking mode" 104 | -------------------------------------------------------------------------------- /smartsolar-mppt-esp8266-example.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: victron-mppt 3 | external_components_source: github://KinDR007/VictronMPPT-ESPHOME@main 4 | 5 | esphome: 6 | name: ${name} 7 | min_version: 2024.6.0 8 | 9 | esp8266: 10 | board: d1_mini 11 | 12 | external_components: 13 | - source: ${external_components_source} 14 | refresh: 0s 15 | 16 | wifi: 17 | ssid: !secret wifi_ssid 18 | password: !secret wifi_password 19 | 20 | ota: 21 | platform: esphome 22 | 23 | logger: 24 | baud_rate: 0 25 | esp8266_store_log_strings_in_flash: false 26 | 27 | # If you use Home Assistant please remove this `mqtt` section and uncomment the `api` component! 28 | # The native API has many advantages over MQTT: https://esphome.io/components/api.html#advantages-over-mqtt 29 | mqtt: 30 | broker: !secret mqtt_host 31 | username: !secret mqtt_username 32 | password: !secret mqtt_password 33 | id: mqtt_client 34 | 35 | # api: 36 | 37 | uart: 38 | id: uart_0 39 | tx_pin: D8 # Not connected! The communication is read-only 40 | rx_pin: D7 # Connect this this GPIO and GND to the MPPT charger 41 | baud_rate: 19200 42 | rx_buffer_size: 256 43 | 44 | victron: 45 | uart_id: uart_0 46 | id: victron0 47 | throttle: 10s 48 | 49 | sensor: 50 | - platform: victron 51 | victron_id: victron0 52 | max_power_yesterday: 53 | name: "${name} max power yesterday" 54 | max_power_today: 55 | name: "${name} max power today" 56 | yield_total: 57 | name: "${name} yield total" 58 | yield_yesterday: 59 | name: "${name} yield yesterday" 60 | yield_today: 61 | name: "${name} yield today" 62 | panel_voltage: 63 | name: "${name} panel voltage" 64 | panel_power: 65 | name: "${name} panel power" 66 | battery_current: 67 | name: "${name} panel current" 68 | battery_voltage: 69 | name: "${name} battery voltage" 70 | day_number: 71 | name: "${name} day number" 72 | charging_mode_id: 73 | name: "${name} charging mode id" 74 | error_code: 75 | name: "${name} error code" 76 | tracking_mode_id: 77 | name: "${name} tracking mode id" 78 | load_current: 79 | name: "${name} load current" 80 | 81 | text_sensor: 82 | - platform: victron 83 | victron_id: victron0 84 | charging_mode: 85 | name: "${name} charging mode" 86 | error: 87 | name: "${name} error" 88 | tracking_mode: 89 | name: "${name} tracking mode" 90 | firmware_version: 91 | name: "${name} firmware version" 92 | device_type: 93 | name: "${name} device type" 94 | serial_number: 95 | name: "${name} serial number" 96 | 97 | binary_sensor: 98 | - platform: victron 99 | victron_id: victron0 100 | load_state: 101 | name: "${name} load state" 102 | relay_state: 103 | name: "${name} relay state" 104 | -------------------------------------------------------------------------------- /test-esp32.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | esphome -s external_components_source components ${1:-run} debug-esp32-example.yaml 4 | -------------------------------------------------------------------------------- /test-esp8266.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | esphome -s external_components_source components ${1:-run} ${2:-smartsolar-mppt-esp8266-example.yaml} 4 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | # Gitignore settings for ESPHome 2 | # This is an example and may include too much for your use-case. 3 | # You can modify this file to suit your needs. 4 | /.esphome/ 5 | /secrets.yaml 6 | -------------------------------------------------------------------------------- /tests/emulator-all-keys.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: emulator-all-keys 3 | tx_pin: GPIO4 4 | rx_pin: GPIO5 5 | 6 | esphome: 7 | name: ${name} 8 | 9 | esp8266: 10 | board: d1_mini 11 | 12 | wifi: 13 | ssid: !secret wifi_ssid 14 | password: !secret wifi_password 15 | 16 | ota: 17 | platform: esphome 18 | 19 | logger: 20 | level: DEBUG 21 | 22 | api: 23 | reboot_timeout: 0s 24 | 25 | uart: 26 | baud_rate: 19200 27 | tx_pin: ${tx_pin} 28 | rx_pin: ${rx_pin} 29 | debug: 30 | direction: BOTH 31 | dummy_receiver: true 32 | after: 33 | delimiter: "\n" 34 | sequence: 35 | - lambda: UARTDebug::log_string(direction, bytes); 36 | 37 | interval: 38 | - interval: 3s 39 | then: 40 | - uart.write: "AC_OUT_I\t50\r\n" 41 | - uart.write: "AC_OUT_V\t23000\r\n" 42 | - uart.write: "Alarm\tOFF\r\n" 43 | - uart.write: "AR\t1000001\r\n" 44 | - uart.write: "BMV\t702\r\n" 45 | - uart.write: "CE\t13500\r\n" 46 | - uart.write: "CS\t5\r\n" 47 | - uart.write: "DM\t120\r\n" 48 | - uart.write: "ERR\t0\r\n" 49 | - uart.write: "FW\t1.19\r\n" 50 | - uart.write: "H10\t45\r\n" 51 | - uart.write: "H11\t5\r\n" 52 | - uart.write: "H12\t0\r\n" 53 | - uart.write: "H13\t0\r\n" 54 | - uart.write: "H14\t0\r\n" 55 | - uart.write: "H15\t11500\r\n" 56 | - uart.write: "H1\t55000\r\n" 57 | - uart.write: "H16\t14800\r\n" 58 | - uart.write: "H17\t34\r\n" 59 | - uart.write: "H18\t45\r\n" 60 | - uart.write: "H19\t456\r\n" 61 | - uart.write: "H20\t45\r\n" 62 | - uart.write: "H21\t300\r\n" 63 | - uart.write: "H2\t15000\r\n" 64 | - uart.write: "H22\t45\r\n" 65 | - uart.write: "H23\t350\r\n" 66 | - uart.write: "H3\t13000\r\n" 67 | - uart.write: "H4\t230\r\n" 68 | - uart.write: "H5\t12\r\n" 69 | - uart.write: "H6\t234000\r\n" 70 | - uart.write: "H7\t11000\r\n" 71 | - uart.write: "H8\t14800\r\n" 72 | - uart.write: "H9\t7200\r\n" 73 | - uart.write: "HSDS\t0\r\n" 74 | - uart.write: "I\t15000\r\n" 75 | - uart.write: "IL\t1500\r\n" 76 | - uart.write: "LOAD\tON\r\n" 77 | - uart.write: "MODE\t2\r\n" 78 | - uart.write: "MPPT\t2\r\n" 79 | - uart.write: "OR\t0x00000003\r\n" 80 | - uart.write: "P\t130\r\n" 81 | - uart.write: "PID\t0x204\r\n" 82 | - uart.write: "PPV\t130\r\n" 83 | - uart.write: "Relay\tOFF\r\n" 84 | - uart.write: "SER#\tHQ141112345\r\n" 85 | - uart.write: "SOC\t876\r\n" 86 | - uart.write: "T\t25\r\n" 87 | - uart.write: "TTG\t45\r\n" 88 | - uart.write: "V\t12800\r\n" 89 | - uart.write: "V2\t12802\r\n" 90 | - uart.write: "V3\t12803\r\n" 91 | - uart.write: "VM\t1280\r\n" 92 | - uart.write: "VPV\t3350\r\n" 93 | - uart.write: "VS\t12200\r\n" 94 | - uart.write: "WARN\t1\r\n" 95 | - uart.write: "FWE\t109FF\r\n" 96 | - uart.write: "MON\t-1\r\n" 97 | - uart.write: "Checksum\t⸮:A0922001C0004\r\n" 98 | - uart.write: ":A052200EB00000039\r\n" 99 | - uart.write: ":A8DED00221699\r\n" 100 | - uart.write: ":A0422009500000090\r\n" 101 | - uart.write: ":A8DED0021169A\r\n" 102 | - uart.write: ":ABBED003C4A1D\r\n" 103 | - uart.write: ":ACBEC003C4A0E\r\n" 104 | - uart.write: "\r\n" 105 | -------------------------------------------------------------------------------- /tests/fake-bluesmart-ip22-0xa332.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: fake-bluesmart-ip22 3 | tx_pin: GPIO4 4 | rx_pin: GPIO5 5 | 6 | esphome: 7 | name: ${name} 8 | 9 | esp8266: 10 | board: d1_mini 11 | 12 | wifi: 13 | ssid: !secret wifi_ssid 14 | password: !secret wifi_password 15 | 16 | ota: 17 | platform: esphome 18 | 19 | logger: 20 | level: DEBUG 21 | 22 | api: 23 | reboot_timeout: 0s 24 | 25 | uart: 26 | baud_rate: 19200 27 | tx_pin: ${tx_pin} 28 | rx_pin: ${rx_pin} 29 | debug: 30 | direction: BOTH 31 | dummy_receiver: true 32 | after: 33 | delimiter: "\n" 34 | sequence: 35 | - lambda: UARTDebug::log_string(direction, bytes); 36 | 37 | interval: 38 | - interval: 3s 39 | then: 40 | - uart.write: "PID\t0xA332\r\n" 41 | - uart.write: "FWE\t0342FF\r\n" 42 | - uart.write: "SER#\tHQ2249TNFVM\r\n" 43 | - uart.write: "V\t26870\r\n" 44 | - uart.write: "I\t2000\r\n" 45 | - uart.write: "T\t---\r\n" 46 | - uart.write: "ERR\t0\r\n" 47 | - uart.write: "CS\t3\r\n" 48 | - uart.write: "HC#\t57825\r\n" 49 | - uart.write: "Checksum\t\x18:A0920000022\n" 50 | - uart.write: ":A0E200021FC\n" 51 | - uart.write: ":A0920000022\n" 52 | - uart.write: ":A0E200021FC\n" 53 | - uart.write: ":A132000326900007D\n" 54 | - uart.write: ":A0220007F0AA0\n" 55 | - uart.write: ":A0920000022\n" 56 | - uart.write: ":A0E200021FC\n" 57 | - uart.write: ":A0A20001E6600009D\n" 58 | - uart.write: ":A0920000022\n" 59 | - uart.write: ":A0E200021FC\n" 60 | - uart.write: ":A0920000022\n" 61 | - uart.write: ":A0E200021FC\n" 62 | - uart.write: ":A0920000022\n" 63 | - uart.write: ":A0E200021FC\n" 64 | - uart.write: ":A0220007F0AA0\n" 65 | - uart.write: ":A8DED00800A47\n" 66 | - uart.write: ":AD5ED00800AFF\n" 67 | - uart.write: ":A16EE007F0ABE\n" 68 | - uart.write: ":A20010077000000B3\n" 69 | - uart.write: ":A30EC00052A\n" 70 | - uart.write: ":A31EC000122D5569A32A300FF4203007FFFFFFFFFC5BEBD9A60A00063010000B9FFFFFFFFBF\n" 71 | - uart.write: ":A32EC000122E2979989A30016040000BCFFFFFFFFB332EB9A60A00063010000BDFFFFFFFF73\n" 72 | - uart.write: ":A33EC000129E3A49834A300FF420300CCFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFF90\n" 73 | - uart.write: ":A20EC00ECEDFFFFFFFFFFFF8DED000C22E297998CED000C22E297993EEC000FB332EB9A56\n" 74 | - uart.write: ":A0A20001E6600009D\n" 75 | - uart.write: ":A132000AA69000005\n" 76 | - uart.write: ":A0920000022\n" 77 | - uart.write: ":A0E200021FC\n" 78 | - uart.write: ":A0920000022\n" 79 | - uart.write: ":A0E200021FC\n" 80 | - uart.write: ":A0920000022\n" 81 | - uart.write: ":A0E200021FC\n" 82 | - uart.write: ":A0220007F0AA0\n" 83 | - uart.write: ":A0920000022\n" 84 | - uart.write: ":A0E200021FC\n" 85 | - uart.write: "\r\n" 86 | -------------------------------------------------------------------------------- /tests/fake-bluesmart-ip65-0xa30a.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: fake-bluesmart-ip22 3 | tx_pin: GPIO4 4 | rx_pin: GPIO5 5 | 6 | esphome: 7 | name: ${name} 8 | 9 | esp8266: 10 | board: d1_mini 11 | 12 | wifi: 13 | ssid: !secret wifi_ssid 14 | password: !secret wifi_password 15 | 16 | ota: 17 | platform: esphome 18 | 19 | logger: 20 | level: DEBUG 21 | 22 | api: 23 | reboot_timeout: 0s 24 | 25 | uart: 26 | baud_rate: 19200 27 | tx_pin: ${tx_pin} 28 | rx_pin: ${rx_pin} 29 | debug: 30 | direction: BOTH 31 | dummy_receiver: true 32 | after: 33 | delimiter: "\n" 34 | sequence: 35 | - lambda: UARTDebug::log_string(direction, bytes); 36 | 37 | interval: 38 | - interval: 5s 39 | then: 40 | - uart.write: "PID\t0xA30A\r\n" 41 | - uart.write: "FWE\t0325FF\r\n" 42 | - uart.write: "SER#\tHQ2232GGMEH\r\n" 43 | - uart.write: "V\t14200\r\n" 44 | - uart.write: "I\t1000\r\n" 45 | - uart.write: "ERR\t0\r\n" 46 | - uart.write: "CS\t4\r\n" 47 | - uart.write: "HC#\t136663\r\n" 48 | - uart.write: "Checksum\t\x13" 49 | - uart.write: ":A8FED000D00C2\n" 50 | - uart.write: ":A8CED001E050000AF\n" 51 | - uart.write: ":AD7ED000D007A\n" 52 | - uart.write: ":A200100560C0000C8\n" 53 | - uart.write: "\r\n" 54 | - delay: 1s 55 | 56 | - uart.write: "PID\t0xA30A\r\n" 57 | - uart.write: "FWE\t0325FF\r\n" 58 | - uart.write: "SER#\tHQ2232GGMEH\r\n" 59 | - uart.write: "V\t14190\r\n" 60 | - uart.write: "I\t1000\r\n" 61 | - uart.write: "ERR\t0\r\n" 62 | - uart.write: "CS\t4\r\n" 63 | - uart.write: "HC#\t136663\r\n" 64 | - uart.write: "Checksum\t\v:A8DED008C0540\n" 65 | - uart.write: ":A8FED000800C7\n" 66 | - uart.write: ":A8CED00EE020000E2\n" 67 | - uart.write: ":AD5ED008C05F8\n" 68 | - uart.write: ":AD7ED0008007F\n" 69 | - uart.write: ":A2001005F0C0000BF\n" 70 | - uart.write: "\r\n" 71 | -------------------------------------------------------------------------------- /tests/fake-bmv600.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: fake-bmv600 3 | tx_pin: GPIO4 4 | rx_pin: GPIO5 5 | 6 | esphome: 7 | name: ${name} 8 | 9 | esp8266: 10 | board: d1_mini 11 | 12 | wifi: 13 | ssid: !secret wifi_ssid 14 | password: !secret wifi_password 15 | 16 | ota: 17 | platform: esphome 18 | 19 | logger: 20 | level: DEBUG 21 | 22 | api: 23 | reboot_timeout: 0s 24 | 25 | uart: 26 | baud_rate: 19200 27 | tx_pin: ${tx_pin} 28 | rx_pin: ${rx_pin} 29 | debug: 30 | direction: BOTH 31 | dummy_receiver: true 32 | after: 33 | delimiter: "\n" 34 | sequence: 35 | - lambda: UARTDebug::log_string(direction, bytes); 36 | 37 | interval: 38 | - interval: 3s 39 | then: 40 | - uart.write: "Alarm\tOFF\r\n" 41 | - uart.write: "AR\t1\r\n" 42 | - uart.write: "BMV\t702\r\n" 43 | - uart.write: "CE\t13500\r\n" 44 | - uart.write: "FW\t1.19\r\n" 45 | - uart.write: "H10\t45\r\n" 46 | - uart.write: "H11\t5\r\n" 47 | - uart.write: "H12\t0\r\n" 48 | - uart.write: "H13\t0\r\n" 49 | - uart.write: "H14\t0\r\n" 50 | - uart.write: "H15\t11500\r\n" 51 | - uart.write: "H16\t14800\r\n" 52 | - uart.write: "H1\t55000\r\n" 53 | - uart.write: "H2\t15000\r\n" 54 | - uart.write: "H3\t13000\r\n" 55 | - uart.write: "H4\t230\r\n" 56 | - uart.write: "H5\t12\r\n" 57 | - uart.write: "H6\t234000\r\n" 58 | - uart.write: "H7\t11000\r\n" 59 | - uart.write: "H8\t14800\r\n" 60 | - uart.write: "H9\t7200\r\n" 61 | - uart.write: "I\t15000\r\n" 62 | - uart.write: "Relay\tOFF\r\n" 63 | - uart.write: "SOC\t876\r\n" 64 | - uart.write: "TTG\t45\r\n" 65 | - uart.write: "VS\t12800\r\n" 66 | - uart.write: "V\t12800\r\n" 67 | - uart.write: "Checksum\t?\r\n" 68 | - uart.write: "\r\n" 69 | -------------------------------------------------------------------------------- /tests/fake-bmv700.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: fake-bmv700 3 | tx_pin: GPIO4 4 | rx_pin: GPIO5 5 | 6 | esphome: 7 | name: ${name} 8 | 9 | esp8266: 10 | board: d1_mini 11 | 12 | wifi: 13 | ssid: !secret wifi_ssid 14 | password: !secret wifi_password 15 | 16 | ota: 17 | platform: esphome 18 | 19 | logger: 20 | level: DEBUG 21 | 22 | api: 23 | reboot_timeout: 0s 24 | 25 | uart: 26 | baud_rate: 19200 27 | tx_pin: ${tx_pin} 28 | rx_pin: ${rx_pin} 29 | debug: 30 | direction: BOTH 31 | dummy_receiver: true 32 | after: 33 | delimiter: "\n" 34 | sequence: 35 | - lambda: UARTDebug::log_string(direction, bytes); 36 | 37 | interval: 38 | - interval: 3s 39 | then: 40 | - uart.write: "V\t12800\r\n" 41 | - uart.write: "VS\t12800\r\n" 42 | - uart.write: "VM\t1280\r\n" 43 | - uart.write: "DM\t120\r\n" 44 | - uart.write: "I\t15000\r\n" 45 | - uart.write: "T\t25\r\n" 46 | - uart.write: "P\t130\r\n" 47 | - uart.write: "CE\t13500\r\n" 48 | - uart.write: "SOC\t876\r\n" 49 | - uart.write: "TTG\t45\r\n" 50 | - uart.write: "Alarm\tOFF\r\n" 51 | - uart.write: "Relay\tOFF\r\n" 52 | - uart.write: "AR\t1\r\n" 53 | - uart.write: "H1\t55000\r\n" 54 | - uart.write: "H2\t15000\r\n" 55 | - uart.write: "H3\t13000\r\n" 56 | - uart.write: "H4\t230\r\n" 57 | - uart.write: "H5\t12\r\n" 58 | - uart.write: "H6\t234000\r\n" 59 | - uart.write: "H7\t11000\r\n" 60 | - uart.write: "H8\t14800\r\n" 61 | - uart.write: "H9\t7200\r\n" 62 | - uart.write: "H10\t45\r\n" 63 | - uart.write: "H11\t5\r\n" 64 | - uart.write: "H12\t0\r\n" 65 | - uart.write: "H15\t11500\r\n" 66 | - uart.write: "H16\t14800\r\n" 67 | - uart.write: "H17\t34\r\n" 68 | - uart.write: "H18\t45\r\n" 69 | - uart.write: "BMV\t702\r\n" 70 | - uart.write: "FW\t1.19\r\n" 71 | - uart.write: "PID\t0x204\r\n" 72 | - uart.write: "Checksum\t?\r\n" 73 | - uart.write: "\r\n" 74 | -------------------------------------------------------------------------------- /tests/fake-bmv710.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: fake-bmv710 3 | tx_pin: GPIO4 4 | rx_pin: GPIO5 5 | 6 | esphome: 7 | name: ${name} 8 | 9 | esp8266: 10 | board: d1_mini 11 | 12 | wifi: 13 | ssid: !secret wifi_ssid 14 | password: !secret wifi_password 15 | 16 | ota: 17 | platform: esphome 18 | 19 | logger: 20 | level: DEBUG 21 | 22 | api: 23 | reboot_timeout: 0s 24 | 25 | uart: 26 | baud_rate: 19200 27 | tx_pin: ${tx_pin} 28 | rx_pin: ${rx_pin} 29 | debug: 30 | direction: BOTH 31 | dummy_receiver: true 32 | after: 33 | delimiter: "\n" 34 | sequence: 35 | - lambda: UARTDebug::log_string(direction, bytes); 36 | 37 | interval: 38 | - interval: 3s 39 | then: 40 | - uart.write: "V\t12800\r\n" 41 | - uart.write: "VS\t12800\r\n" 42 | - uart.write: "VM\t1280\r\n" 43 | - uart.write: "DM\t120\r\n" 44 | - uart.write: "I\t15000\r\n" 45 | - uart.write: "T\t25\r\n" 46 | - uart.write: "P\t130\r\n" 47 | - uart.write: "CE\t13500\r\n" 48 | - uart.write: "SOC\t876\r\n" 49 | - uart.write: "TTG\t45\r\n" 50 | - uart.write: "Alarm\tOFF\r\n" 51 | - uart.write: "Relay\tOFF\r\n" 52 | - uart.write: "AR\t1\r\n" 53 | - uart.write: "H1\t55000\r\n" 54 | - uart.write: "H2\t15000\r\n" 55 | - uart.write: "H3\t13000\r\n" 56 | - uart.write: "H4\t230\r\n" 57 | - uart.write: "H5\t12\r\n" 58 | - uart.write: "H6\t234000\r\n" 59 | - uart.write: "H7\t11000\r\n" 60 | - uart.write: "H8\t14800\r\n" 61 | - uart.write: "H9\t7200\r\n" 62 | - uart.write: "H10\t45\r\n" 63 | - uart.write: "H11\t5\r\n" 64 | - uart.write: "H12\t0\r\n" 65 | - uart.write: "H15\t11500\r\n" 66 | - uart.write: "H16\t14800\r\n" 67 | - uart.write: "H17\t34\r\n" 68 | - uart.write: "H18\t45\r\n" 69 | - uart.write: "BMV\t702\r\n" 70 | - uart.write: "FW\t1.19\r\n" 71 | - uart.write: "PID\t0x204\r\n" 72 | - uart.write: "MON\t-1\r\n" 73 | - uart.write: "Checksum\t?\r\n" 74 | - uart.write: "\r\n" 75 | -------------------------------------------------------------------------------- /tests/fake-bmv712.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: fake-bmv712 3 | tx_pin: GPIO4 4 | rx_pin: GPIO5 5 | 6 | esphome: 7 | name: ${name} 8 | 9 | esp8266: 10 | board: d1_mini 11 | 12 | wifi: 13 | ssid: !secret wifi_ssid 14 | password: !secret wifi_password 15 | 16 | ota: 17 | platform: esphome 18 | 19 | logger: 20 | level: DEBUG 21 | 22 | api: 23 | reboot_timeout: 0s 24 | 25 | uart: 26 | baud_rate: 19200 27 | tx_pin: ${tx_pin} 28 | rx_pin: ${rx_pin} 29 | debug: 30 | direction: BOTH 31 | dummy_receiver: true 32 | after: 33 | delimiter: "\n" 34 | sequence: 35 | - lambda: UARTDebug::log_string(direction, bytes); 36 | 37 | interval: 38 | - interval: 3s 39 | then: 40 | - uart.write: "PID\t0xA381\r\n" 41 | - uart.write: "V\t26464\r\n" 42 | - uart.write: "VS\t34\r\n" 43 | - uart.write: "I\t-5647\r\n" 44 | - uart.write: "P\t-149\r\n" 45 | - uart.write: "CE\t-6671\r\n" 46 | - uart.write: "SOC\t937\r\n" 47 | - uart.write: "TTG\t436\r\n" 48 | - uart.write: "Alarm\tOFF\r\n" 49 | - uart.write: "Relay\tOFF\r\n" 50 | - uart.write: "AR\t0\r\n" 51 | - uart.write: "BMV\t712 Smart\r\n" 52 | - uart.write: "FW\t0408\r\n" 53 | - uart.write: "MON\t0\r\n" 54 | - uart.write: "\r\n" 55 | - uart.write: "H1\t-96470\r\n" 56 | - uart.write: "H2\t-6671\r\n" 57 | - uart.write: "H3\t-26485\r\n" 58 | - uart.write: "H4\t29\r\n" 59 | - uart.write: "H5\t4\r\n" 60 | - uart.write: "H6\t-2316635\r\n" 61 | - uart.write: "H7\t27\r\n" 62 | - uart.write: "H8\t28776\r\n" 63 | - uart.write: "H9\t7134\r\n" 64 | - uart.write: "H10\t31\r\n" 65 | - uart.write: "H11\t0\r\n" 66 | - uart.write: "H12\t0\r\n" 67 | - uart.write: "H15\t22\r\n" 68 | - uart.write: "H16\t38\r\n" 69 | - uart.write: "H17\t6066\r\n" 70 | - uart.write: "H18\t6117\r\n" 71 | - uart.write: "Checksum\tn" 72 | - uart.write: "\r\n" 73 | 74 | - uart.write: "PID\t0xA381\r\n" 75 | - uart.write: "V\t26457\r\n" 76 | - uart.write: "VS\t34\r\n" 77 | - uart.write: "I\t-6032\r\n" 78 | - uart.write: "P\t-160\r\n" 79 | - uart.write: "CE\t-6673\r\n" 80 | - uart.write: "SOC\t937\r\n" 81 | - uart.write: "TTG\t435\r\n" 82 | - uart.write: "Alarm\tOFF\r\n" 83 | - uart.write: "Relay\tOFF\r\n" 84 | - uart.write: "AR\t0\r\n" 85 | - uart.write: "BMV\t712 Smart\r\n" 86 | - uart.write: "FW\t0408\r\n" 87 | - uart.write: "MON\t0\r\n" 88 | - uart.write: "Checksum\t\x9E" 89 | - uart.write: "\r\n" 90 | - uart.write: "H1\t-96470\r\n" 91 | - uart.write: "H2\t-6673\r\n" 92 | - uart.write: "H3\t-26485\r\n" 93 | - uart.write: "H4\t29\r\n" 94 | - uart.write: "H5\t4\r\n" 95 | - uart.write: "H6\t-2316636\r\n" 96 | - uart.write: "H7\t27\r\n" 97 | - uart.write: "H8\t28776\r\n" 98 | - uart.write: "H9\t7135\r\n" 99 | - uart.write: "H10\t31\r\n" 100 | - uart.write: "H11\t0\r\n" 101 | - uart.write: "H12\t0\r\n" 102 | - uart.write: "H15\t22\r\n" 103 | - uart.write: "H16\t38\r\n" 104 | - uart.write: "H17\t6066\r\n" 105 | - uart.write: "H18\t6117\r\n" 106 | - uart.write: "Checksum\tj" 107 | - uart.write: "\r\n" 108 | -------------------------------------------------------------------------------- /tests/fake-multi-rs-solar.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: fake-multi-rs-solar 3 | tx_pin: GPIO4 4 | rx_pin: GPIO5 5 | 6 | esphome: 7 | name: ${name} 8 | 9 | esp8266: 10 | board: d1_mini 11 | 12 | wifi: 13 | ssid: !secret wifi_ssid 14 | password: !secret wifi_password 15 | 16 | ota: 17 | platform: esphome 18 | 19 | logger: 20 | level: DEBUG 21 | 22 | api: 23 | reboot_timeout: 0s 24 | 25 | uart: 26 | baud_rate: 19200 27 | tx_pin: ${tx_pin} 28 | rx_pin: ${rx_pin} 29 | debug: 30 | direction: BOTH 31 | dummy_receiver: true 32 | after: 33 | delimiter: "\n" 34 | sequence: 35 | - lambda: UARTDebug::log_string(direction, bytes); 36 | 37 | interval: 38 | - interval: 3s 39 | then: 40 | - uart.write: "PID\t0xA442\r\n" 41 | - uart.write: "SER#\tHQ2206WVU9D\r\n" 42 | - uart.write: "FWE\t109FF\r\n" 43 | - uart.write: "CS\t6\r\n" 44 | - uart.write: "ERR\t0\r\n" 45 | - uart.write: "AC_OUT_I\t10\r\n" 46 | - uart.write: "AC_OUT_V\t23001\r\n" 47 | - uart.write: "H19\t2551\r\n" 48 | - uart.write: "H20\t217\r\n" 49 | - uart.write: "H21\t1289\r\n" 50 | - uart.write: "H22\t264\r\n" 51 | - uart.write: "H23\t1404\r\n" 52 | - uart.write: "HSDS\t5\r\n" 53 | - uart.write: "I\t-3200\r\n" 54 | - uart.write: "PPV\t0\r\n" 55 | - uart.write: "V\t56650\r\n" 56 | - uart.write: "VPV\t190090\r\n" 57 | - uart.write: "Checksum\t⸮:A0922001C0004\r\n" 58 | - uart.write: ":A052200EB00000039\r\n" 59 | - uart.write: ":A8DED00221699\r\n" 60 | - uart.write: ":A0422009500000090\r\n" 61 | - uart.write: ":A8DED0021169A\r\n" 62 | - uart.write: ":ABBED003C4A1D\r\n" 63 | - uart.write: ":ACBEC003C4A0E\r\n" 64 | - uart.write: "\r\n" 65 | -------------------------------------------------------------------------------- /tests/fake-phoenix-inverter.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: fake-phoenix-inverter 3 | tx_pin: GPIO4 4 | rx_pin: GPIO5 5 | 6 | esphome: 7 | name: ${name} 8 | 9 | esp8266: 10 | board: d1_mini 11 | 12 | wifi: 13 | ssid: !secret wifi_ssid 14 | password: !secret wifi_password 15 | 16 | ota: 17 | platform: esphome 18 | 19 | logger: 20 | level: DEBUG 21 | 22 | api: 23 | reboot_timeout: 0s 24 | 25 | uart: 26 | baud_rate: 19200 27 | tx_pin: ${tx_pin} 28 | rx_pin: ${rx_pin} 29 | debug: 30 | direction: BOTH 31 | dummy_receiver: true 32 | after: 33 | delimiter: "\n" 34 | sequence: 35 | - lambda: UARTDebug::log_string(direction, bytes); 36 | 37 | interval: 38 | - interval: 3s 39 | then: 40 | - uart.write: "AC_OUT_I\t50\r\n" 41 | - uart.write: "AC_OUT_V\t23000\r\n" 42 | - uart.write: "AR\t1\r\n" 43 | - uart.write: "CS\t5\r\n" 44 | - uart.write: "FW\t1.19\r\n" 45 | - uart.write: "MODE\t2\r\n" 46 | - uart.write: "PID\t0xA201\r\n" 47 | - uart.write: "SER#\tHQ141112345\r\n" 48 | - uart.write: "WARN\t1\r\n" 49 | - uart.write: "Checksum\t?\r\n" 50 | - uart.write: "\r\n" 51 | -------------------------------------------------------------------------------- /tests/fake-smartsolar-mppt-150-70-rev3.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: fake-smartsolar-mppt 3 | tx_pin: GPIO4 4 | rx_pin: GPIO5 5 | 6 | esphome: 7 | name: ${name} 8 | 9 | esp8266: 10 | board: d1_mini 11 | 12 | wifi: 13 | ssid: !secret wifi_ssid 14 | password: !secret wifi_password 15 | 16 | ota: 17 | platform: esphome 18 | 19 | logger: 20 | level: DEBUG 21 | 22 | api: 23 | reboot_timeout: 0s 24 | 25 | uart: 26 | baud_rate: 19200 27 | tx_pin: ${tx_pin} 28 | rx_pin: ${rx_pin} 29 | debug: 30 | direction: BOTH 31 | dummy_receiver: true 32 | after: 33 | delimiter: "\n" 34 | sequence: 35 | - lambda: UARTDebug::log_string(direction, bytes); 36 | 37 | interval: 38 | - interval: 10s 39 | then: 40 | - uart.write: "PID\t0xA06C\r\n" 41 | - uart.write: "FWE\t316FF\r\n" 42 | - uart.write: "SER#\tHQ2247EP3FN\r\n" 43 | - uart.write: "V\t52700\r\n" 44 | - uart.write: "I\t7600\r\n" 45 | - uart.write: "VPV\t103870\r\n" 46 | - uart.write: "PPV\t412\r\n" 47 | - uart.write: "MPPT\t2\r\n" 48 | - uart.write: "CS\t3\r\n" 49 | - uart.write: "OR\t0x00000000\r\n" 50 | - uart.write: "Checksum\t\xFA" 51 | - uart.write: ":A2001002470030093\n" 52 | - uart.write: ":ADBED00BF0ABA\n" 53 | - uart.write: ":ABCED0045A10000BC\n" 54 | - uart.write: ":ABBED008D28EE\n" 55 | - uart.write: ":AB4ED000DA10000FC\n" 56 | - uart.write: ":AB5ED00802801\n" 57 | - uart.write: "\r\n" 58 | - uart.write: "ERR\t0\r\n" 59 | - uart.write: "LOAD\tON\r\n" 60 | - uart.write: "Relay\tOFF\r\n" 61 | - uart.write: "H19\t1158\r\n" 62 | - uart.write: "H20\t77\r\n" 63 | - uart.write: "H21\t479\r\n" 64 | - uart.write: "H22\t135\r\n" 65 | - uart.write: "H23\t525\r\n" 66 | - uart.write: "HSDS\t2\r\n" 67 | - uart.write: "Checksum\t\x05" 68 | - uart.write: "\r\n" 69 | 70 | - delay: 2s 71 | 72 | - uart.write: "PID\t0xA06C\r\n" 73 | - uart.write: "FWE\t316FF\r\n" 74 | - uart.write: "SER#\tHQ2247EP3FN\r\n" 75 | - uart.write: "V\t52700\r\n" 76 | - uart.write: "I\t7600\r\n" 77 | - uart.write: "VPV\t103880\r\n" 78 | - uart.write: "PPV\t412\r\n" 79 | - uart.write: "MPPT\t2\r\n" 80 | - uart.write: "CS\t3\r\n" 81 | - uart.write: "OR\t0x00000000\r\n" 82 | - uart.write: "Checksum\t\xF9:A2001002570030092\n" 83 | - uart.write: ":ADBED00D10AA8\n" 84 | - uart.write: ":ABCED0024A10000DD\n" 85 | - uart.write: ":ABBED009C28DF\n" 86 | - uart.write: ":AB4ED00BEA100004B\n" 87 | - uart.write: ":AB5ED00802801\n" 88 | - uart.write: ":AD5ED009714DE\n" 89 | - uart.write: ":A8DED00971426\n" 90 | - uart.write: "\r\n" 91 | - uart.write: "ERR\t0\r\n" 92 | - uart.write: "LOAD\tON\r\n" 93 | - uart.write: "Relay\tOFF\r\n" 94 | - uart.write: "H19\t1158\r\n" 95 | - uart.write: "H20\t77\r\n" 96 | - uart.write: "H21\t479\r\n" 97 | - uart.write: "H22\t135\r\n" 98 | - uart.write: "H23\t525\r\n" 99 | - uart.write: "HSDS\t2\r\n" 100 | - uart.write: "Checksum\t\x05" 101 | - uart.write: "\r\n" 102 | -------------------------------------------------------------------------------- /tests/fake-smartsolar-mppt.yaml: -------------------------------------------------------------------------------- 1 | substitutions: 2 | name: fake-smartsolar-mppt 3 | tx_pin: GPIO4 4 | rx_pin: GPIO5 5 | 6 | esphome: 7 | name: ${name} 8 | 9 | esp8266: 10 | board: d1_mini 11 | 12 | wifi: 13 | ssid: !secret wifi_ssid 14 | password: !secret wifi_password 15 | 16 | ota: 17 | platform: esphome 18 | 19 | logger: 20 | level: DEBUG 21 | 22 | api: 23 | reboot_timeout: 0s 24 | 25 | uart: 26 | baud_rate: 19200 27 | tx_pin: ${tx_pin} 28 | rx_pin: ${rx_pin} 29 | debug: 30 | direction: BOTH 31 | dummy_receiver: true 32 | after: 33 | delimiter: "\n" 34 | sequence: 35 | - lambda: UARTDebug::log_string(direction, bytes); 36 | 37 | interval: 38 | - interval: 3s 39 | then: 40 | - uart.write: "PID\t0xA053\r\n" 41 | - uart.write: "FW\t156\r\n" 42 | - uart.write: "SER#\tHQ1942K7LJ8\r\n" 43 | - uart.write: "V\t12450\r\n" 44 | - uart.write: "I\t0\r\n" 45 | - uart.write: "VPV\t10\r\n" 46 | - uart.write: "PPV\t0\r\n" 47 | - uart.write: "CS\t0\r\n" 48 | - uart.write: "MPPT\t0\r\n" 49 | - uart.write: "OR\t0x00000001\r\n" 50 | - uart.write: "ERR\t0\r\n" 51 | - uart.write: "LOAD\tON\r\n" 52 | - uart.write: "IL\t0\r\n" 53 | - uart.write: "H19\t3\r\n" 54 | - uart.write: "H20\t0\r\n" 55 | - uart.write: "H21\t24\r\n" 56 | - uart.write: "H22\t1\r\n" 57 | - uart.write: "H23\t15\r\n" 58 | - uart.write: "HSDS\t3\r\n" 59 | - uart.write: "Checksum\t\xE1" 60 | - uart.write: "\r\n" 61 | --------------------------------------------------------------------------------