diff --git a/.clang-format b/.clang-format index a7c337f80e..f2d86c57cd 100644 --- a/.clang-format +++ b/.clang-format @@ -49,7 +49,7 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: true ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true -DerivePointerAlignment: true +DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true diff --git a/.clang-tidy b/.clang-tidy index 1202d12c27..fc5ce854f1 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -5,30 +5,19 @@ Checks: >- -android-*, -boost-*, -bugprone-branch-clone, - -bugprone-macro-parentheses, -bugprone-narrowing-conversions, - -bugprone-reserved-identifier, -bugprone-signed-char-misuse, - -bugprone-suspicious-include, -bugprone-too-small-loop-variable, - -bugprone-unhandled-self-assignment, - -cert-dcl37-c, -cert-dcl50-cpp, - -cert-dcl51-cpp, -cert-err58-cpp, - -cert-oop54-cpp, -cert-oop57-cpp, -cert-str34-c, - -clang-analyzer-core.CallAndMessage, - -clang-analyzer-optin.*, + -clang-analyzer-optin.cplusplus.UninitializedObject, -clang-analyzer-osx.*, - -clang-analyzer-security.*, -clang-diagnostic-shadow-field, -cppcoreguidelines-avoid-c-arrays, -cppcoreguidelines-avoid-goto, -cppcoreguidelines-avoid-magic-numbers, - -cppcoreguidelines-avoid-non-const-global-variables, - -cppcoreguidelines-c-copy-assignment-signature, -cppcoreguidelines-init-variables, -cppcoreguidelines-macro-usage, -cppcoreguidelines-narrowing-conversions, @@ -45,17 +34,17 @@ Checks: >- -cppcoreguidelines-pro-type-union-access, -cppcoreguidelines-pro-type-vararg, -cppcoreguidelines-special-member-functions, - -fuchsia-*, -fuchsia-default-arguments, -fuchsia-multiple-inheritance, -fuchsia-overloaded-operator, -fuchsia-statically-constructed-objects, + -fuchsia-default-arguments-declarations, + -fuchsia-default-arguments-calls, -google-build-using-namespace, -google-explicit-constructor, -google-readability-braces-around-statements, -google-readability-casting, -google-readability-todo, - -google-runtime-int, -google-runtime-references, -hicpp-*, -llvm-else-after-return, @@ -65,12 +54,8 @@ Checks: >- -llvmlibc-*, -misc-non-private-member-variables-in-classes, -misc-no-recursion, - -misc-unconventional-assign-operator, -misc-unused-parameters, -modernize-avoid-c-arrays, - -modernize-deprecated-headers, - -modernize-pass-by-value, - -modernize-pass-by-value, -modernize-return-braced-init-list, -modernize-use-auto, -modernize-use-default-member-init, @@ -78,7 +63,6 @@ Checks: >- -modernize-use-trailing-return-type, -mpi-*, -objc-*, - -performance-unnecessary-value-param, -readability-braces-around-statements, -readability-const-return-type, -readability-convert-member-functions-to-static, @@ -94,8 +78,7 @@ Checks: >- -readability-redundant-string-init, -readability-uppercase-literal-suffix, -readability-use-anyofallof, - -warnings-as-errors, - -zircon-* + -warnings-as-errors WarningsAsErrors: '*' HeaderFilterRegex: '^.*/src/esphome/.*' AnalyzeTemporaryDtors: false diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 8f7d751437..3904962d7c 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,16 +2,29 @@ "name": "ESPHome Dev", "context": "..", "dockerFile": "../docker/Dockerfile.dev", - "postCreateCommand": "mkdir -p config && pip3 install -e .", - "runArgs": ["--privileged", "-e", "ESPHOME_DASHBOARD_USE_PING=1"], + "postCreateCommand": [ + "script/devcontainer-post-create" + ], + "runArgs": [ + "--privileged", + "-e", + "ESPHOME_DASHBOARD_USE_PING=1" + ], "appPort": 6052, "extensions": [ + // python "ms-python.python", "visualstudioexptteam.vscodeintellicode", - "redhat.vscode-yaml" + // yaml + "redhat.vscode-yaml", + // cpp + "ms-vscode.cpptools", + // editorconfig + "editorconfig.editorconfig", ], "settings": { - "python.pythonPath": "/usr/local/bin/python", + "python.languageServer": "Pylance", + "python.pythonPath": "/usr/bin/python3", "python.linting.pylintEnabled": true, "python.linting.enabled": true, "python.formatting.provider": "black", @@ -19,7 +32,7 @@ "editor.formatOnSave": true, "editor.formatOnType": true, "files.trimTrailingWhitespace": true, - "terminal.integrated.shell.linux": "/bin/bash", + "terminal.integrated.defaultProfile.linux": "bash", "yaml.customTags": [ "!secret scalar", "!lambda scalar", @@ -27,6 +40,18 @@ "!include_dir_list scalar", "!include_dir_merge_list scalar", "!include_dir_merge_named scalar" - ] + ], + "files.exclude": { + "**/.git": true, + "**/.DS_Store": true, + "**/*.pyc": { + "when": "$(basename).py" + }, + "**/__pycache__": true + }, + "files.associations": { + "**/.vscode/*.json": "jsonc" + }, + "C_Cpp.clang_format_path": "/usr/bin/clang-format-11", } } diff --git a/.dockerignore b/.dockerignore index e1baed38ca..9f14b98059 100644 --- a/.dockerignore +++ b/.dockerignore @@ -103,6 +103,10 @@ venv.bak/ # mypy .mypy_cache/ +# PlatformIO +.pio/ + +# ESPHome config/ examples/ Dockerfile diff --git a/.editorconfig b/.editorconfig index 29cbb1e32f..8ccf1eeebc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,7 +7,7 @@ insert_final_newline = true charset = utf-8 # python -[*.{py}] +[*.py] indent_style = space indent_size = 4 @@ -25,4 +25,10 @@ indent_size = 2 [*.{yaml,yml}] indent_style = space indent_size = 2 -quote_type = single \ No newline at end of file +quote_type = single + +# JSON +[*.json] +indent_style = space +indent_size = 2 + diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index b5f8b7b0e0..45fd3e141b 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -3,7 +3,7 @@ name: CI for docker images # Only run when docker paths change on: push: - branches: [dev, beta, master] + branches: [dev, beta, release] paths: - 'docker/**' - '.github/workflows/**' @@ -18,38 +18,23 @@ jobs: name: Build docker containers runs-on: ubuntu-latest strategy: - fail-fast: false matrix: arch: [amd64, armv7, aarch64] - build_type: ["hassio", "docker"] + build_type: ["ha-addon", "docker", "lint"] steps: - - uses: actions/checkout@v2 - - name: Set up env variables - run: | - base_version="3.4.0" + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.9' + - name: Set TAG + run: | + echo "TAG=check" >> $GITHUB_ENV - if [[ "${{ matrix.build_type }}" == "hassio" ]]; then - build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" - build_to="esphome/esphome-hassio-${{ matrix.arch }}" - dockerfile="docker/Dockerfile.hassio" - else - build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}" - build_to="esphome/esphome-${{ matrix.arch }}" - dockerfile="docker/Dockerfile" - fi - - echo "BUILD_FROM=${build_from}" >> $GITHUB_ENV - echo "BUILD_TO=${build_to}" >> $GITHUB_ENV - echo "DOCKERFILE=${dockerfile}" >> $GITHUB_ENV - - name: Pull for cache - run: | - docker pull "${BUILD_TO}:dev" || true - - name: Register QEMU binfmt - run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes - - run: | - docker build \ - --build-arg "BUILD_FROM=${BUILD_FROM}" \ - --build-arg "BUILD_VERSION=ci" \ - --cache-from "${BUILD_TO}:dev" \ - --file "${DOCKERFILE}" \ - . + - name: Run build + run: | + docker/build.py \ + --tag "${TAG}" \ + --arch "${{ matrix.arch }}" \ + --build-type "${{ matrix.build_type }}" \ + build diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a27cbe67b0..4ccaaa47b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,40 +4,36 @@ name: CI on: push: - # On dev branch release-dev already performs CI checks - # On other branches the `pull_request` trigger will be used - branches: [beta, master] + branches: [dev, beta, release] pull_request: jobs: - lint-clang-format: + ci-with-container: + name: ${{ matrix.name }} runs-on: ubuntu-latest - # cpp lint job runs with esphome-lint docker image so that clang-format-* - # doesn't have to be installed - container: esphome/esphome-lint:1.1 - steps: - - uses: actions/checkout@v2 - # Set up the pio project so that the cpp checks know how files are compiled - # (build flags, libraries etc) - - name: Set up platformio environment - run: pio init --ide atom - - - name: Run clang-format - run: script/clang-format -i - - name: Suggest changes - run: script/ci-suggest-changes - - lint-clang-tidy: - runs-on: ubuntu-latest - # cpp lint job runs with esphome-lint docker image so that clang-format-* - # doesn't have to be installed - container: esphome/esphome-lint:1.1 - # Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files strategy: fail-fast: false matrix: - split: [1, 2, 3, 4] + include: + - id: clang-format + name: Run script/clang-format + - id: clang-tidy + name: Run script/clang-tidy 1/4 + split: 1 + - id: clang-tidy + name: Run script/clang-tidy 2/4 + split: 2 + - id: clang-tidy + name: Run script/clang-tidy 3/4 + split: 3 + - id: clang-tidy + name: Run script/clang-tidy 4/4 + split: 4 + + # cpp lint job runs with esphome-lint docker image so that clang-format-* + # doesn't have to be installed + container: ghcr.io/esphome/esphome-lint:1.1 steps: - uses: actions/checkout@v2 # Set up the pio project so that the cpp checks know how files are compiled @@ -45,26 +41,57 @@ jobs: - name: Set up platformio environment run: pio init --ide atom - - name: Register problem matchers run: | echo "::add-matcher::.github/workflows/matchers/clang-tidy.json" echo "::add-matcher::.github/workflows/matchers/gcc.json" + + - name: Run clang-format + run: script/clang-format -i + if: ${{ matrix.id == 'clang-format' }} + - name: Run clang-tidy run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }} + if: ${{ matrix.id == 'clang-tidy' }} + - name: Suggest changes run: script/ci-suggest-changes - lint-python: + ci: # Don't use the esphome-lint docker image because it may contain outdated requirements. # This way, all dependencies are cached via the cache action. + name: ${{ matrix.name }} runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - id: ci-custom + name: Run script/ci-custom + - id: lint-python + name: Run script/lint-python + - id: test + file: tests/test1.yaml + name: Test tests/test1.yaml + - id: test + file: tests/test2.yaml + name: Test tests/test2.yaml + - id: test + file: tests/test3.yaml + name: Test tests/test3.yaml + - id: test + file: tests/test4.yaml + name: Test tests/test4.yaml + - id: pytest + name: Run pytest + steps: - uses: actions/checkout@v2 - name: Set up Python uses: actions/setup-python@v2 with: python-version: '3.7' + - name: Cache pip modules uses: actions/cache@v1 with: @@ -72,6 +99,17 @@ jobs: key: esphome-pip-3.7-${{ hashFiles('setup.py') }} restore-keys: | esphome-pip-3.7- + + # Use per test platformio cache because tests have different platform versions + - name: Cache ~/.platformio + uses: actions/cache@v1 + with: + path: ~/.platformio + key: test-home-platformio-${{ matrix.file }}-${{ hashFiles('esphome/core/config.py') }} + restore-keys: | + test-home-platformio-${{ matrix.file }}- + if: ${{ matrix.id == 'test' }} + - name: Set up python environment run: script/setup @@ -80,82 +118,22 @@ jobs: echo "::add-matcher::.github/workflows/matchers/ci-custom.json" echo "::add-matcher::.github/workflows/matchers/lint-python.json" echo "::add-matcher::.github/workflows/matchers/python.json" + echo "::add-matcher::.github/workflows/matchers/pytest.json" + echo "::add-matcher::.github/workflows/matchers/gcc.json" + - name: Lint Custom - run: script/ci-custom.py + run: | + script/ci-custom.py + script/build_codeowners.py --check + if: ${{ matrix.id == 'ci-custom' }} - name: Lint Python run: script/lint-python - - name: Lint CODEOWNERS - run: script/build_codeowners.py --check + if: ${{ matrix.id == 'lint-python' }} - test: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - test: - - test1 - - test2 - - test3 - - test4 - - test5 - steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.7' - - name: Cache pip modules - uses: actions/cache@v1 - with: - path: ~/.cache/pip - key: esphome-pip-3.7-${{ hashFiles('setup.py') }} - restore-keys: | - esphome-pip-3.7- - # Use per test platformio cache because tests have different platform versions - - name: Cache ~/.platformio - uses: actions/cache@v1 - with: - path: ~/.platformio - key: test-home-platformio-${{ matrix.test }}-${{ hashFiles('esphome/core/config.py') }} - restore-keys: | - test-home-platformio-${{ matrix.test }}- - - name: Set up environment - run: script/setup + - run: esphome compile ${{ matrix.file }} + if: ${{ matrix.id == 'test' }} - - - name: Register problem matchers - run: | - echo "::add-matcher::.github/workflows/matchers/gcc.json" - echo "::add-matcher::.github/workflows/matchers/python.json" - - run: esphome compile tests/${{ matrix.test }}.yaml - - pytest: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.7' - - name: Cache pip modules - uses: actions/cache@v1 - with: - path: ~/.cache/pip - key: esphome-pip-3.7-${{ hashFiles('setup.py') }} - restore-keys: | - esphome-pip-3.7- - - name: Set up environment - run: script/setup - - name: Install Github Actions annotator - run: pip install pytest-github-actions-annotate-failures - - - name: Register problem matchers - run: | - echo "::add-matcher::.github/workflows/matchers/python.json" - name: Run pytest run: | - pytest \ - -qq \ - --durations=10 \ - -o console_output_style=count \ - tests + pytest -vv --tb=native tests + if: ${{ matrix.id == 'pytest' }} diff --git a/.github/workflows/docker-lint-build.yml b/.github/workflows/docker-lint-build.yml index d254ac332a..32aec87cdd 100644 --- a/.github/workflows/docker-lint-build.yml +++ b/.github/workflows/docker-lint-build.yml @@ -13,30 +13,88 @@ on: - '.github/workflows/docker-lint-build.yml' jobs: - publish-docker-lint-iage: - name: Build docker containers + deploy-docker: + name: Build and publish docker containers + if: github.repository == 'esphome/esphome' runs-on: ubuntu-latest + strategy: + matrix: + arch: [amd64, armv7, aarch64] + build_type: ["lint"] steps: - - uses: actions/checkout@v2 - - name: Set TAG - run: | - echo "TAG=1.1" >> $GITHUB_ENV - - name: Pull for cache - run: | - docker pull "esphome/esphome-lint:latest" || true - - name: Build - run: | - docker build \ - --cache-from "esphome/esphome-lint:latest" \ - --file "docker/Dockerfile.lint" \ - --tag "esphome/esphome-lint:latest" \ - --tag "esphome/esphome-lint:${TAG}" \ - . - - name: Log in to docker hub - env: - DOCKER_USER: ${{ secrets.DOCKER_USER }} - DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} - run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}" - - run: | - docker push "esphome/esphome-lint:${TAG}" - docker push "esphome/esphome-lint:latest" + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.9' + - name: Set TAG + run: | + echo "TAG=1.1" >> $GITHUB_ENV + + - name: Run build + run: | + docker/build.py \ + --tag "${TAG}" \ + --arch "${{ matrix.arch }}" \ + --build-type "${{ matrix.build_type }}" \ + build + + - name: Log in to docker hub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_USER }} + password: ${{ secrets.DOCKER_PASSWORD }} + - name: Log in to the GitHub container registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Run push + run: | + docker/build.py \ + --tag "${TAG}" \ + --arch "${{ matrix.arch }}" \ + --build-type "${{ matrix.build_type }}" \ + push + + deploy-docker-manifest: + if: github.repository == 'esphome/esphome' + runs-on: ubuntu-latest + needs: [deploy-docker] + strategy: + matrix: + build_type: ["lint"] + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.9' + - name: Set TAG + run: | + echo "TAG=1.1" >> $GITHUB_ENV + - name: Enable experimental manifest support + run: | + mkdir -p ~/.docker + echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json + + - name: Log in to docker hub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_USER }} + password: ${{ secrets.DOCKER_PASSWORD }} + - name: Log in to the GitHub container registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Run manifest + run: | + docker/build.py \ + --tag "${TAG}" \ + --build-type "${{ matrix.build_type }}" \ + manifest diff --git a/.github/workflows/matchers/pytest.json b/.github/workflows/matchers/pytest.json new file mode 100644 index 0000000000..0eb8f050e6 --- /dev/null +++ b/.github/workflows/matchers/pytest.json @@ -0,0 +1,19 @@ +{ + "problemMatcher": [ + { + "owner": "pytest", + "fileLocation": "absolute", + "pattern": [ + { + "regexp": "^\\s+File \"(.*)\", line (\\d+), in (.*)$", + "file": 1, + "line": 2 + }, + { + "regexp": "^\\s+(.*)$", + "message": 1 + } + ] + } + ] +} diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml deleted file mode 100644 index f8b90d524f..0000000000 --- a/.github/workflows/release-dev.yml +++ /dev/null @@ -1,247 +0,0 @@ -name: Publish dev releases to docker hub - -on: - push: - branches: - - dev - -jobs: - # THE LINT/TEST JOBS ARE COPIED FROM ci.yaml - - lint-clang-format: - runs-on: ubuntu-latest - # cpp lint job runs with esphome-lint docker image so that clang-format-* - # doesn't have to be installed - container: esphome/esphome-lint:1.1 - steps: - - uses: actions/checkout@v2 - # Set up the pio project so that the cpp checks know how files are compiled - # (build flags, libraries etc) - - name: Set up platformio environment - run: pio init --ide atom - - - name: Run clang-format - run: script/clang-format -i - - name: Suggest changes - run: script/ci-suggest-changes - - lint-clang-tidy: - runs-on: ubuntu-latest - # cpp lint job runs with esphome-lint docker image so that clang-format-* - # doesn't have to be installed - container: esphome/esphome-lint:1.1 - # Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files - strategy: - fail-fast: false - matrix: - split: [1, 2, 3, 4] - steps: - - uses: actions/checkout@v2 - # Set up the pio project so that the cpp checks know how files are compiled - # (build flags, libraries etc) - - name: Set up platformio environment - run: pio init --ide atom - - - - name: Register problem matchers - run: | - echo "::add-matcher::.github/workflows/matchers/clang-tidy.json" - echo "::add-matcher::.github/workflows/matchers/gcc.json" - - name: Run clang-tidy - run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }} - - name: Suggest changes - run: script/ci-suggest-changes - - lint-python: - # Don't use the esphome-lint docker image because it may contain outdated requirements. - # This way, all dependencies are cached via the cache action. - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.7' - - name: Cache pip modules - uses: actions/cache@v1 - with: - path: ~/.cache/pip - key: esphome-pip-3.7-${{ hashFiles('setup.py') }} - restore-keys: | - esphome-pip-3.7- - - name: Set up python environment - run: script/setup - - - name: Register problem matchers - run: | - echo "::add-matcher::.github/workflows/matchers/ci-custom.json" - echo "::add-matcher::.github/workflows/matchers/lint-python.json" - echo "::add-matcher::.github/workflows/matchers/python.json" - - name: Lint Custom - run: script/ci-custom.py - - name: Lint Python - run: script/lint-python - - name: Lint CODEOWNERS - run: script/build_codeowners.py --check - - test: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - test: - - test1 - - test2 - - test3 - - test4 - - test5 - steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.7' - - name: Cache pip modules - uses: actions/cache@v1 - with: - path: ~/.cache/pip - key: esphome-pip-3.7-${{ hashFiles('setup.py') }} - restore-keys: | - esphome-pip-3.7- - # Use per test platformio cache because tests have different platform versions - - name: Cache ~/.platformio - uses: actions/cache@v1 - with: - path: ~/.platformio - key: test-home-platformio-${{ matrix.test }}-${{ hashFiles('esphome/core/config.py') }} - restore-keys: | - test-home-platformio-${{ matrix.test }}- - - name: Set up environment - run: script/setup - - - - name: Register problem matchers - run: | - echo "::add-matcher::.github/workflows/matchers/gcc.json" - echo "::add-matcher::.github/workflows/matchers/python.json" - - run: esphome compile tests/${{ matrix.test }}.yaml - - pytest: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.7' - - name: Cache pip modules - uses: actions/cache@v1 - with: - path: ~/.cache/pip - key: esphome-pip-3.7-${{ hashFiles('setup.py') }} - restore-keys: | - esphome-pip-3.7- - - name: Set up environment - run: script/setup - - name: Install Github Actions annotator - run: pip install pytest-github-actions-annotate-failures - - - name: Register problem matchers - run: | - echo "::add-matcher::.github/workflows/matchers/python.json" - - name: Run pytest - run: | - pytest \ - -qq \ - --durations=10 \ - -o console_output_style=count \ - tests - - deploy-docker: - name: Build and publish docker containers - if: github.repository == 'esphome/esphome' - runs-on: ubuntu-latest - needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest] - strategy: - matrix: - arch: [amd64, armv7, aarch64] - # Hassio dev image doesn't use esphome/esphome-hassio-$arch and uses base directly - build_type: ["docker"] - steps: - - uses: actions/checkout@v2 - - name: Set TAG - run: | - TAG="${GITHUB_SHA:0:7}" - echo "TAG=${TAG}" >> $GITHUB_ENV - - name: Set up env variables - run: | - base_version="3.4.0" - - if [[ "${{ matrix.build_type }}" == "hassio" ]]; then - build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" - build_to="esphome/esphome-hassio-${{ matrix.arch }}" - dockerfile="docker/Dockerfile.hassio" - else - build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}" - build_to="esphome/esphome-${{ matrix.arch }}" - dockerfile="docker/Dockerfile" - fi - - echo "BUILD_FROM=${build_from}" >> $GITHUB_ENV - echo "BUILD_TO=${build_to}" >> $GITHUB_ENV - echo "DOCKERFILE=${dockerfile}" >> $GITHUB_ENV - - name: Pull for cache - run: | - docker pull "${BUILD_TO}:dev" || true - - name: Register QEMU binfmt - run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes - - run: | - docker build \ - --build-arg "BUILD_FROM=${BUILD_FROM}" \ - --build-arg "BUILD_VERSION=${TAG}" \ - --tag "${BUILD_TO}:${TAG}" \ - --tag "${BUILD_TO}:dev" \ - --cache-from "${BUILD_TO}:dev" \ - --file "${DOCKERFILE}" \ - . - - name: Log in to docker hub - env: - DOCKER_USER: ${{ secrets.DOCKER_USER }} - DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} - run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}" - - run: | - docker push "${BUILD_TO}:${TAG}" - docker push "${BUILD_TO}:dev" - - - deploy-docker-manifest: - if: github.repository == 'esphome/esphome' - runs-on: ubuntu-latest - needs: [deploy-docker] - steps: - - name: Enable experimental manifest support - run: | - mkdir -p ~/.docker - echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json - - name: Set TAG - run: | - TAG="${GITHUB_SHA:0:7}" - echo "TAG=${TAG}" >> $GITHUB_ENV - - name: Log in to docker hub - env: - DOCKER_USER: ${{ secrets.DOCKER_USER }} - DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} - run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}" - - name: "Create the manifest" - run: | - docker manifest create esphome/esphome:${TAG} \ - esphome/esphome-aarch64:${TAG} \ - esphome/esphome-amd64:${TAG} \ - esphome/esphome-armv7:${TAG} - docker manifest push esphome/esphome:${TAG} - - docker manifest create esphome/esphome:dev \ - esphome/esphome-aarch64:${TAG} \ - esphome/esphome-amd64:${TAG} \ - esphome/esphome-armv7:${TAG} - docker manifest push esphome/esphome:dev diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9523a2164c..1b15b540f6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,164 +1,35 @@ name: Publish Release on: + workflow_dispatch: release: types: [published] + schedule: + - cron: "0 2 * * *" jobs: - # THE LINT/TEST JOBS ARE COPIED FROM ci.yaml - - lint-clang-format: + init: + name: Initialize build runs-on: ubuntu-latest - # cpp lint job runs with esphome-lint docker image so that clang-format-* - # doesn't have to be installed - container: esphome/esphome-lint:1.1 + outputs: + tag: ${{ steps.tag.outputs.tag }} steps: - uses: actions/checkout@v2 - # Set up the pio project so that the cpp checks know how files are compiled - # (build flags, libraries etc) - - name: Set up platformio environment - run: pio init --ide atom - - - name: Run clang-format - run: script/clang-format -i - - name: Suggest changes - run: script/ci-suggest-changes - - lint-clang-tidy: - runs-on: ubuntu-latest - # cpp lint job runs with esphome-lint docker image so that clang-format-* - # doesn't have to be installed - container: esphome/esphome-lint:1.1 - # Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files - strategy: - fail-fast: false - matrix: - split: [1, 2, 3, 4] - steps: - - uses: actions/checkout@v2 - # Set up the pio project so that the cpp checks know how files are compiled - # (build flags, libraries etc) - - name: Set up platformio environment - run: pio init --ide atom - - - - name: Register problem matchers + - name: Get tag + id: tag run: | - echo "::add-matcher::.github/workflows/matchers/clang-tidy.json" - echo "::add-matcher::.github/workflows/matchers/gcc.json" - - name: Run clang-tidy - run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }} - - name: Suggest changes - run: script/ci-suggest-changes - - lint-python: - # Don't use the esphome-lint docker image because it may contain outdated requirements. - # This way, all dependencies are cached via the cache action. - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.7' - - name: Cache pip modules - uses: actions/cache@v1 - with: - path: ~/.cache/pip - key: esphome-pip-3.7-${{ hashFiles('setup.py') }} - restore-keys: | - esphome-pip-3.7- - - name: Set up python environment - run: script/setup - - - name: Register problem matchers - run: | - echo "::add-matcher::.github/workflows/matchers/ci-custom.json" - echo "::add-matcher::.github/workflows/matchers/lint-python.json" - echo "::add-matcher::.github/workflows/matchers/python.json" - - name: Lint Custom - run: script/ci-custom.py - - name: Lint Python - run: script/lint-python - - name: Lint CODEOWNERS - run: script/build_codeowners.py --check - - test: - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - test: - - test1 - - test2 - - test3 - - test4 - - test5 - steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.7' - - name: Cache pip modules - uses: actions/cache@v1 - with: - path: ~/.cache/pip - key: esphome-pip-3.7-${{ hashFiles('setup.py') }} - restore-keys: | - esphome-pip-3.7- - # Use per test platformio cache because tests have different platform versions - - name: Cache ~/.platformio - uses: actions/cache@v1 - with: - path: ~/.platformio - key: test-home-platformio-${{ matrix.test }}-${{ hashFiles('esphome/core/config.py') }} - restore-keys: | - test-home-platformio-${{ matrix.test }}- - - name: Set up environment - run: script/setup - - - name: Register problem matchers - run: | - echo "::add-matcher::.github/workflows/matchers/gcc.json" - echo "::add-matcher::.github/workflows/matchers/python.json" - - run: esphome compile tests/${{ matrix.test }}.yaml - - pytest: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.7' - - name: Cache pip modules - uses: actions/cache@v1 - with: - path: ~/.cache/pip - key: esphome-pip-3.7-${{ hashFiles('setup.py') }} - restore-keys: | - esphome-pip-3.7- - - name: Set up environment - run: script/setup - - name: Install Github Actions annotator - run: pip install pytest-github-actions-annotate-failures - - - name: Register problem matchers - run: | - echo "::add-matcher::.github/workflows/matchers/python.json" - - name: Run pytest - run: | - pytest \ - -qq \ - --durations=10 \ - -o console_output_style=count \ - tests + if [[ "$GITHUB_EVENT_NAME" = "release" ]]; then + TAG="${GITHUB_REF#refs/tags/v}" + else + TAG=$(cat esphome/const.py | sed -n -E "s/^__version__\s+=\s+\"(.+)\"$/\1/p") + today="$(date --utc '+%Y%m%d')" + TAG="${TAG}${today}" + fi + echo "::set-output name=tag::${TAG}" deploy-pypi: name: Build and publish to PyPi - if: github.repository == 'esphome/esphome' - needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest] + if: github.repository == 'esphome/esphome' && github.event_name == 'release' runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -182,119 +53,85 @@ jobs: name: Build and publish docker containers if: github.repository == 'esphome/esphome' runs-on: ubuntu-latest - needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest] + needs: [init] strategy: matrix: arch: [amd64, armv7, aarch64] - build_type: ["hassio", "docker"] + build_type: ["ha-addon", "docker"] steps: - - uses: actions/checkout@v2 - - name: Set TAG - run: | - TAG="${GITHUB_REF#refs/tags/v}" - echo "TAG=${TAG}" >> $GITHUB_ENV - - name: Set up env variables - run: | - base_version="3.4.0" + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.9' - if [[ "${{ matrix.build_type }}" == "hassio" ]]; then - build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" - build_to="esphome/esphome-hassio-${{ matrix.arch }}" - dockerfile="docker/Dockerfile.hassio" - else - build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}" - build_to="esphome/esphome-${{ matrix.arch }}" - dockerfile="docker/Dockerfile" - fi + - name: Run build + run: | + docker/build.py \ + --tag "${{ needs.init.outputs.tag }}" \ + --arch "${{ matrix.arch }}" \ + --build-type "${{ matrix.build_type }}" \ + build - if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then - cache_tag="beta" - else - cache_tag="latest" - fi + - name: Log in to docker hub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_USER }} + password: ${{ secrets.DOCKER_PASSWORD }} + - name: Log in to the GitHub container registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - # Set env variables so these values don't need to be calculated again - echo "BUILD_FROM=${build_from}" >> $GITHUB_ENV - echo "BUILD_TO=${build_to}" >> $GITHUB_ENV - echo "DOCKERFILE=${dockerfile}" >> $GITHUB_ENV - echo "CACHE_TAG=${cache_tag}" >> $GITHUB_ENV - - name: Pull for cache - run: | - docker pull "${BUILD_TO}:${CACHE_TAG}" || true - - name: Register QEMU binfmt - run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes - - run: | - docker build \ - --build-arg "BUILD_FROM=${BUILD_FROM}" \ - --build-arg "BUILD_VERSION=${TAG}" \ - --tag "${BUILD_TO}:${TAG}" \ - --cache-from "${BUILD_TO}:${CACHE_TAG}" \ - --file "${DOCKERFILE}" \ - . - - name: Log in to docker hub - env: - DOCKER_USER: ${{ secrets.DOCKER_USER }} - DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} - run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}" - - run: docker push "${BUILD_TO}:${TAG}" - - # Always publish to beta tag (also full releases) - - name: Publish docker beta tag - run: | - docker tag "${BUILD_TO}:${TAG}" "${BUILD_TO}:beta" - docker push "${BUILD_TO}:beta" - - - if: ${{ !github.event.release.prerelease }} - name: Publish docker latest tag - run: | - docker tag "${BUILD_TO}:${TAG}" "${BUILD_TO}:latest" - docker push "${BUILD_TO}:latest" + - name: Run push + run: | + docker/build.py \ + --tag "${{ needs.init.outputs.tag }}" \ + --arch "${{ matrix.arch }}" \ + --build-type "${{ matrix.build_type }}" \ + push deploy-docker-manifest: if: github.repository == 'esphome/esphome' runs-on: ubuntu-latest - needs: [deploy-docker] + needs: [init, deploy-docker] + strategy: + matrix: + build_type: ["ha-addon", "docker"] steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.9' - name: Enable experimental manifest support run: | mkdir -p ~/.docker echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json - - name: Set TAG - run: | - TAG="${GITHUB_REF#refs/tags/v}" - echo "TAG=${TAG}" >> $GITHUB_ENV + - name: Log in to docker hub - env: - DOCKER_USER: ${{ secrets.DOCKER_USER }} - DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} - run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}" - - name: "Create the manifest" - run: | - docker manifest create esphome/esphome:${TAG} \ - esphome/esphome-aarch64:${TAG} \ - esphome/esphome-amd64:${TAG} \ - esphome/esphome-armv7:${TAG} - docker manifest push esphome/esphome:${TAG} + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_USER }} + password: ${{ secrets.DOCKER_PASSWORD }} + - name: Log in to the GitHub container registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} - - name: Publish docker beta tag + - name: Run manifest run: | - docker manifest create esphome/esphome:beta \ - esphome/esphome-aarch64:${TAG} \ - esphome/esphome-amd64:${TAG} \ - esphome/esphome-armv7:${TAG} - docker manifest push esphome/esphome:beta - - - name: Publish docker latest tag - if: ${{ !github.event.release.prerelease }} - run: | - docker manifest create esphome/esphome:latest \ - esphome/esphome-aarch64:${TAG} \ - esphome/esphome-amd64:${TAG} \ - esphome/esphome-armv7:${TAG} - docker manifest push esphome/esphome:latest + docker/build.py \ + --tag "${{ needs.init.outputs.tag }}" \ + --build-type "${{ matrix.build_type }}" \ + manifest deploy-hassio-repo: - if: github.repository == 'esphome/esphome' + if: github.repository == 'esphome/esphome' && github.event_name == 'release' runs-on: ubuntu-latest needs: [deploy-docker] steps: @@ -307,4 +144,4 @@ jobs: -X POST \ -H "Accept: application/vnd.github.v3+json" \ https://api.github.com/repos/esphome/hassio/actions/workflows/bump-version.yml/dispatches \ - -d "{\"ref\":\"master\",\"inputs\":{\"version\":\"$TAG\"}}" + -d "{\"ref\":\"main\",\"inputs\":{\"version\":\"$TAG\"}}" diff --git a/.gitignore b/.gitignore index a24550ad54..954ecb2cb8 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,9 @@ __pycache__/ # Intellij Idea .idea +# Vim +*.swp + # Hide some OS X stuff .DS_Store .AppleDouble diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1e56aa17cf..a821c21fa7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,5 +23,5 @@ repos: - id: no-commit-to-branch args: - --branch=dev - - --branch=master + - --branch=release - --branch=beta diff --git a/.vscode/tasks.json b/.vscode/tasks.json index cc83d8bcdf..8c55646f28 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,11 +1,32 @@ { - "version": "2.0.0", - "tasks": [ + "version": "2.0.0", + "tasks": [ + { + "label": "run", + "type": "shell", + "command": "python3 -m esphome dashboard config/", + "problemMatcher": [] + }, + { + "label": "clang-tidy", + "type": "shell", + "command": "test -f .gcc-flags.json || pio init --silent --ide atom; ./script/clang-tidy", + "problemMatcher": [ { - "label": "run", - "type": "shell", - "command": "python3 -m esphome dashboard config", - "problemMatcher": [] + "owner": "clang-tidy", + "fileLocation": "absolute", + "pattern": [ + { + "regexp": "^(.*):(\\d+):(\\d+):\\s+(error):\\s+(.*) \\[([a-z0-9,\\-]+)\\]\\s*$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + ] } - ] + ] + } + ] } diff --git a/CODEOWNERS b/CODEOWNERS index 0594a60ef6..557fe7cc08 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -15,10 +15,12 @@ esphome/components/ac_dimmer/* @glmnet esphome/components/adc/* @esphome/core esphome/components/addressable_light/* @justfalter esphome/components/animation/* @syndlex +esphome/components/anova/* @buxtronix esphome/components/api/* @OttoWinter esphome/components/async_tcp/* @OttoWinter esphome/components/atc_mithermometer/* @ahpohl esphome/components/b_parasite/* @rbaron +esphome/components/ballu/* @bazuchan esphome/components/bang_bang/* @OttoWinter esphome/components/binary_sensor/* @esphome/core esphome/components/ble_client/* @buxtronix @@ -45,6 +47,7 @@ esphome/components/fingerprint_grow/* @OnFreund @loongyh esphome/components/globals/* @esphome/core esphome/components/gpio/* @esphome/core esphome/components/gps/* @coogle +esphome/components/havells_solar/* @sourabhjaiswal esphome/components/homeassistant/* @OttoWinter esphome/components/i2c/* @esphome/core esphome/components/improv/* @jesserockz @@ -70,7 +73,13 @@ esphome/components/midea_ac/* @dudanov esphome/components/midea_dongle/* @dudanov esphome/components/mitsubishi/* @RubyBailey esphome/components/network/* @esphome/core +esphome/components/nextion/* @senexcrenshaw +esphome/components/nextion/binary_sensor/* @senexcrenshaw +esphome/components/nextion/sensor/* @senexcrenshaw +esphome/components/nextion/switch/* @senexcrenshaw +esphome/components/nextion/text_sensor/* @senexcrenshaw esphome/components/nfc/* @jesserockz +esphome/components/number/* @esphome/core esphome/components/ota/* @esphome/core esphome/components/output/* @esphome/core esphome/components/pid/* @OttoWinter diff --git a/README.md b/README.md index f21e748d40..bb6fb37d3a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ESPHome [![Build Status](https://travis-ci.org/esphome/esphome.svg?branch=master)](https://travis-ci.org/esphome/esphome) [![Discord Chat](https://img.shields.io/discord/429907082951524364.svg)](https://discord.gg/KhAMKrd) [![GitHub release](https://img.shields.io/github/release/esphome/esphome.svg)](https://GitHub.com/esphome/esphome/releases/) +# ESPHome [![Discord Chat](https://img.shields.io/discord/429907082951524364.svg)](https://discord.gg/KhAMKrd) [![GitHub release](https://img.shields.io/github/release/esphome/esphome.svg)](https://GitHub.com/esphome/esphome/releases/) [![ESPHome Logo](https://esphome.io/_images/logo-text.png)](https://esphome.io/) diff --git a/docker/Dockerfile b/docker/Dockerfile index 0d126a2944..907c041119 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -ARG BUILD_FROM=esphome/esphome-base-amd64:3.4.0 +ARG BUILD_FROM=esphome/esphome-base:latest FROM ${BUILD_FROM} # First install requirements to leverage caching when requirements don't change diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index ebcf14d1bc..27eaa18da9 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -1,13 +1 @@ -FROM esphome/esphome-base-amd64:3.4.0 - -COPY . . - -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - python3-wheel \ - net-tools \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -WORKDIR /workspaces -ENV SHELL /bin/bash +FROM esphome/esphome-lint:1.1 diff --git a/docker/Dockerfile.hassio b/docker/Dockerfile.hassio index 5dd9339b18..ad80074ada 100644 --- a/docker/Dockerfile.hassio +++ b/docker/Dockerfile.hassio @@ -1,4 +1,4 @@ -ARG BUILD_FROM +ARG BUILD_FROM=esphome/esphome-hassio-base:latest FROM ${BUILD_FROM} # First install requirements to leverage caching when requirements don't change diff --git a/docker/Dockerfile.lint b/docker/Dockerfile.lint index 60d63152a0..3a090c3b41 100644 --- a/docker/Dockerfile.lint +++ b/docker/Dockerfile.lint @@ -1,4 +1,5 @@ -FROM esphome/esphome-lint-base:3.4.0 +ARG BUILD_FROM=esphome/esphome-lint-base:latest +FROM ${BUILD_FROM} COPY requirements.txt requirements_optional.txt requirements_test.txt docker/platformio_install_deps.py platformio.ini / RUN \ diff --git a/docker/build.py b/docker/build.py new file mode 100755 index 0000000000..54a279f845 --- /dev/null +++ b/docker/build.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +from dataclasses import dataclass +import subprocess +import argparse +import platform +import shlex +import re +import sys + + +CHANNEL_DEV = 'dev' +CHANNEL_BETA = 'beta' +CHANNEL_RELEASE = 'release' +CHANNELS = [CHANNEL_DEV, CHANNEL_BETA, CHANNEL_RELEASE] + +ARCH_AMD64 = 'amd64' +ARCH_ARMV7 = 'armv7' +ARCH_AARCH64 = 'aarch64' +ARCHS = [ARCH_AMD64, ARCH_ARMV7, ARCH_AARCH64] + +TYPE_DOCKER = 'docker' +TYPE_HA_ADDON = 'ha-addon' +TYPE_LINT = 'lint' +TYPES = [TYPE_DOCKER, TYPE_HA_ADDON, TYPE_LINT] + + +BASE_VERSION = "3.6.0" + + +parser = argparse.ArgumentParser() +parser.add_argument("--tag", type=str, required=True, help="The main docker tag to push to. If a version number also adds latest and/or beta tag") +parser.add_argument("--arch", choices=ARCHS, required=False, help="The architecture to build for") +parser.add_argument("--build-type", choices=TYPES, required=True, help="The type of build to run") +parser.add_argument("--dry-run", action="store_true", help="Don't run any commands, just print them") +subparsers = parser.add_subparsers(help="Action to perform", dest="command", required=True) +build_parser = subparsers.add_parser("build", help="Build the image") +push_parser = subparsers.add_parser("push", help="Tag the already built image and push it to docker hub") +manifest_parser = subparsers.add_parser("manifest", help="Create a manifest from already pushed images") + + + +# only lists some possibilities, doesn't have to be perfect +# https://stackoverflow.com/a/45125525 +UNAME_TO_ARCH = { + "x86_64": ARCH_AMD64, + "aarch64": ARCH_AARCH64, + "aarch64_be": ARCH_AARCH64, + "arm": ARCH_ARMV7, +} + + +@dataclass(frozen=True) +class DockerParams: + build_from: str + build_to: str + manifest_to: str + dockerfile: str + + @classmethod + def for_type_arch(cls, build_type, arch): + prefix = { + TYPE_DOCKER: "esphome/esphome", + TYPE_HA_ADDON: "esphome/esphome-hassio", + TYPE_LINT: "esphome/esphome-lint" + }[build_type] + build_from = f"ghcr.io/{prefix}-base-{arch}:{BASE_VERSION}" + build_to = f"{prefix}-{arch}" + dockerfile = { + TYPE_DOCKER: "docker/Dockerfile", + TYPE_HA_ADDON: "docker/Dockerfile.hassio", + TYPE_LINT: "docker/Dockerfile.lint", + }[build_type] + return cls( + build_from=build_from, + build_to=build_to, + manifest_to=prefix, + dockerfile=dockerfile + ) + + +def main(): + args = parser.parse_args() + + def run_command(*cmd, ignore_error: bool = False): + print(f"$ {shlex.join(list(cmd))}") + if not args.dry_run: + rc = subprocess.call(list(cmd)) + if rc != 0 and not ignore_error: + print("Command failed") + sys.exit(1) + + # detect channel from tag + match = re.match(r'^\d+\.\d+(?:\.\d+)?(b\d+)?$', args.tag) + if match is None: + channel = CHANNEL_DEV + elif match.group(1) is None: + channel = CHANNEL_RELEASE + else: + channel = CHANNEL_BETA + + tags_to_push = [args.tag] + if channel == CHANNEL_DEV: + tags_to_push.append("dev") + elif channel == CHANNEL_BETA: + tags_to_push.append("beta") + elif channel == CHANNEL_RELEASE: + # Additionally push to beta + tags_to_push.append("beta") + tags_to_push.append("latest") + + if args.command == "build": + # 1. pull cache image + params = DockerParams.for_type_arch(args.build_type, args.arch) + cache_tag = { + CHANNEL_DEV: "dev", + CHANNEL_BETA: "beta", + CHANNEL_RELEASE: "latest", + }[channel] + cache_img = f"ghcr.io/{params.build_to}:{cache_tag}" + run_command("docker", "pull", cache_img, ignore_error=True) + + # 2. register QEMU binfmt (if not host arch) + is_native = UNAME_TO_ARCH.get(platform.machine()) == args.arch + if not is_native: + run_command( + "docker", "run", "--rm", "--privileged", "multiarch/qemu-user-static:5.2.0-2", + "--reset", "-p", "yes" + ) + + # 3. build + run_command( + "docker", "build", + "--build-arg", f"BUILD_FROM={params.build_from}", + "--build-arg", f"BUILD_VERSION={args.tag}", + "--tag", f"{params.build_to}:{args.tag}", + "--cache-from", cache_img, + "--file", params.dockerfile, + "." + ) + elif args.command == "push": + params = DockerParams.for_type_arch(args.build_type, args.arch) + imgs = [f"{params.build_to}:{tag}" for tag in tags_to_push] + imgs += [f"ghcr.io/{params.build_to}:{tag}" for tag in tags_to_push] + src = imgs[0] + # 1. tag images + for img in imgs[1:]: + run_command( + "docker", "tag", src, img + ) + # 2. push images + for img in imgs: + run_command( + "docker", "push", img + ) + elif args.command == "manifest": + manifest = DockerParams.for_type_arch(args.build_type, ARCH_AMD64).manifest_to + + targets = [f"{manifest}:{tag}" for tag in tags_to_push] + targets += [f"ghcr.io/{manifest}:{tag}" for tag in tags_to_push] + # 1. Create manifests + for target in targets: + cmd = ["docker", "manifest", "create", target] + for arch in ARCHS: + src = f"{DockerParams.for_type_arch(args.build_type, arch).build_to}:{args.tag}" + if target.startswith("ghcr.io"): + src = f"ghcr.io/{src}" + cmd.append(src) + run_command(*cmd) + # 2. Push manifests + for target in targets: + run_command( + "docker", "manifest", "push", target + ) + + +if __name__ == "__main__": + main() diff --git a/esphome/__main__.py b/esphome/__main__.py index 232652db9f..a7a3836b69 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -516,7 +516,7 @@ def parse_args(argv): deprecated_argv_suggestion = None - if ["dashboard", "config"] == argv[1:3]: + if ["dashboard", "config"] == argv[1:3] or ["version"] == argv[1:2]: # this is most likely meant in new-style arg format. do not try compat parsing pass else: diff --git a/esphome/codegen.py b/esphome/codegen.py index 8361faeb81..c05cc5efca 100644 --- a/esphome/codegen.py +++ b/esphome/codegen.py @@ -60,6 +60,7 @@ from esphome.cpp_types import ( # noqa uint8, uint16, uint32, + uint64, int32, const_char_ptr, NAN, diff --git a/esphome/components/a4988/a4988.cpp b/esphome/components/a4988/a4988.cpp index 4100563412..429fa25648 100644 --- a/esphome/components/a4988/a4988.cpp +++ b/esphome/components/a4988/a4988.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace a4988 { -static const char *TAG = "a4988.stepper"; +static const char *const TAG = "a4988.stepper"; void A4988::setup() { ESP_LOGCONFIG(TAG, "Setting up A4988..."); diff --git a/esphome/components/ac_dimmer/ac_dimmer.cpp b/esphome/components/ac_dimmer/ac_dimmer.cpp index a60cc9e29a..ad6018268e 100644 --- a/esphome/components/ac_dimmer/ac_dimmer.cpp +++ b/esphome/components/ac_dimmer/ac_dimmer.cpp @@ -9,15 +9,15 @@ namespace esphome { namespace ac_dimmer { -static const char *TAG = "ac_dimmer"; +static const char *const TAG = "ac_dimmer"; // Global array to store dimmer objects -static AcDimmerDataStore *all_dimmers[32]; +static AcDimmerDataStore *all_dimmers[32]; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) /// Time in microseconds the gate should be held high /// 10µs should be long enough for most triacs /// For reference: BT136 datasheet says 2µs nominal (page 7) -static uint32_t GATE_ENABLE_TIME = 10; +static const uint32_t GATE_ENABLE_TIME = 10; /// Function called from timer interrupt /// Input is current time in microseconds (micros()) diff --git a/esphome/components/adalight/adalight_light_effect.cpp b/esphome/components/adalight/adalight_light_effect.cpp index d162c90721..63fd7c60cc 100644 --- a/esphome/components/adalight/adalight_light_effect.cpp +++ b/esphome/components/adalight/adalight_light_effect.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace adalight { -static const char *TAG = "adalight_light_effect"; +static const char *const TAG = "adalight_light_effect"; static const uint32_t ADALIGHT_ACK_INTERVAL = 1000; static const uint32_t ADALIGHT_RECEIVE_TIMEOUT = 1000; diff --git a/esphome/components/adc/adc_sensor.cpp b/esphome/components/adc/adc_sensor.cpp index c6e2141725..d6469ab785 100644 --- a/esphome/components/adc/adc_sensor.cpp +++ b/esphome/components/adc/adc_sensor.cpp @@ -8,10 +8,33 @@ ADC_MODE(ADC_VCC) namespace esphome { namespace adc { -static const char *TAG = "adc"; +static const char *const TAG = "adc"; #ifdef ARDUINO_ARCH_ESP32 -void ADCSensor::set_attenuation(adc_attenuation_t attenuation) { this->attenuation_ = attenuation; } +void ADCSensor::set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; } + +inline adc1_channel_t gpio_to_adc1(uint8_t pin) { + switch (pin) { + case 36: + return ADC1_CHANNEL_0; + case 37: + return ADC1_CHANNEL_1; + case 38: + return ADC1_CHANNEL_2; + case 39: + return ADC1_CHANNEL_3; + case 32: + return ADC1_CHANNEL_4; + case 33: + return ADC1_CHANNEL_5; + case 34: + return ADC1_CHANNEL_6; + case 35: + return ADC1_CHANNEL_7; + default: + return ADC1_CHANNEL_MAX; + } +} #endif void ADCSensor::setup() { @@ -21,7 +44,9 @@ void ADCSensor::setup() { #endif #ifdef ARDUINO_ARCH_ESP32 - analogSetPinAttenuation(this->pin_, this->attenuation_); + adc1_config_channel_atten(gpio_to_adc1(pin_), attenuation_); + adc1_config_width(ADC_WIDTH_BIT_12); + adc_gpio_init(ADC_UNIT_1, (adc_channel_t) gpio_to_adc1(pin_)); #endif } void ADCSensor::dump_config() { @@ -36,18 +61,20 @@ void ADCSensor::dump_config() { #ifdef ARDUINO_ARCH_ESP32 ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_); switch (this->attenuation_) { - case ADC_0db: + case ADC_ATTEN_DB_0: ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)"); break; - case ADC_2_5db: + case ADC_ATTEN_DB_2_5: ESP_LOGCONFIG(TAG, " Attenuation: 2.5db (max 1.5V)"); break; - case ADC_6db: + case ADC_ATTEN_DB_6: ESP_LOGCONFIG(TAG, " Attenuation: 6db (max 2.2V)"); break; - case ADC_11db: + case ADC_ATTEN_DB_11: ESP_LOGCONFIG(TAG, " Attenuation: 11db (max 3.9V)"); break; + default: // This is to satisfy the unused ADC_ATTEN_MAX + break; } #endif LOG_UPDATE_INTERVAL(this); @@ -60,20 +87,23 @@ void ADCSensor::update() { } float ADCSensor::sample() { #ifdef ARDUINO_ARCH_ESP32 - float value_v = analogRead(this->pin_) / 4095.0f; // NOLINT + int raw = adc1_get_raw(gpio_to_adc1(pin_)); + float value_v = raw / 4095.0f; switch (this->attenuation_) { - case ADC_0db: + case ADC_ATTEN_DB_0: value_v *= 1.1; break; - case ADC_2_5db: + case ADC_ATTEN_DB_2_5: value_v *= 1.5; break; - case ADC_6db: + case ADC_ATTEN_DB_6: value_v *= 2.2; break; - case ADC_11db: + case ADC_ATTEN_DB_11: value_v *= 3.9; break; + default: // This is to satisfy the unused ADC_ATTEN_MAX + break; } return value_v; #endif diff --git a/esphome/components/adc/adc_sensor.h b/esphome/components/adc/adc_sensor.h index 3a08ff6be4..4591ed758d 100644 --- a/esphome/components/adc/adc_sensor.h +++ b/esphome/components/adc/adc_sensor.h @@ -6,6 +6,10 @@ #include "esphome/components/sensor/sensor.h" #include "esphome/components/voltage_sampler/voltage_sampler.h" +#ifdef ARDUINO_ARCH_ESP32 +#include "driver/adc.h" +#endif + namespace esphome { namespace adc { @@ -13,7 +17,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage public: #ifdef ARDUINO_ARCH_ESP32 /// Set the attenuation for this pin. Only available on the ESP32. - void set_attenuation(adc_attenuation_t attenuation); + void set_attenuation(adc_atten_t attenuation); #endif /// Update adc values. @@ -34,7 +38,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage uint8_t pin_; #ifdef ARDUINO_ARCH_ESP32 - adc_attenuation_t attenuation_{ADC_0db}; + adc_atten_t attenuation_{ADC_ATTEN_DB_0}; #endif }; diff --git a/esphome/components/adc/sensor.py b/esphome/components/adc/sensor.py index 90561679b7..7a944a7260 100644 --- a/esphome/components/adc/sensor.py +++ b/esphome/components/adc/sensor.py @@ -16,10 +16,10 @@ from esphome.const import ( AUTO_LOAD = ["voltage_sampler"] ATTENUATION_MODES = { - "0db": cg.global_ns.ADC_0db, - "2.5db": cg.global_ns.ADC_2_5db, - "6db": cg.global_ns.ADC_6db, - "11db": cg.global_ns.ADC_11db, + "0db": cg.global_ns.ADC_ATTEN_DB_0, + "2.5db": cg.global_ns.ADC_ATTEN_DB_2_5, + "6db": cg.global_ns.ADC_ATTEN_DB_6, + "11db": cg.global_ns.ADC_ATTEN_DB_11, } diff --git a/esphome/components/addressable_light/addressable_light_display.cpp b/esphome/components/addressable_light/addressable_light_display.cpp index 2e94e9e082..16fab15b17 100644 --- a/esphome/components/addressable_light/addressable_light_display.cpp +++ b/esphome/components/addressable_light/addressable_light_display.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace addressable_light { -static const char* TAG = "addressable_light.display"; +static const char *const TAG = "addressable_light.display"; int AddressableLightDisplay::get_width_internal() { return this->width_; } int AddressableLightDisplay::get_height_internal() { return this->height_; } @@ -24,7 +24,7 @@ void AddressableLightDisplay::update() { void AddressableLightDisplay::display() { bool dirty = false; uint8_t old_r, old_g, old_b, old_w; - Color* c; + Color *c; for (uint32_t offset = 0; offset < this->addressable_light_buffer_.size(); offset++) { c = &(this->addressable_light_buffer_[offset]); diff --git a/esphome/components/ade7953/ade7953.cpp b/esphome/components/ade7953/ade7953.cpp index d55f585b26..0e6d5624c1 100644 --- a/esphome/components/ade7953/ade7953.cpp +++ b/esphome/components/ade7953/ade7953.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace ade7953 { -static const char *TAG = "ade7953"; +static const char *const TAG = "ade7953"; void ADE7953::dump_config() { ESP_LOGCONFIG(TAG, "ADE7953:"); @@ -21,8 +21,8 @@ void ADE7953::dump_config() { } #define ADE_PUBLISH_(name, factor) \ - if (name && this->name##_sensor_) { \ - float value = *name / factor; \ + if ((name) && this->name##_sensor_) { \ + float value = *(name) / (factor); \ this->name##_sensor_->publish_state(value); \ } #define ADE_PUBLISH(name, factor) ADE_PUBLISH_(name, factor) diff --git a/esphome/components/ads1115/ads1115.cpp b/esphome/components/ads1115/ads1115.cpp index 0899571a47..92f0ffbe3a 100644 --- a/esphome/components/ads1115/ads1115.cpp +++ b/esphome/components/ads1115/ads1115.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace ads1115 { -static const char *TAG = "ads1115"; +static const char *const TAG = "ads1115"; static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00; static const uint8_t ADS1115_REGISTER_CONFIG = 0x01; @@ -64,11 +64,6 @@ void ADS1115Component::setup() { return; } this->prev_config_ = config; - - for (auto *sensor : this->sensors_) { - this->set_interval(sensor->get_name(), sensor->update_interval(), - [this, sensor] { this->request_measurement(sensor); }); - } } void ADS1115Component::dump_config() { ESP_LOGCONFIG(TAG, "Setting up ADS1115..."); @@ -107,17 +102,22 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) { } this->prev_config_ = config; - // about 1.6 ms with 860 samples per second + // about 1.2 ms with 860 samples per second delay(2); - uint32_t start = millis(); - while (this->read_byte_16(ADS1115_REGISTER_CONFIG, &config) && (config >> 15) == 0) { - if (millis() - start > 100) { - ESP_LOGW(TAG, "Reading ADS1115 timed out"); - this->status_set_warning(); - return NAN; + // in continuous mode, conversion will always be running, rely on the delay + // to ensure conversion is taking place with the correct settings + // can we use the rdy pin to trigger when a conversion is done? + if (!this->continuous_mode_) { + uint32_t start = millis(); + while (this->read_byte_16(ADS1115_REGISTER_CONFIG, &config) && (config >> 15) == 0) { + if (millis() - start > 100) { + ESP_LOGW(TAG, "Reading ADS1115 timed out"); + this->status_set_warning(); + return NAN; + } + yield(); } - yield(); } } diff --git a/esphome/components/aht10/aht10.cpp b/esphome/components/aht10/aht10.cpp index d3a77fee5f..4688440d80 100644 --- a/esphome/components/aht10/aht10.cpp +++ b/esphome/components/aht10/aht10.cpp @@ -18,7 +18,7 @@ namespace esphome { namespace aht10 { -static const char *TAG = "aht10"; +static const char *const TAG = "aht10"; static const uint8_t AHT10_CALIBRATE_CMD[] = {0xE1}; static const uint8_t AHT10_MEASURE_CMD[] = {0xAC, 0x33, 0x00}; static const uint8_t AHT10_DEFAULT_DELAY = 5; // ms, for calibration and temperature measurement diff --git a/esphome/components/am2320/am2320.cpp b/esphome/components/am2320/am2320.cpp index 59cb977fe8..7e8795dd30 100644 --- a/esphome/components/am2320/am2320.cpp +++ b/esphome/components/am2320/am2320.cpp @@ -9,7 +9,7 @@ namespace esphome { namespace am2320 { -static const char *TAG = "am2320"; +static const char *const TAG = "am2320"; // ---=== Calc CRC16 ===--- uint16_t crc_16(uint8_t *ptr, uint8_t length) { diff --git a/esphome/components/xiaomi_miflora/__init__.py b/esphome/components/anova/__init__.py similarity index 100% rename from esphome/components/xiaomi_miflora/__init__.py rename to esphome/components/anova/__init__.py diff --git a/esphome/components/anova/anova.cpp b/esphome/components/anova/anova.cpp new file mode 100644 index 0000000000..c4b08ca6b5 --- /dev/null +++ b/esphome/components/anova/anova.cpp @@ -0,0 +1,140 @@ +#include "anova.h" +#include "esphome/core/log.h" + +#ifdef ARDUINO_ARCH_ESP32 + +namespace esphome { +namespace anova { + +static const char *TAG = "anova"; + +using namespace esphome::climate; + +void Anova::dump_config() { LOG_CLIMATE("", "Anova BLE Cooker", this); } + +void Anova::setup() { + this->codec_ = new AnovaCodec(); + this->current_request_ = 0; +} + +void Anova::loop() {} + +void Anova::control(const ClimateCall &call) { + if (call.get_mode().has_value()) { + ClimateMode mode = *call.get_mode(); + AnovaPacket *pkt; + switch (mode) { + case climate::CLIMATE_MODE_OFF: + pkt = this->codec_->get_stop_request(); + break; + case climate::CLIMATE_MODE_HEAT: + pkt = this->codec_->get_start_request(); + break; + default: + ESP_LOGW(TAG, "Unsupported mode: %d", mode); + return; + } + auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, + pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); + if (status) + ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status); + } + if (call.get_target_temperature().has_value()) { + auto pkt = this->codec_->get_set_target_temp_request(*call.get_target_temperature()); + auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, + pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); + if (status) + ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status); + } +} + +void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { + switch (event) { + case ESP_GATTC_DISCONNECT_EVT: { + this->current_temperature = NAN; + this->target_temperature = NAN; + this->publish_state(); + break; + } + case ESP_GATTC_SEARCH_CMPL_EVT: { + auto chr = this->parent_->get_characteristic(ANOVA_SERVICE_UUID, ANOVA_CHARACTERISTIC_UUID); + if (chr == nullptr) { + ESP_LOGW(TAG, "[%s] No control service found at device, not an Anova..?", this->get_name().c_str()); + break; + } + this->char_handle_ = chr->handle; + + auto status = esp_ble_gattc_register_for_notify(this->parent_->gattc_if, this->parent_->remote_bda, chr->handle); + if (status) { + ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status); + } + break; + } + case ESP_GATTC_REG_FOR_NOTIFY_EVT: { + this->node_state = espbt::ClientState::Established; + this->current_request_ = 0; + this->update(); + break; + } + case ESP_GATTC_NOTIFY_EVT: { + if (param->notify.handle != this->char_handle_) + break; + this->codec_->decode(param->notify.value, param->notify.value_len); + if (this->codec_->has_target_temp()) { + this->target_temperature = this->codec_->target_temp_; + } + if (this->codec_->has_current_temp()) { + this->current_temperature = this->codec_->current_temp_; + } + if (this->codec_->has_running()) { + this->mode = this->codec_->running_ ? climate::CLIMATE_MODE_HEAT : climate::CLIMATE_MODE_OFF; + } + this->publish_state(); + + if (this->current_request_ > 0) { + AnovaPacket *pkt = nullptr; + switch (this->current_request_++) { + case 1: + pkt = this->codec_->get_read_target_temp_request(); + break; + case 2: + pkt = this->codec_->get_read_current_temp_request(); + break; + default: + this->current_request_ = 0; + break; + } + if (pkt != nullptr) { + auto status = + esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, pkt->length, + pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); + if (status) + ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), + status); + } + } + break; + } + default: + break; + } +} + +void Anova::update() { + if (this->node_state != espbt::ClientState::Established) + return; + + if (this->current_request_ == 0) { + auto pkt = this->codec_->get_read_device_status_request(); + auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, + pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); + if (status) + ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status); + this->current_request_++; + } +} + +} // namespace anova +} // namespace esphome + +#endif diff --git a/esphome/components/anova/anova.h b/esphome/components/anova/anova.h new file mode 100644 index 0000000000..63d03cb329 --- /dev/null +++ b/esphome/components/anova/anova.h @@ -0,0 +1,50 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/ble_client/ble_client.h" +#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" +#include "esphome/components/climate/climate.h" +#include "anova_base.h" + +#ifdef ARDUINO_ARCH_ESP32 + +#include + +namespace esphome { +namespace anova { + +namespace espbt = esphome::esp32_ble_tracker; + +static const uint16_t ANOVA_SERVICE_UUID = 0xFFE0; +static const uint16_t ANOVA_CHARACTERISTIC_UUID = 0xFFE1; + +class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode, public PollingComponent { + public: + void setup() override; + void loop() override; + void update() override; + void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, + esp_ble_gattc_cb_param_t *param) override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::DATA; } + climate::ClimateTraits traits() { + auto traits = climate::ClimateTraits(); + traits.set_supports_current_temperature(true); + traits.set_supports_heat_mode(true); + traits.set_visual_min_temperature(25.0); + traits.set_visual_max_temperature(100.0); + traits.set_visual_temperature_step(0.1); + return traits; + } + + protected: + AnovaCodec *codec_; + void control(const climate::ClimateCall &call) override; + uint16_t char_handle_; + uint8_t current_request_; +}; + +} // namespace anova +} // namespace esphome + +#endif diff --git a/esphome/components/anova/anova_base.cpp b/esphome/components/anova/anova_base.cpp new file mode 100644 index 0000000000..8cbc481643 --- /dev/null +++ b/esphome/components/anova/anova_base.cpp @@ -0,0 +1,119 @@ +#include "anova_base.h" + +namespace esphome { +namespace anova { + +AnovaPacket *AnovaCodec::clean_packet_() { + this->packet_.length = strlen((char *) this->packet_.data); + this->packet_.data[this->packet_.length] = '\0'; + ESP_LOGV("anova", "SendPkt: %s\n", this->packet_.data); + return &this->packet_; +} + +AnovaPacket *AnovaCodec::get_read_device_status_request() { + this->current_query_ = READ_DEVICE_STATUS; + sprintf((char *) this->packet_.data, "%s", CMD_READ_DEVICE_STATUS); + return this->clean_packet_(); +} + +AnovaPacket *AnovaCodec::get_read_target_temp_request() { + this->current_query_ = READ_TARGET_TEMPERATURE; + sprintf((char *) this->packet_.data, "%s", CMD_READ_TARGET_TEMP); + return this->clean_packet_(); +} + +AnovaPacket *AnovaCodec::get_read_current_temp_request() { + this->current_query_ = READ_CURRENT_TEMPERATURE; + sprintf((char *) this->packet_.data, "%s", CMD_READ_CURRENT_TEMP); + return this->clean_packet_(); +} + +AnovaPacket *AnovaCodec::get_read_unit_request() { + this->current_query_ = READ_UNIT; + sprintf((char *) this->packet_.data, "%s", CMD_READ_UNIT); + return this->clean_packet_(); +} + +AnovaPacket *AnovaCodec::get_read_data_request() { + this->current_query_ = READ_DATA; + sprintf((char *) this->packet_.data, "%s", CMD_READ_DATA); + return this->clean_packet_(); +} + +AnovaPacket *AnovaCodec::get_set_target_temp_request(float temperature) { + this->current_query_ = SET_TARGET_TEMPERATURE; + sprintf((char *) this->packet_.data, CMD_SET_TARGET_TEMP, temperature); + return this->clean_packet_(); +} + +AnovaPacket *AnovaCodec::get_set_unit_request(char unit) { + this->current_query_ = SET_UNIT; + sprintf((char *) this->packet_.data, CMD_SET_TEMP_UNIT, unit); + return this->clean_packet_(); +} + +AnovaPacket *AnovaCodec::get_start_request() { + this->current_query_ = START; + sprintf((char *) this->packet_.data, CMD_START); + return this->clean_packet_(); +} + +AnovaPacket *AnovaCodec::get_stop_request() { + this->current_query_ = STOP; + sprintf((char *) this->packet_.data, CMD_STOP); + return this->clean_packet_(); +} + +void AnovaCodec::decode(const uint8_t *data, uint16_t length) { + memset(this->buf_, 0, 32); + strncpy(this->buf_, (char *) data, length); + ESP_LOGV("anova", "Received: %s\n", this->buf_); + this->has_target_temp_ = this->has_current_temp_ = this->has_unit_ = this->has_running_ = false; + switch (this->current_query_) { + case READ_DEVICE_STATUS: { + if (!strncmp(this->buf_, "stopped", 7)) { + this->has_running_ = true; + this->running_ = false; + } + if (!strncmp(this->buf_, "running", 7)) { + this->has_running_ = true; + this->running_ = true; + } + break; + } + case START: { + if (!strncmp(this->buf_, "start", 5)) { + this->has_running_ = true; + this->running_ = true; + } + break; + } + case STOP: { + if (!strncmp(this->buf_, "stop", 4)) { + this->has_running_ = true; + this->running_ = false; + } + break; + } + case READ_TARGET_TEMPERATURE: { + this->target_temp_ = strtof(this->buf_, nullptr); + this->has_target_temp_ = true; + break; + } + case SET_TARGET_TEMPERATURE: { + this->target_temp_ = strtof(this->buf_, nullptr); + this->has_target_temp_ = true; + break; + } + case READ_CURRENT_TEMPERATURE: { + this->current_temp_ = strtof(this->buf_, nullptr); + this->has_current_temp_ = true; + break; + } + default: + break; + } +} + +} // namespace anova +} // namespace esphome diff --git a/esphome/components/anova/anova_base.h b/esphome/components/anova/anova_base.h new file mode 100644 index 0000000000..e94fe619a6 --- /dev/null +++ b/esphome/components/anova/anova_base.h @@ -0,0 +1,79 @@ +#pragma once + +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace anova { + +enum CurrentQuery { + NONE, + READ_DEVICE_STATUS, + READ_TARGET_TEMPERATURE, + READ_CURRENT_TEMPERATURE, + READ_DATA, + READ_UNIT, + SET_TARGET_TEMPERATURE, + SET_UNIT, + START, + STOP, +}; + +struct AnovaPacket { + uint16_t length; + uint8_t data[24]; +}; + +#define CMD_READ_DEVICE_STATUS "status\r" +#define CMD_READ_TARGET_TEMP "read set temp\r" +#define CMD_READ_CURRENT_TEMP "read temp\r" +#define CMD_READ_UNIT "read unit\r" +#define CMD_READ_DATA "read data\r" +#define CMD_SET_TARGET_TEMP "set temp %.1f\r" +#define CMD_SET_TEMP_UNIT "set unit %c\r" + +#define CMD_START "start\r" +#define CMD_STOP "stop\r" + +class AnovaCodec { + public: + AnovaPacket *get_read_device_status_request(); + AnovaPacket *get_read_target_temp_request(); + AnovaPacket *get_read_current_temp_request(); + AnovaPacket *get_read_data_request(); + AnovaPacket *get_read_unit_request(); + + AnovaPacket *get_set_target_temp_request(float temperature); + AnovaPacket *get_set_unit_request(char unit); + + AnovaPacket *get_start_request(); + AnovaPacket *get_stop_request(); + + void decode(const uint8_t *data, uint16_t length); + bool has_target_temp() { return this->has_target_temp_; } + bool has_current_temp() { return this->has_current_temp_; } + bool has_unit() { return this->has_unit_; } + bool has_running() { return this->has_running_; } + + union { + float target_temp_; + float current_temp_; + char unit_; + bool running_; + }; + + protected: + AnovaPacket *clean_packet_(); + AnovaPacket packet_; + + bool has_target_temp_; + bool has_current_temp_; + bool has_unit_; + bool has_running_; + char buf_[32]; + + CurrentQuery current_query_; +}; + +} // namespace anova +} // namespace esphome diff --git a/esphome/components/anova/climate.py b/esphome/components/anova/climate.py new file mode 100644 index 0000000000..ab1c9045d8 --- /dev/null +++ b/esphome/components/anova/climate.py @@ -0,0 +1,25 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import climate, ble_client +from esphome.const import CONF_ID + +CODEOWNERS = ["@buxtronix"] +DEPENDENCIES = ["ble_client"] + +anova_ns = cg.esphome_ns.namespace("anova") +Anova = anova_ns.class_( + "Anova", climate.Climate, ble_client.BLEClientNode, cg.PollingComponent +) + +CONFIG_SCHEMA = ( + climate.CLIMATE_SCHEMA.extend({cv.GenerateID(): cv.declare_id(Anova)}) + .extend(ble_client.BLE_CLIENT_SCHEMA) + .extend(cv.polling_component_schema("60s")) +) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield climate.register_climate(var, config) + yield ble_client.register_ble_node(var, config) diff --git a/esphome/components/apds9960/apds9960.cpp b/esphome/components/apds9960/apds9960.cpp index 2e09d11182..15b0cb39f8 100644 --- a/esphome/components/apds9960/apds9960.cpp +++ b/esphome/components/apds9960/apds9960.cpp @@ -4,10 +4,10 @@ namespace esphome { namespace apds9960 { -static const char *TAG = "apds9960"; +static const char *const TAG = "apds9960"; #define APDS9960_ERROR_CHECK(func) \ - if (!func) { \ + if (!(func)) { \ this->mark_failed(); \ return; \ } diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index a5bd9aec6d..073775ed2e 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -38,6 +38,7 @@ service APIConnection { rpc switch_command (SwitchCommandRequest) returns (void) {} rpc camera_image (CameraImageRequest) returns (void) {} rpc climate_command (ClimateCommandRequest) returns (void) {} + rpc number_command (NumberCommandRequest) returns (void) {} } @@ -378,6 +379,7 @@ message LightStateResponse { fixed32 key = 1; bool state = 2; float brightness = 3; + float color_brightness = 10; float red = 4; float green = 5; float blue = 6; @@ -396,6 +398,8 @@ message LightCommandRequest { bool state = 3; bool has_brightness = 4; float brightness = 5; + bool has_color_brightness = 20; + float color_brightness = 21; bool has_rgb = 6; float red = 7; float green = 8; @@ -418,6 +422,12 @@ enum SensorStateClass { STATE_CLASS_MEASUREMENT = 1; } +enum SensorLastResetType { + LAST_RESET_NONE = 0; + LAST_RESET_NEVER = 1; + LAST_RESET_AUTO = 2; +} + message ListEntitiesSensorResponse { option (id) = 16; option (source) = SOURCE_SERVER; @@ -434,6 +444,7 @@ message ListEntitiesSensorResponse { bool force_update = 8; string device_class = 9; SensorStateClass state_class = 10; + SensorLastResetType last_reset_type = 11; } message SensorStateResponse { option (id) = 25; @@ -795,3 +806,41 @@ message ClimateCommandRequest { bool has_custom_preset = 20; string custom_preset = 21; } + +// ==================== NUMBER ==================== +message ListEntitiesNumberResponse { + option (id) = 49; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_NUMBER"; + + string object_id = 1; + fixed32 key = 2; + string name = 3; + string unique_id = 4; + + string icon = 5; + float min_value = 6; + float max_value = 7; + float step = 8; +} +message NumberStateResponse { + option (id) = 50; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_NUMBER"; + option (no_delay) = true; + + fixed32 key = 1; + float state = 2; + // If the number does not have a valid state yet. + // Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller + bool missing_state = 3; +} +message NumberCommandRequest { + option (id) = 51; + option (source) = SOURCE_CLIENT; + option (ifdef) = "USE_NUMBER"; + option (no_delay) = true; + + fixed32 key = 1; + float state = 2; +} diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index a04dc0630b..fb05772e5e 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -16,7 +16,7 @@ namespace esphome { namespace api { -static const char *TAG = "api.connection"; +static const char *const TAG = "api.connection"; APIConnection::APIConnection(AsyncClient *client, APIServer *parent) : client_(client), parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) { @@ -308,6 +308,7 @@ bool APIConnection::send_light_state(light::LightState *light) { if (traits.get_supports_brightness()) resp.brightness = values.get_brightness(); if (traits.get_supports_rgb()) { + resp.color_brightness = values.get_color_brightness(); resp.red = values.get_red(); resp.green = values.get_green(); resp.blue = values.get_blue(); @@ -352,6 +353,8 @@ void APIConnection::light_command(const LightCommandRequest &msg) { call.set_state(msg.state); if (msg.has_brightness) call.set_brightness(msg.brightness); + if (msg.has_color_brightness) + call.set_color_brightness(msg.color_brightness); if (msg.has_rgb) { call.set_red(msg.red); call.set_green(msg.green); @@ -396,6 +399,7 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) { msg.force_update = sensor->get_force_update(); msg.device_class = sensor->get_device_class(); msg.state_class = static_cast(sensor->state_class); + msg.last_reset_type = static_cast(sensor->last_reset_type); return this->send_list_entities_sensor_response(msg); } @@ -550,6 +554,42 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) { } #endif +#ifdef USE_NUMBER +bool APIConnection::send_number_state(number::Number *number, float state) { + if (!this->state_subscription_) + return false; + + NumberStateResponse resp{}; + resp.key = number->get_object_id_hash(); + resp.state = state; + resp.missing_state = !number->has_state(); + return this->send_number_state_response(resp); +} +bool APIConnection::send_number_info(number::Number *number) { + ListEntitiesNumberResponse msg; + msg.key = number->get_object_id_hash(); + msg.object_id = number->get_object_id(); + msg.name = number->get_name(); + msg.unique_id = get_default_unique_id("number", number); + msg.icon = number->traits.get_icon(); + + msg.min_value = number->traits.get_min_value(); + msg.max_value = number->traits.get_max_value(); + msg.step = number->traits.get_step(); + + return this->send_list_entities_number_response(msg); +} +void APIConnection::number_command(const NumberCommandRequest &msg) { + number::Number *number = App.get_number_by_key(msg.key); + if (number == nullptr) + return; + + auto call = number->make_call(); + call.set_value(msg.state); + call.perform(); +} +#endif + #ifdef USE_ESP32_CAMERA void APIConnection::send_camera_state(std::shared_ptr image) { if (!this->state_subscription_) diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 3e91ead52c..1d7fc48563 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -62,6 +62,11 @@ class APIConnection : public APIServerConnection { bool send_climate_state(climate::Climate *climate); bool send_climate_info(climate::Climate *climate); void climate_command(const ClimateCommandRequest &msg) override; +#endif +#ifdef USE_NUMBER + bool send_number_state(number::Number *number, float state); + bool send_number_info(number::Number *number); + void number_command(const NumberCommandRequest &msg) override; #endif bool send_log_message(int level, const char *tag, const char *line); void send_homeassistant_service_call(const HomeassistantServiceResponse &call) { diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index e53ac2019a..057d71324f 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -72,6 +72,18 @@ template<> const char *proto_enum_to_string(enums::Sens return "UNKNOWN"; } } +template<> const char *proto_enum_to_string(enums::SensorLastResetType value) { + switch (value) { + case enums::LAST_RESET_NONE: + return "LAST_RESET_NONE"; + case enums::LAST_RESET_NEVER: + return "LAST_RESET_NEVER"; + case enums::LAST_RESET_AUTO: + return "LAST_RESET_AUTO"; + default: + return "UNKNOWN"; + } +} template<> const char *proto_enum_to_string(enums::LogLevel value) { switch (value) { case enums::LOG_LEVEL_NONE: @@ -1263,6 +1275,10 @@ bool LightStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { this->brightness = value.as_float(); return true; } + case 10: { + this->color_brightness = value.as_float(); + return true; + } case 4: { this->red = value.as_float(); return true; @@ -1291,6 +1307,7 @@ void LightStateResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->key); buffer.encode_bool(2, this->state); buffer.encode_float(3, this->brightness); + buffer.encode_float(10, this->color_brightness); buffer.encode_float(4, this->red); buffer.encode_float(5, this->green); buffer.encode_float(6, this->blue); @@ -1315,6 +1332,11 @@ void LightStateResponse::dump_to(std::string &out) const { out.append(buffer); out.append("\n"); + out.append(" color_brightness: "); + sprintf(buffer, "%g", this->color_brightness); + out.append(buffer); + out.append("\n"); + out.append(" red: "); sprintf(buffer, "%g", this->red); out.append(buffer); @@ -1359,6 +1381,10 @@ bool LightCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { this->has_brightness = value.as_bool(); return true; } + case 20: { + this->has_color_brightness = value.as_bool(); + return true; + } case 6: { this->has_rgb = value.as_bool(); return true; @@ -1415,6 +1441,10 @@ bool LightCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { this->brightness = value.as_float(); return true; } + case 21: { + this->color_brightness = value.as_float(); + return true; + } case 7: { this->red = value.as_float(); return true; @@ -1445,6 +1475,8 @@ void LightCommandRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(3, this->state); buffer.encode_bool(4, this->has_brightness); buffer.encode_float(5, this->brightness); + buffer.encode_bool(20, this->has_color_brightness); + buffer.encode_float(21, this->color_brightness); buffer.encode_bool(6, this->has_rgb); buffer.encode_float(7, this->red); buffer.encode_float(8, this->green); @@ -1485,6 +1517,15 @@ void LightCommandRequest::dump_to(std::string &out) const { out.append(buffer); out.append("\n"); + out.append(" has_color_brightness: "); + out.append(YESNO(this->has_color_brightness)); + out.append("\n"); + + out.append(" color_brightness: "); + sprintf(buffer, "%g", this->color_brightness); + out.append(buffer); + out.append("\n"); + out.append(" has_rgb: "); out.append(YESNO(this->has_rgb)); out.append("\n"); @@ -1563,6 +1604,10 @@ bool ListEntitiesSensorResponse::decode_varint(uint32_t field_id, ProtoVarInt va this->state_class = value.as_enum(); return true; } + case 11: { + this->last_reset_type = value.as_enum(); + return true; + } default: return false; } @@ -1618,6 +1663,7 @@ void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(8, this->force_update); buffer.encode_string(9, this->device_class); buffer.encode_enum(10, this->state_class); + buffer.encode_enum(11, this->last_reset_type); } void ListEntitiesSensorResponse::dump_to(std::string &out) const { char buffer[64]; @@ -1663,6 +1709,10 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const { out.append(" state_class: "); out.append(proto_enum_to_string(this->state_class)); out.append("\n"); + + out.append(" last_reset_type: "); + out.append(proto_enum_to_string(this->last_reset_type)); + out.append("\n"); out.append("}"); } bool SensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { @@ -3227,6 +3277,179 @@ void ClimateCommandRequest::dump_to(std::string &out) const { out.append("\n"); out.append("}"); } +bool ListEntitiesNumberResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) { + switch (field_id) { + case 1: { + this->object_id = value.as_string(); + return true; + } + case 3: { + this->name = value.as_string(); + return true; + } + case 4: { + this->unique_id = value.as_string(); + return true; + } + case 5: { + this->icon = value.as_string(); + return true; + } + default: + return false; + } +} +bool ListEntitiesNumberResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 2: { + this->key = value.as_fixed32(); + return true; + } + case 6: { + this->min_value = value.as_float(); + return true; + } + case 7: { + this->max_value = value.as_float(); + return true; + } + case 8: { + this->step = value.as_float(); + return true; + } + default: + return false; + } +} +void ListEntitiesNumberResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_string(1, this->object_id); + buffer.encode_fixed32(2, this->key); + buffer.encode_string(3, this->name); + buffer.encode_string(4, this->unique_id); + buffer.encode_string(5, this->icon); + buffer.encode_float(6, this->min_value); + buffer.encode_float(7, this->max_value); + buffer.encode_float(8, this->step); +} +void ListEntitiesNumberResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("ListEntitiesNumberResponse {\n"); + out.append(" object_id: "); + out.append("'").append(this->object_id).append("'"); + out.append("\n"); + + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" name: "); + out.append("'").append(this->name).append("'"); + out.append("\n"); + + out.append(" unique_id: "); + out.append("'").append(this->unique_id).append("'"); + out.append("\n"); + + out.append(" icon: "); + out.append("'").append(this->icon).append("'"); + out.append("\n"); + + out.append(" min_value: "); + sprintf(buffer, "%g", this->min_value); + out.append(buffer); + out.append("\n"); + + out.append(" max_value: "); + sprintf(buffer, "%g", this->max_value); + out.append(buffer); + out.append("\n"); + + out.append(" step: "); + sprintf(buffer, "%g", this->step); + out.append(buffer); + out.append("\n"); + out.append("}"); +} +bool NumberStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 3: { + this->missing_state = value.as_bool(); + return true; + } + default: + return false; + } +} +bool NumberStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 1: { + this->key = value.as_fixed32(); + return true; + } + case 2: { + this->state = value.as_float(); + return true; + } + default: + return false; + } +} +void NumberStateResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_fixed32(1, this->key); + buffer.encode_float(2, this->state); + buffer.encode_bool(3, this->missing_state); +} +void NumberStateResponse::dump_to(std::string &out) const { + char buffer[64]; + out.append("NumberStateResponse {\n"); + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" state: "); + sprintf(buffer, "%g", this->state); + out.append(buffer); + out.append("\n"); + + out.append(" missing_state: "); + out.append(YESNO(this->missing_state)); + out.append("\n"); + out.append("}"); +} +bool NumberCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) { + switch (field_id) { + case 1: { + this->key = value.as_fixed32(); + return true; + } + case 2: { + this->state = value.as_float(); + return true; + } + default: + return false; + } +} +void NumberCommandRequest::encode(ProtoWriteBuffer buffer) const { + buffer.encode_fixed32(1, this->key); + buffer.encode_float(2, this->state); +} +void NumberCommandRequest::dump_to(std::string &out) const { + char buffer[64]; + out.append("NumberCommandRequest {\n"); + out.append(" key: "); + sprintf(buffer, "%u", this->key); + out.append(buffer); + out.append("\n"); + + out.append(" state: "); + sprintf(buffer, "%g", this->state); + out.append(buffer); + out.append("\n"); + out.append("}"); +} } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index 956cecdeb9..0551508b4b 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -36,6 +36,11 @@ enum SensorStateClass : uint32_t { STATE_CLASS_NONE = 0, STATE_CLASS_MEASUREMENT = 1, }; +enum SensorLastResetType : uint32_t { + LAST_RESET_NONE = 0, + LAST_RESET_NEVER = 1, + LAST_RESET_AUTO = 2, +}; enum LogLevel : uint32_t { LOG_LEVEL_NONE = 0, LOG_LEVEL_ERROR = 1, @@ -371,6 +376,7 @@ class LightStateResponse : public ProtoMessage { uint32_t key{0}; bool state{false}; float brightness{0.0f}; + float color_brightness{0.0f}; float red{0.0f}; float green{0.0f}; float blue{0.0f}; @@ -392,6 +398,8 @@ class LightCommandRequest : public ProtoMessage { bool state{false}; bool has_brightness{false}; float brightness{0.0f}; + bool has_color_brightness{false}; + float color_brightness{0.0f}; bool has_rgb{false}; float red{0.0f}; float green{0.0f}; @@ -426,6 +434,7 @@ class ListEntitiesSensorResponse : public ProtoMessage { bool force_update{false}; std::string device_class{}; enums::SensorStateClass state_class{}; + enums::SensorLastResetType last_reset_type{}; void encode(ProtoWriteBuffer buffer) const override; void dump_to(std::string &out) const override; @@ -779,6 +788,45 @@ class ClimateCommandRequest : public ProtoMessage { bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; +class ListEntitiesNumberResponse : public ProtoMessage { + public: + std::string object_id{}; + uint32_t key{0}; + std::string name{}; + std::string unique_id{}; + std::string icon{}; + float min_value{0.0f}; + float max_value{0.0f}; + float step{0.0f}; + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override; +}; +class NumberStateResponse : public ProtoMessage { + public: + uint32_t key{0}; + float state{0.0f}; + bool missing_state{false}; + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; +class NumberCommandRequest : public ProtoMessage { + public: + uint32_t key{0}; + float state{0.0f}; + void encode(ProtoWriteBuffer buffer) const override; + void dump_to(std::string &out) const override; + + protected: + bool decode_32bit(uint32_t field_id, Proto32Bit value) override; +}; } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index 06345296a7..440a5d0ab3 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace api { -static const char *TAG = "api.service"; +static const char *const TAG = "api.service"; bool APIServerConnectionBase::send_hello_response(const HelloResponse &msg) { ESP_LOGVV(TAG, "send_hello_response: %s", msg.dump().c_str()); @@ -184,6 +184,20 @@ bool APIServerConnectionBase::send_climate_state_response(const ClimateStateResp #endif #ifdef USE_CLIMATE #endif +#ifdef USE_NUMBER +bool APIServerConnectionBase::send_list_entities_number_response(const ListEntitiesNumberResponse &msg) { + ESP_LOGVV(TAG, "send_list_entities_number_response: %s", msg.dump().c_str()); + return this->send_message_(msg, 49); +} +#endif +#ifdef USE_NUMBER +bool APIServerConnectionBase::send_number_state_response(const NumberStateResponse &msg) { + ESP_LOGVV(TAG, "send_number_state_response: %s", msg.dump().c_str()); + return this->send_message_(msg, 50); +} +#endif +#ifdef USE_NUMBER +#endif bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) { switch (msg_type) { case 1: { @@ -349,6 +363,15 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, msg.decode(msg_data, msg_size); ESP_LOGVV(TAG, "on_climate_command_request: %s", msg.dump().c_str()); this->on_climate_command_request(msg); +#endif + break; + } + case 51: { +#ifdef USE_NUMBER + NumberCommandRequest msg; + msg.decode(msg_data, msg_size); + ESP_LOGVV(TAG, "on_number_command_request: %s", msg.dump().c_str()); + this->on_number_command_request(msg); #endif break; } @@ -547,6 +570,19 @@ void APIServerConnection::on_climate_command_request(const ClimateCommandRequest this->climate_command(msg); } #endif +#ifdef USE_NUMBER +void APIServerConnection::on_number_command_request(const NumberCommandRequest &msg) { + if (!this->is_connection_setup()) { + this->on_no_setup_connection(); + return; + } + if (!this->is_authenticated()) { + this->on_unauthenticated_access(); + return; + } + this->number_command(msg); +} +#endif } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index afbe39e314..398c10a811 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -111,6 +111,15 @@ class APIServerConnectionBase : public ProtoService { #endif #ifdef USE_CLIMATE virtual void on_climate_command_request(const ClimateCommandRequest &value){}; +#endif +#ifdef USE_NUMBER + bool send_list_entities_number_response(const ListEntitiesNumberResponse &msg); +#endif +#ifdef USE_NUMBER + bool send_number_state_response(const NumberStateResponse &msg); +#endif +#ifdef USE_NUMBER + virtual void on_number_command_request(const NumberCommandRequest &value){}; #endif protected: bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override; @@ -147,6 +156,9 @@ class APIServerConnection : public APIServerConnectionBase { #endif #ifdef USE_CLIMATE virtual void climate_command(const ClimateCommandRequest &msg) = 0; +#endif +#ifdef USE_NUMBER + virtual void number_command(const NumberCommandRequest &msg) = 0; #endif protected: void on_hello_request(const HelloRequest &msg) override; @@ -179,6 +191,9 @@ class APIServerConnection : public APIServerConnectionBase { #ifdef USE_CLIMATE void on_climate_command_request(const ClimateCommandRequest &msg) override; #endif +#ifdef USE_NUMBER + void on_number_command_request(const NumberCommandRequest &msg) override; +#endif }; } // namespace api diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index dd77f2004f..7434030565 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -15,7 +15,7 @@ namespace esphome { namespace api { -static const char *TAG = "api"; +static const char *const TAG = "api"; // APIServer void APIServer::setup() { @@ -180,7 +180,7 @@ void APIServer::on_switch_update(switch_::Switch *obj, bool state) { #endif #ifdef USE_TEXT_SENSOR -void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, std::string state) { +void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) { if (obj->is_internal()) return; for (auto *c : this->clients_) @@ -197,9 +197,18 @@ void APIServer::on_climate_update(climate::Climate *obj) { } #endif +#ifdef USE_NUMBER +void APIServer::on_number_update(number::Number *obj, float state) { + if (obj->is_internal()) + return; + for (auto *c : this->clients_) + c->send_number_state(obj, state); +} +#endif + float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; } void APIServer::set_port(uint16_t port) { this->port_ = port; } -APIServer *global_api_server = nullptr; +APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) void APIServer::set_password(const std::string &password) { this->password_ = password; } void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) { diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index 538a385efb..add22e121e 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -56,10 +56,13 @@ class APIServer : public Component, public Controller { void on_switch_update(switch_::Switch *obj, bool state) override; #endif #ifdef USE_TEXT_SENSOR - void on_text_sensor_update(text_sensor::TextSensor *obj, std::string state) override; + void on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) override; #endif #ifdef USE_CLIMATE void on_climate_update(climate::Climate *obj) override; +#endif +#ifdef USE_NUMBER + void on_number_update(number::Number *obj, float state) override; #endif void send_homeassistant_service_call(const HomeassistantServiceResponse &call); void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); } @@ -91,7 +94,7 @@ class APIServer : public Component, public Controller { std::vector user_services_; }; -extern APIServer *global_api_server; +extern APIServer *global_api_server; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) template class APIConnectedCondition : public Condition { public: diff --git a/esphome/components/api/list_entities.cpp b/esphome/components/api/list_entities.cpp index d4245136ae..8897758073 100644 --- a/esphome/components/api/list_entities.cpp +++ b/esphome/components/api/list_entities.cpp @@ -51,5 +51,9 @@ bool ListEntitiesIterator::on_camera(esp32_camera::ESP32Camera *camera) { bool ListEntitiesIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_info(climate); } #endif +#ifdef USE_NUMBER +bool ListEntitiesIterator::on_number(number::Number *number) { return this->client_->send_number_info(number); } +#endif + } // namespace api } // namespace esphome diff --git a/esphome/components/api/list_entities.h b/esphome/components/api/list_entities.h index 6b10a72fdf..c55ba5089e 100644 --- a/esphome/components/api/list_entities.h +++ b/esphome/components/api/list_entities.h @@ -39,6 +39,9 @@ class ListEntitiesIterator : public ComponentIterator { #endif #ifdef USE_CLIMATE bool on_climate(climate::Climate *climate) override; +#endif +#ifdef USE_NUMBER + bool on_number(number::Number *number) override; #endif bool on_end() override; diff --git a/esphome/components/api/proto.cpp b/esphome/components/api/proto.cpp index 4bd22af769..0d2eb2d279 100644 --- a/esphome/components/api/proto.cpp +++ b/esphome/components/api/proto.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace api { -static const char *TAG = "api.proto"; +static const char *const TAG = "api.proto"; void ProtoMessage::decode(const uint8_t *buffer, size_t length) { uint32_t i = 0; diff --git a/esphome/components/api/subscribe_state.cpp b/esphome/components/api/subscribe_state.cpp index 2612a852d3..25aa7c8b31 100644 --- a/esphome/components/api/subscribe_state.cpp +++ b/esphome/components/api/subscribe_state.cpp @@ -37,6 +37,11 @@ bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) #ifdef USE_CLIMATE bool InitialStateIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_state(climate); } #endif +#ifdef USE_NUMBER +bool InitialStateIterator::on_number(number::Number *number) { + return this->client_->send_number_state(number, number->state); +} +#endif InitialStateIterator::InitialStateIterator(APIServer *server, APIConnection *client) : ComponentIterator(server), client_(client) {} diff --git a/esphome/components/api/subscribe_state.h b/esphome/components/api/subscribe_state.h index 51b9c695e4..f03322ac4a 100644 --- a/esphome/components/api/subscribe_state.h +++ b/esphome/components/api/subscribe_state.h @@ -36,6 +36,9 @@ class InitialStateIterator : public ComponentIterator { #endif #ifdef USE_CLIMATE bool on_climate(climate::Climate *climate) override; +#endif +#ifdef USE_NUMBER + bool on_number(number::Number *number) override; #endif protected: APIConnection *client_; diff --git a/esphome/components/api/user_services.h b/esphome/components/api/user_services.h index 3094ba397c..1f9ffc5914 100644 --- a/esphome/components/api/user_services.h +++ b/esphome/components/api/user_services.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "esphome/core/component.h" #include "esphome/core/automation.h" #include "api_pb2.h" @@ -20,8 +22,8 @@ template enums::ServiceArgType to_service_arg_type(); template class UserServiceBase : public UserServiceDescriptor { public: - UserServiceBase(const std::string &name, const std::array &arg_names) - : name_(name), arg_names_(arg_names) { + UserServiceBase(std::string name, const std::array &arg_names) + : name_(std::move(name)), arg_names_(arg_names) { this->key_ = fnv1_hash(this->name_); } diff --git a/esphome/components/api/util.cpp b/esphome/components/api/util.cpp index f929db5d6a..6e05d49b74 100644 --- a/esphome/components/api/util.cpp +++ b/esphome/components/api/util.cpp @@ -167,6 +167,21 @@ void ComponentIterator::advance() { } } break; +#endif +#ifdef USE_NUMBER + case IteratorState::NUMBER: + if (this->at_ >= App.get_numbers().size()) { + advance_platform = true; + } else { + auto *number = App.get_numbers()[this->at_]; + if (number->is_internal()) { + success = true; + break; + } else { + success = this->on_number(number); + } + } + break; #endif case IteratorState::MAX: if (this->on_end()) { diff --git a/esphome/components/api/util.h b/esphome/components/api/util.h index 5a29a48cbe..f8b248056b 100644 --- a/esphome/components/api/util.h +++ b/esphome/components/api/util.h @@ -47,6 +47,9 @@ class ComponentIterator { #endif #ifdef USE_CLIMATE virtual bool on_climate(climate::Climate *climate) = 0; +#endif +#ifdef USE_NUMBER + virtual bool on_number(number::Number *number) = 0; #endif virtual bool on_end(); @@ -81,6 +84,9 @@ class ComponentIterator { #endif #ifdef USE_CLIMATE CLIMATE, +#endif +#ifdef USE_NUMBER + NUMBER, #endif MAX, } state_{IteratorState::NONE}; diff --git a/esphome/components/as3935/as3935.cpp b/esphome/components/as3935/as3935.cpp index 9446a2fdd6..1cc400bb7b 100644 --- a/esphome/components/as3935/as3935.cpp +++ b/esphome/components/as3935/as3935.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace as3935 { -static const char *TAG = "as3935"; +static const char *const TAG = "as3935"; void AS3935Component::setup() { ESP_LOGCONFIG(TAG, "Setting up AS3935..."); diff --git a/esphome/components/as3935_i2c/as3935_i2c.cpp b/esphome/components/as3935_i2c/as3935_i2c.cpp index a522116815..1a1cd8fe82 100644 --- a/esphome/components/as3935_i2c/as3935_i2c.cpp +++ b/esphome/components/as3935_i2c/as3935_i2c.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace as3935_i2c { -static const char *TAG = "as3935_i2c"; +static const char *const TAG = "as3935_i2c"; void I2CAS3935Component::write_register(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t start_pos) { uint8_t write_reg; diff --git a/esphome/components/as3935_spi/as3935_spi.cpp b/esphome/components/as3935_spi/as3935_spi.cpp index 54b52b2fd0..3a517df56d 100644 --- a/esphome/components/as3935_spi/as3935_spi.cpp +++ b/esphome/components/as3935_spi/as3935_spi.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace as3935_spi { -static const char *TAG = "as3935_spi"; +static const char *const TAG = "as3935_spi"; void SPIAS3935Component::setup() { ESP_LOGI(TAG, "SPIAS3935Component setup started!"); diff --git a/esphome/components/atc_mithermometer/atc_mithermometer.cpp b/esphome/components/atc_mithermometer/atc_mithermometer.cpp index 1a555cfa83..5656cdf430 100644 --- a/esphome/components/atc_mithermometer/atc_mithermometer.cpp +++ b/esphome/components/atc_mithermometer/atc_mithermometer.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace atc_mithermometer { -static const char *TAG = "atc_mithermometer"; +static const char *const TAG = "atc_mithermometer"; void ATCMiThermometer::dump_config() { ESP_LOGCONFIG(TAG, "ATC MiThermometer"); diff --git a/esphome/components/atm90e32/atm90e32.cpp b/esphome/components/atm90e32/atm90e32.cpp index d732212cdd..ebceaa817c 100644 --- a/esphome/components/atm90e32/atm90e32.cpp +++ b/esphome/components/atm90e32/atm90e32.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace atm90e32 { -static const char *TAG = "atm90e32"; +static const char *const TAG = "atm90e32"; void ATM90E32Component::update() { if (this->read16_(ATM90E32_REGISTER_METEREN) != 1) { diff --git a/esphome/components/atm90e32/sensor.py b/esphome/components/atm90e32/sensor.py index 68ec199bff..2c34d76b52 100644 --- a/esphome/components/atm90e32/sensor.py +++ b/esphome/components/atm90e32/sensor.py @@ -21,6 +21,7 @@ from esphome.const import ( ICON_EMPTY, ICON_LIGHTBULB, ICON_CURRENT_AC, + LAST_RESET_TYPE_AUTO, STATE_CLASS_MEASUREMENT, UNIT_HERTZ, UNIT_VOLT, @@ -91,10 +92,20 @@ ATM90E32_PHASE_SCHEMA = cv.Schema( STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_FORWARD_ACTIVE_ENERGY): sensor.sensor_schema( - UNIT_WATT_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY, STATE_CLASS_MEASUREMENT + UNIT_WATT_HOURS, + ICON_EMPTY, + 2, + DEVICE_CLASS_ENERGY, + STATE_CLASS_MEASUREMENT, + LAST_RESET_TYPE_AUTO, ), cv.Optional(CONF_REVERSE_ACTIVE_ENERGY): sensor.sensor_schema( - UNIT_WATT_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY, STATE_CLASS_MEASUREMENT + UNIT_WATT_HOURS, + ICON_EMPTY, + 2, + DEVICE_CLASS_ENERGY, + STATE_CLASS_MEASUREMENT, + LAST_RESET_TYPE_AUTO, ), cv.Optional(CONF_GAIN_VOLTAGE, default=7305): cv.uint16_t, cv.Optional(CONF_GAIN_CT, default=27961): cv.uint16_t, diff --git a/esphome/components/b_parasite/b_parasite.cpp b/esphome/components/b_parasite/b_parasite.cpp index 828e392993..81c9243bdb 100644 --- a/esphome/components/b_parasite/b_parasite.cpp +++ b/esphome/components/b_parasite/b_parasite.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace b_parasite { -static const char* TAG = "b_parasite"; +static const char *const TAG = "b_parasite"; void BParasite::dump_config() { ESP_LOGCONFIG(TAG, "b_parasite"); @@ -16,25 +16,25 @@ void BParasite::dump_config() { LOG_SENSOR(" ", "Soil Moisture", this->soil_moisture_); } -bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice& device) { +bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) { if (device.address_uint64() != address_) { ESP_LOGVV(TAG, "parse_device(): unknown MAC address."); return false; } ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", device.address_str().c_str()); - const auto& service_datas = device.get_service_datas(); + const auto &service_datas = device.get_service_datas(); if (service_datas.size() != 1) { ESP_LOGE(TAG, "Unexpected service_datas size (%d)", service_datas.size()); return false; } - const auto& service_data = service_datas[0]; + const auto &service_data = service_datas[0]; ESP_LOGVV(TAG, "Service data:"); for (const uint8_t byte : service_data.data) { ESP_LOGVV(TAG, "0x%02x", byte); } - const auto& data = service_data.data; + const auto &data = service_data.data; // Counter for deduplicating messages. uint8_t counter = data[1] & 0x0f; diff --git a/esphome/components/xiaomi_mijia/__init__.py b/esphome/components/ballu/__init__.py similarity index 100% rename from esphome/components/xiaomi_mijia/__init__.py rename to esphome/components/ballu/__init__.py diff --git a/esphome/components/ballu/ballu.cpp b/esphome/components/ballu/ballu.cpp new file mode 100644 index 0000000000..e2703a79fb --- /dev/null +++ b/esphome/components/ballu/ballu.cpp @@ -0,0 +1,239 @@ +#include "ballu.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace ballu { + +static const char *const TAG = "ballu.climate"; + +const uint16_t BALLU_HEADER_MARK = 9000; +const uint16_t BALLU_HEADER_SPACE = 4500; +const uint16_t BALLU_BIT_MARK = 575; +const uint16_t BALLU_ONE_SPACE = 1675; +const uint16_t BALLU_ZERO_SPACE = 550; + +const uint32_t BALLU_CARRIER_FREQUENCY = 38000; + +const uint8_t BALLU_STATE_LENGTH = 13; + +const uint8_t BALLU_AUTO = 0; +const uint8_t BALLU_COOL = 0x20; +const uint8_t BALLU_DRY = 0x40; +const uint8_t BALLU_HEAT = 0x80; +const uint8_t BALLU_FAN = 0xc0; + +const uint8_t BALLU_FAN_AUTO = 0xa0; +const uint8_t BALLU_FAN_HIGH = 0x20; +const uint8_t BALLU_FAN_MED = 0x40; +const uint8_t BALLU_FAN_LOW = 0x60; + +const uint8_t BALLU_SWING_VER = 0x07; +const uint8_t BALLU_SWING_HOR = 0xe0; +const uint8_t BALLU_POWER = 0x20; + +void BalluClimate::transmit_state() { + uint8_t remote_state[BALLU_STATE_LENGTH] = {0}; + + auto temp = (uint8_t) roundf(clamp(this->target_temperature, YKR_K_002E_TEMP_MIN, YKR_K_002E_TEMP_MAX)); + auto swing_ver = + ((this->swing_mode == climate::CLIMATE_SWING_VERTICAL) || (this->swing_mode == climate::CLIMATE_SWING_BOTH)); + auto swing_hor = + ((this->swing_mode == climate::CLIMATE_SWING_HORIZONTAL) || (this->swing_mode == climate::CLIMATE_SWING_BOTH)); + + remote_state[0] = 0xc3; + remote_state[1] = ((temp - 8) << 3) | (swing_ver ? 0 : BALLU_SWING_VER); + remote_state[2] = swing_hor ? 0 : BALLU_SWING_HOR; + remote_state[9] = (this->mode == climate::CLIMATE_MODE_OFF) ? 0 : BALLU_POWER; + remote_state[11] = 0x1e; + + // Fan speed + switch (this->fan_mode.value()) { + case climate::CLIMATE_FAN_HIGH: + remote_state[4] |= BALLU_FAN_HIGH; + break; + case climate::CLIMATE_FAN_MEDIUM: + remote_state[4] |= BALLU_FAN_MED; + break; + case climate::CLIMATE_FAN_LOW: + remote_state[4] |= BALLU_FAN_LOW; + break; + case climate::CLIMATE_FAN_AUTO: + remote_state[4] |= BALLU_FAN_AUTO; + break; + default: + break; + } + + // Mode + switch (this->mode) { + case climate::CLIMATE_MODE_AUTO: + remote_state[6] |= BALLU_AUTO; + break; + case climate::CLIMATE_MODE_HEAT: + remote_state[6] |= BALLU_HEAT; + break; + case climate::CLIMATE_MODE_COOL: + remote_state[6] |= BALLU_COOL; + break; + case climate::CLIMATE_MODE_DRY: + remote_state[6] |= BALLU_DRY; + break; + case climate::CLIMATE_MODE_FAN_ONLY: + remote_state[6] |= BALLU_FAN; + break; + case climate::CLIMATE_MODE_OFF: + remote_state[6] |= BALLU_AUTO; + default: + break; + } + + // Checksum + for (uint8_t i = 0; i < BALLU_STATE_LENGTH - 1; i++) + remote_state[12] += remote_state[i]; + + ESP_LOGV(TAG, "Sending: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", remote_state[0], + remote_state[1], remote_state[2], remote_state[3], remote_state[4], remote_state[5], remote_state[6], + remote_state[7], remote_state[8], remote_state[9], remote_state[10], remote_state[11], remote_state[12]); + + // Send code + auto transmit = this->transmitter_->transmit(); + auto data = transmit.get_data(); + + data->set_carrier_frequency(38000); + + // Header + data->mark(BALLU_HEADER_MARK); + data->space(BALLU_HEADER_SPACE); + // Data + for (uint8_t i : remote_state) { + for (uint8_t j = 0; j < 8; j++) { + data->mark(BALLU_BIT_MARK); + bool bit = i & (1 << j); + data->space(bit ? BALLU_ONE_SPACE : BALLU_ZERO_SPACE); + } + } + // Footer + data->mark(BALLU_BIT_MARK); + + transmit.perform(); +} + +bool BalluClimate::on_receive(remote_base::RemoteReceiveData data) { + // Validate header + if (!data.expect_item(BALLU_HEADER_MARK, BALLU_HEADER_SPACE)) { + ESP_LOGV(TAG, "Header fail"); + return false; + } + + uint8_t remote_state[BALLU_STATE_LENGTH] = {0}; + // Read all bytes. + for (int i = 0; i < BALLU_STATE_LENGTH; i++) { + // Read bit + for (int j = 0; j < 8; j++) { + if (data.expect_item(BALLU_BIT_MARK, BALLU_ONE_SPACE)) + remote_state[i] |= 1 << j; + + else if (!data.expect_item(BALLU_BIT_MARK, BALLU_ZERO_SPACE)) { + ESP_LOGV(TAG, "Byte %d bit %d fail", i, j); + return false; + } + } + + ESP_LOGVV(TAG, "Byte %d %02X", i, remote_state[i]); + } + // Validate footer + if (!data.expect_mark(BALLU_BIT_MARK)) { + ESP_LOGV(TAG, "Footer fail"); + return false; + } + + uint8_t checksum = 0; + // Calculate checksum and compare with signal value. + for (uint8_t i = 0; i < BALLU_STATE_LENGTH - 1; i++) + checksum += remote_state[i]; + + if (checksum != remote_state[BALLU_STATE_LENGTH - 1]) { + ESP_LOGVV(TAG, "Checksum fail"); + return false; + } + + ESP_LOGV(TAG, "Received: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", remote_state[0], + remote_state[1], remote_state[2], remote_state[3], remote_state[4], remote_state[5], remote_state[6], + remote_state[7], remote_state[8], remote_state[9], remote_state[10], remote_state[11], remote_state[12]); + + // verify header remote code + if (remote_state[0] != 0xc3) + return false; + + // powr on/off button + ESP_LOGV(TAG, "Power: %02X", (remote_state[9] & BALLU_POWER)); + + if ((remote_state[9] & BALLU_POWER) != BALLU_POWER) { + this->mode = climate::CLIMATE_MODE_OFF; + } else { + auto mode = remote_state[6] & 0xe0; + ESP_LOGV(TAG, "Mode: %02X", mode); + switch (mode) { + case BALLU_HEAT: + this->mode = climate::CLIMATE_MODE_HEAT; + break; + case BALLU_COOL: + this->mode = climate::CLIMATE_MODE_COOL; + break; + case BALLU_DRY: + this->mode = climate::CLIMATE_MODE_DRY; + break; + case BALLU_FAN: + this->mode = climate::CLIMATE_MODE_FAN_ONLY; + break; + case BALLU_AUTO: + this->mode = climate::CLIMATE_MODE_AUTO; + break; + } + } + + // Set received temp + int temp = remote_state[1] & 0xf8; + ESP_LOGVV(TAG, "Temperature Raw: %02X", temp); + temp = ((uint8_t) temp >> 3) + 8; + ESP_LOGVV(TAG, "Temperature Climate: %u", temp); + this->target_temperature = temp; + + // Set received fan speed + auto fan = remote_state[4] & 0xe0; + ESP_LOGVV(TAG, "Fan: %02X", fan); + switch (fan) { + case BALLU_FAN_HIGH: + this->fan_mode = climate::CLIMATE_FAN_HIGH; + break; + case BALLU_FAN_MED: + this->fan_mode = climate::CLIMATE_FAN_MEDIUM; + break; + case BALLU_FAN_LOW: + this->fan_mode = climate::CLIMATE_FAN_LOW; + break; + case BALLU_FAN_AUTO: + default: + this->fan_mode = climate::CLIMATE_FAN_AUTO; + break; + } + + // Set received swing status + ESP_LOGVV(TAG, "Swing status: %02X %02X", remote_state[1] & BALLU_SWING_VER, remote_state[2] & BALLU_SWING_HOR); + if (((remote_state[1] & BALLU_SWING_VER) != BALLU_SWING_VER) && + ((remote_state[2] & BALLU_SWING_HOR) != BALLU_SWING_HOR)) { + this->swing_mode = climate::CLIMATE_SWING_BOTH; + } else if ((remote_state[1] & BALLU_SWING_VER) != BALLU_SWING_VER) { + this->swing_mode = climate::CLIMATE_SWING_VERTICAL; + } else if ((remote_state[2] & BALLU_SWING_HOR) != BALLU_SWING_HOR) { + this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL; + } else { + this->swing_mode = climate::CLIMATE_SWING_OFF; + } + + this->publish_state(); + return true; +} + +} // namespace ballu +} // namespace esphome diff --git a/esphome/components/ballu/ballu.h b/esphome/components/ballu/ballu.h new file mode 100644 index 0000000000..80a4699cfb --- /dev/null +++ b/esphome/components/ballu/ballu.h @@ -0,0 +1,31 @@ +#pragma once + +#include "esphome/components/climate_ir/climate_ir.h" + +namespace esphome { +namespace ballu { + +// Support for Ballu air conditioners with YKR-K/002E remote + +// Temperature +const float YKR_K_002E_TEMP_MIN = 16.0; +const float YKR_K_002E_TEMP_MAX = 32.0; + +class BalluClimate : public climate_ir::ClimateIR { + public: + BalluClimate() + : climate_ir::ClimateIR(YKR_K_002E_TEMP_MIN, YKR_K_002E_TEMP_MAX, 1.0f, true, true, + {climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, + climate::CLIMATE_FAN_HIGH}, + {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL, + climate::CLIMATE_SWING_HORIZONTAL, climate::CLIMATE_SWING_BOTH}) {} + + protected: + /// Transmit via IR the state of this climate controller. + void transmit_state() override; + /// Handle received IR Buffer + bool on_receive(remote_base::RemoteReceiveData data) override; +}; + +} // namespace ballu +} // namespace esphome diff --git a/esphome/components/ballu/climate.py b/esphome/components/ballu/climate.py new file mode 100644 index 0000000000..82e9fead1e --- /dev/null +++ b/esphome/components/ballu/climate.py @@ -0,0 +1,21 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import climate_ir +from esphome.const import CONF_ID + +AUTO_LOAD = ["climate_ir"] +CODEOWNERS = ["@bazuchan"] + +ballu_ns = cg.esphome_ns.namespace("ballu") +BalluClimate = ballu_ns.class_("BalluClimate", climate_ir.ClimateIR) + +CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(BalluClimate), + } +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await climate_ir.register_climate_ir(var, config) diff --git a/esphome/components/bang_bang/bang_bang_climate.cpp b/esphome/components/bang_bang/bang_bang_climate.cpp index c915d69981..4b41684707 100644 --- a/esphome/components/bang_bang/bang_bang_climate.cpp +++ b/esphome/components/bang_bang/bang_bang_climate.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace bang_bang { -static const char *TAG = "bang_bang.climate"; +static const char *const TAG = "bang_bang.climate"; void BangBangClimate::setup() { this->sensor_->add_on_state_callback([this](float state) { @@ -21,7 +21,12 @@ void BangBangClimate::setup() { restore->to_call(this).perform(); } else { // restore from defaults, change_away handles those for us - this->mode = climate::CLIMATE_MODE_HEAT_COOL; + if (supports_cool_ && supports_heat_) + this->mode = climate::CLIMATE_MODE_HEAT_COOL; + else if (supports_cool_) + this->mode = climate::CLIMATE_MODE_COOL; + else if (supports_heat_) + this->mode = climate::CLIMATE_MODE_HEAT; this->change_away_(false); } } @@ -43,12 +48,13 @@ climate::ClimateTraits BangBangClimate::traits() { traits.set_supports_current_temperature(true); traits.set_supported_modes({ climate::CLIMATE_MODE_OFF, - climate::CLIMATE_MODE_HEAT_COOL, }); if (supports_cool_) traits.add_supported_mode(climate::CLIMATE_MODE_COOL); if (supports_heat_) traits.add_supported_mode(climate::CLIMATE_MODE_HEAT); + if (supports_cool_ && supports_heat_) + traits.add_supported_mode(climate::CLIMATE_MODE_HEAT_COOL); traits.set_supports_two_point_target_temperature(true); if (supports_away_) traits.set_supported_presets({ @@ -59,12 +65,8 @@ climate::ClimateTraits BangBangClimate::traits() { return traits; } void BangBangClimate::compute_state_() { - if (this->mode != climate::CLIMATE_MODE_HEAT_COOL) { - // in non-auto mode, switch directly to appropriate action - // - HEAT mode -> HEATING action - // - COOL mode -> COOLING action - // - OFF mode -> OFF action (not IDLE!) - this->switch_to_action_(static_cast(this->mode)); + if (this->mode == climate::CLIMATE_MODE_OFF) { + this->switch_to_action_(climate::CLIMATE_ACTION_OFF); return; } if (isnan(this->current_temperature) || isnan(this->target_temperature_low) || isnan(this->target_temperature_high)) { diff --git a/esphome/components/bh1750/bh1750.cpp b/esphome/components/bh1750/bh1750.cpp index 799440fdfc..43af116c9e 100644 --- a/esphome/components/bh1750/bh1750.cpp +++ b/esphome/components/bh1750/bh1750.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace bh1750 { -static const char *TAG = "bh1750.sensor"; +static const char *const TAG = "bh1750.sensor"; static const uint8_t BH1750_COMMAND_POWER_ON = 0b00000001; static const uint8_t BH1750_COMMAND_MT_REG_HI = 0b01000000; // last 3 bits diff --git a/esphome/components/binary/fan/binary_fan.cpp b/esphome/components/binary/fan/binary_fan.cpp index fa6ddc249f..eaf41829bb 100644 --- a/esphome/components/binary/fan/binary_fan.cpp +++ b/esphome/components/binary/fan/binary_fan.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace binary { -static const char *TAG = "binary.fan"; +static const char *const TAG = "binary.fan"; void binary::BinaryFan::dump_config() { ESP_LOGCONFIG(TAG, "Fan '%s':", this->fan_->get_name().c_str()); diff --git a/esphome/components/binary_sensor/__init__.py b/esphome/components/binary_sensor/__init__.py index 68d4d3e324..8f66978320 100644 --- a/esphome/components/binary_sensor/__init__.py +++ b/esphome/components/binary_sensor/__init__.py @@ -22,7 +22,6 @@ from esphome.const import ( CONF_STATE, CONF_TIMING, CONF_TRIGGER_ID, - CONF_FOR, CONF_NAME, CONF_MQTT_ID, DEVICE_CLASS_EMPTY, @@ -372,11 +371,6 @@ BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend( cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger), } ), - cv.Optional(CONF_INVERTED): cv.invalid( - "The inverted binary_sensor property has been replaced by the " - "new 'invert' binary sensor filter. Please see " - "https://esphome.io/components/binary_sensor/index.html." - ), } ) @@ -455,10 +449,6 @@ async def new_binary_sensor(config): BINARY_SENSOR_CONDITION_SCHEMA = maybe_simple_id( { cv.Required(CONF_ID): cv.use_id(BinarySensor), - cv.Optional(CONF_FOR): cv.invalid( - "This option has been removed in 1.13, please use the " - "'for' condition instead." - ), } ) diff --git a/esphome/components/binary_sensor/automation.cpp b/esphome/components/binary_sensor/automation.cpp index b8be2d1b1f..ce082aafb3 100644 --- a/esphome/components/binary_sensor/automation.cpp +++ b/esphome/components/binary_sensor/automation.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace binary_sensor { -static const char *TAG = "binary_sensor.automation"; +static const char *const TAG = "binary_sensor.automation"; void binary_sensor::MultiClickTrigger::on_state_(bool state) { // Handle duplicate events @@ -80,6 +80,10 @@ void binary_sensor::MultiClickTrigger::schedule_cooldown_() { this->cancel_timeout("is_not_valid"); } void binary_sensor::MultiClickTrigger::schedule_is_valid_(uint32_t min_length) { + if (min_length == 0) { + this->is_valid_ = true; + return; + } this->is_valid_ = false; this->set_timeout("is_valid", min_length, [this]() { ESP_LOGV(TAG, "Multi Click: You can now %s the button.", this->parent_->state ? "RELEASE" : "PRESS"); diff --git a/esphome/components/binary_sensor/automation.h b/esphome/components/binary_sensor/automation.h index 6b0321628c..0c1e80afba 100644 --- a/esphome/components/binary_sensor/automation.h +++ b/esphome/components/binary_sensor/automation.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "esphome/core/component.h" #include "esphome/core/automation.h" #include "esphome/components/binary_sensor/binary_sensor.h" @@ -87,8 +89,8 @@ class DoubleClickTrigger : public Trigger<> { class MultiClickTrigger : public Trigger<>, public Component { public: - explicit MultiClickTrigger(BinarySensor *parent, const std::vector &timing) - : parent_(parent), timing_(timing) {} + explicit MultiClickTrigger(BinarySensor *parent, std::vector timing) + : parent_(parent), timing_(std::move(timing)) {} void setup() override { this->last_state_ = this->parent_->state; diff --git a/esphome/components/binary_sensor/binary_sensor.cpp b/esphome/components/binary_sensor/binary_sensor.cpp index 1cde692dd4..2e1f228be6 100644 --- a/esphome/components/binary_sensor/binary_sensor.cpp +++ b/esphome/components/binary_sensor/binary_sensor.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace binary_sensor { -static const char *TAG = "binary_sensor"; +static const char *const TAG = "binary_sensor"; void BinarySensor::add_on_state_callback(std::function &&callback) { this->state_callback_.add(std::move(callback)); @@ -61,7 +61,7 @@ void BinarySensor::add_filter(Filter *filter) { last_filter->next_ = filter; } } -void BinarySensor::add_filters(std::vector filters) { +void BinarySensor::add_filters(const std::vector &filters) { for (Filter *filter : filters) { this->add_filter(filter); } diff --git a/esphome/components/binary_sensor/binary_sensor.h b/esphome/components/binary_sensor/binary_sensor.h index f91c93c424..62e8031cb5 100644 --- a/esphome/components/binary_sensor/binary_sensor.h +++ b/esphome/components/binary_sensor/binary_sensor.h @@ -9,10 +9,10 @@ namespace esphome { namespace binary_sensor { #define LOG_BINARY_SENSOR(prefix, type, obj) \ - if (obj != nullptr) { \ - ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, obj->get_name().c_str()); \ - if (!obj->get_device_class().empty()) { \ - ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, obj->get_device_class().c_str()); \ + if ((obj) != nullptr) { \ + ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \ + if (!(obj)->get_device_class().empty()) { \ + ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \ } \ } @@ -60,7 +60,7 @@ class BinarySensor : public Nameable { std::string get_device_class(); void add_filter(Filter *filter); - void add_filters(std::vector filters); + void add_filters(const std::vector &filters); // ========== INTERNAL METHODS ========== // (In most use cases you won't need these) diff --git a/esphome/components/binary_sensor/filter.cpp b/esphome/components/binary_sensor/filter.cpp index c6ca3e2f79..53c2daf42d 100644 --- a/esphome/components/binary_sensor/filter.cpp +++ b/esphome/components/binary_sensor/filter.cpp @@ -1,11 +1,13 @@ #include "filter.h" + #include "binary_sensor.h" +#include namespace esphome { namespace binary_sensor { -static const char *TAG = "sensor.filter"; +static const char *const TAG = "sensor.filter"; void Filter::output(bool value, bool is_initial) { if (!this->dedup_.next(value)) @@ -64,7 +66,7 @@ float DelayedOffFilter::get_setup_priority() const { return setup_priority::HARD optional InvertFilter::new_value(bool value, bool is_initial) { return !value; } -AutorepeatFilter::AutorepeatFilter(const std::vector &timings) : timings_(timings) {} +AutorepeatFilter::AutorepeatFilter(std::vector timings) : timings_(std::move(timings)) {} optional AutorepeatFilter::new_value(bool value, bool is_initial) { if (value) { @@ -108,7 +110,7 @@ void AutorepeatFilter::next_value_(bool val) { float AutorepeatFilter::get_setup_priority() const { return setup_priority::HARDWARE; } -LambdaFilter::LambdaFilter(const std::function(bool)> &f) : f_(f) {} +LambdaFilter::LambdaFilter(std::function(bool)> f) : f_(std::move(f)) {} optional LambdaFilter::new_value(bool value, bool is_initial) { return this->f_(value); } diff --git a/esphome/components/binary_sensor/filter.h b/esphome/components/binary_sensor/filter.h index 8528e74a9f..59068634af 100644 --- a/esphome/components/binary_sensor/filter.h +++ b/esphome/components/binary_sensor/filter.h @@ -79,7 +79,7 @@ struct AutorepeatFilterTiming { class AutorepeatFilter : public Filter, public Component { public: - explicit AutorepeatFilter(const std::vector &timings); + explicit AutorepeatFilter(std::vector timings); optional new_value(bool value, bool is_initial) override; @@ -95,7 +95,7 @@ class AutorepeatFilter : public Filter, public Component { class LambdaFilter : public Filter { public: - explicit LambdaFilter(const std::function(bool)> &f); + explicit LambdaFilter(std::function(bool)> f); optional new_value(bool value, bool is_initial) override; diff --git a/esphome/components/binary_sensor_map/binary_sensor_map.cpp b/esphome/components/binary_sensor_map/binary_sensor_map.cpp index 7a2eb66cf8..d1123ddff0 100644 --- a/esphome/components/binary_sensor_map/binary_sensor_map.cpp +++ b/esphome/components/binary_sensor_map/binary_sensor_map.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace binary_sensor_map { -static const char *TAG = "binary_sensor_map"; +static const char *const TAG = "binary_sensor_map"; void BinarySensorMap::dump_config() { LOG_SENSOR(" ", "binary_sensor_map", this); } diff --git a/esphome/components/ble_client/ble_client.cpp b/esphome/components/ble_client/ble_client.cpp index f12d3a21c9..956848b73d 100644 --- a/esphome/components/ble_client/ble_client.cpp +++ b/esphome/components/ble_client/ble_client.cpp @@ -9,7 +9,7 @@ namespace esphome { namespace ble_client { -static const char *TAG = "ble_client"; +static const char *const TAG = "ble_client"; void BLEClient::setup() { auto ret = esp_ble_gattc_app_register(this->app_id); diff --git a/esphome/components/ble_client/sensor/ble_sensor.cpp b/esphome/components/ble_client/sensor/ble_sensor.cpp index 69a4b23313..270822be9d 100644 --- a/esphome/components/ble_client/sensor/ble_sensor.cpp +++ b/esphome/components/ble_client/sensor/ble_sensor.cpp @@ -9,7 +9,7 @@ namespace esphome { namespace ble_client { -static const char *TAG = "ble_sensor"; +static const char *const TAG = "ble_sensor"; uint32_t BLESensor::hash_base() { return 343459825UL; } diff --git a/esphome/components/ble_client/switch/ble_switch.cpp b/esphome/components/ble_client/switch/ble_switch.cpp index ef7b64be66..669984d705 100644 --- a/esphome/components/ble_client/switch/ble_switch.cpp +++ b/esphome/components/ble_client/switch/ble_switch.cpp @@ -7,7 +7,7 @@ namespace esphome { namespace ble_client { -static const char *TAG = "ble_switch"; +static const char *const TAG = "ble_switch"; void BLEClientSwitch::write_state(bool state) { this->parent_->set_enabled(state); diff --git a/esphome/components/ble_presence/ble_presence_device.cpp b/esphome/components/ble_presence/ble_presence_device.cpp index ad6b5ece88..1355c2bcc3 100644 --- a/esphome/components/ble_presence/ble_presence_device.cpp +++ b/esphome/components/ble_presence/ble_presence_device.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace ble_presence { -static const char *TAG = "ble_presence"; +static const char *const TAG = "ble_presence"; void BLEPresenceDevice::dump_config() { LOG_BINARY_SENSOR("", "BLE Presence", this); } diff --git a/esphome/components/ble_rssi/ble_rssi_sensor.cpp b/esphome/components/ble_rssi/ble_rssi_sensor.cpp index 6424837770..096259d8d1 100644 --- a/esphome/components/ble_rssi/ble_rssi_sensor.cpp +++ b/esphome/components/ble_rssi/ble_rssi_sensor.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace ble_rssi { -static const char *TAG = "ble_rssi"; +static const char *const TAG = "ble_rssi"; void BLERSSISensor::dump_config() { LOG_SENSOR("", "BLE RSSI Sensor", this); } diff --git a/esphome/components/ble_scanner/ble_scanner.cpp b/esphome/components/ble_scanner/ble_scanner.cpp index 6735599850..798824bb4e 100644 --- a/esphome/components/ble_scanner/ble_scanner.cpp +++ b/esphome/components/ble_scanner/ble_scanner.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace ble_scanner { -static const char *TAG = "ble_scanner"; +static const char *const TAG = "ble_scanner"; void BLEScanner::dump_config() { LOG_TEXT_SENSOR("", "BLE Scanner", this); } diff --git a/esphome/components/bme280/bme280.cpp b/esphome/components/bme280/bme280.cpp index 6bb5ac9800..097974da7a 100644 --- a/esphome/components/bme280/bme280.cpp +++ b/esphome/components/bme280/bme280.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace bme280 { -static const char *TAG = "bme280.sensor"; +static const char *const TAG = "bme280.sensor"; static const uint8_t BME280_REGISTER_DIG_T1 = 0x88; static const uint8_t BME280_REGISTER_DIG_T2 = 0x8A; diff --git a/esphome/components/bme680/bme680.cpp b/esphome/components/bme680/bme680.cpp index 77f381664b..ee7db3c65f 100644 --- a/esphome/components/bme680/bme680.cpp +++ b/esphome/components/bme680/bme680.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace bme680 { -static const char *TAG = "bme680.sensor"; +static const char *const TAG = "bme680.sensor"; static const uint8_t BME680_REGISTER_COEFF1 = 0x89; static const uint8_t BME680_REGISTER_COEFF2 = 0xE1; diff --git a/esphome/components/bme680_bsec/bme680_bsec.cpp b/esphome/components/bme680_bsec/bme680_bsec.cpp index 756629383c..8f53180296 100644 --- a/esphome/components/bme680_bsec/bme680_bsec.cpp +++ b/esphome/components/bme680_bsec/bme680_bsec.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace bme680_bsec { #ifdef USE_BSEC -static const char *TAG = "bme680_bsec.sensor"; +static const char *const TAG = "bme680_bsec.sensor"; static const std::string IAQ_ACCURACY_STATES[4] = {"Stabilizing", "Uncertain", "Calibrating", "Calibrated"}; diff --git a/esphome/components/bmp085/bmp085.cpp b/esphome/components/bmp085/bmp085.cpp index 88c5313df5..44e686fe1a 100644 --- a/esphome/components/bmp085/bmp085.cpp +++ b/esphome/components/bmp085/bmp085.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace bmp085 { -static const char *TAG = "bmp085.sensor"; +static const char *const TAG = "bmp085.sensor"; static const uint8_t BMP085_ADDRESS = 0x77; static const uint8_t BMP085_REGISTER_AC1_H = 0xAA; diff --git a/esphome/components/bmp280/bmp280.cpp b/esphome/components/bmp280/bmp280.cpp index aed9f3e515..4048e40077 100644 --- a/esphome/components/bmp280/bmp280.cpp +++ b/esphome/components/bmp280/bmp280.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace bmp280 { -static const char *TAG = "bmp280.sensor"; +static const char *const TAG = "bmp280.sensor"; static const uint8_t BMP280_REGISTER_STATUS = 0xF3; static const uint8_t BMP280_REGISTER_CONTROL = 0xF4; diff --git a/esphome/components/canbus/canbus.cpp b/esphome/components/canbus/canbus.cpp index 20afd4b296..b8b6b9e65f 100644 --- a/esphome/components/canbus/canbus.cpp +++ b/esphome/components/canbus/canbus.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace canbus { -static const char *TAG = "canbus"; +static const char *const TAG = "canbus"; void Canbus::setup() { ESP_LOGCONFIG(TAG, "Setting up Canbus..."); diff --git a/esphome/components/captive_portal/captive_portal.cpp b/esphome/components/captive_portal/captive_portal.cpp index 0e0e08fdea..746029e011 100644 --- a/esphome/components/captive_portal/captive_portal.cpp +++ b/esphome/components/captive_portal/captive_portal.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace captive_portal { -static const char *TAG = "captive_portal"; +static const char *const TAG = "captive_portal"; void CaptivePortal::handle_index(AsyncWebServerRequest *request) { AsyncResponseStream *stream = request->beginResponseStream("text/html"); @@ -147,7 +147,7 @@ float CaptivePortal::get_setup_priority() const { } void CaptivePortal::dump_config() { ESP_LOGCONFIG(TAG, "Captive Portal:"); } -CaptivePortal *global_captive_portal = nullptr; +CaptivePortal *global_captive_portal = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace captive_portal } // namespace esphome diff --git a/esphome/components/captive_portal/captive_portal.h b/esphome/components/captive_portal/captive_portal.h index afd4ff9dc5..44b1050f45 100644 --- a/esphome/components/captive_portal/captive_portal.h +++ b/esphome/components/captive_portal/captive_portal.h @@ -68,7 +68,7 @@ class CaptivePortal : public AsyncWebHandler, public Component { DNSServer *dns_server_{nullptr}; }; -extern CaptivePortal *global_captive_portal; +extern CaptivePortal *global_captive_portal; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace captive_portal } // namespace esphome diff --git a/esphome/components/ccs811/ccs811.cpp b/esphome/components/ccs811/ccs811.cpp index e7928d4d8b..dec070a9b2 100644 --- a/esphome/components/ccs811/ccs811.cpp +++ b/esphome/components/ccs811/ccs811.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace ccs811 { -static const char *TAG = "ccs811"; +static const char *const TAG = "ccs811"; // based on // - https://cdn.sparkfun.com/datasheets/BreakoutBoards/CCS811_Programming_Guide.pdf diff --git a/esphome/components/ccs811/sensor.py b/esphome/components/ccs811/sensor.py index 4c4f8802d4..4e81d6ac10 100644 --- a/esphome/components/ccs811/sensor.py +++ b/esphome/components/ccs811/sensor.py @@ -8,6 +8,8 @@ from esphome.const import ( STATE_CLASS_MEASUREMENT, UNIT_PARTS_PER_MILLION, UNIT_PARTS_PER_BILLION, + CONF_BASELINE, + CONF_ECO2, CONF_TEMPERATURE, CONF_TVOC, CONF_HUMIDITY, @@ -21,9 +23,6 @@ CCS811Component = ccs811_ns.class_( "CCS811Component", cg.PollingComponent, i2c.I2CDevice ) -CONF_ECO2 = "eco2" -CONF_BASELINE = "baseline" - CONFIG_SCHEMA = ( cv.Schema( { diff --git a/esphome/components/climate/climate.cpp b/esphome/components/climate/climate.cpp index 5369449839..07347e4eee 100644 --- a/esphome/components/climate/climate.cpp +++ b/esphome/components/climate/climate.cpp @@ -3,7 +3,7 @@ namespace esphome { namespace climate { -static const char *TAG = "climate"; +static const char *const TAG = "climate"; void ClimateCall::perform() { ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str()); diff --git a/esphome/components/climate/climate.h b/esphome/components/climate/climate.h index 4b12c98dc7..ed5c5069b7 100644 --- a/esphome/components/climate/climate.h +++ b/esphome/components/climate/climate.h @@ -11,8 +11,8 @@ namespace esphome { namespace climate { #define LOG_CLIMATE(prefix, type, obj) \ - if (obj != nullptr) { \ - ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, obj->get_name().c_str()); \ + if ((obj) != nullptr) { \ + ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \ } class Climate; diff --git a/esphome/components/climate/climate_mode.h b/esphome/components/climate/climate_mode.h index 07fbf32b26..476cf5bd84 100644 --- a/esphome/components/climate/climate_mode.h +++ b/esphome/components/climate/climate_mode.h @@ -7,19 +7,22 @@ namespace climate { /// Enum for all modes a climate device can be in. enum ClimateMode : uint8_t { - /// The climate device is off (not in auto, heat or cool mode) + /// The climate device is off CLIMATE_MODE_OFF = 0, - /// The climate device is set to automatically change the heating/cooling cycle + /// The climate device is set to heat/cool to reach the target temperature. CLIMATE_MODE_HEAT_COOL = 1, - /// The climate device is manually set to cool mode (not in auto mode!) + /// The climate device is set to cool to reach the target temperature CLIMATE_MODE_COOL = 2, - /// The climate device is manually set to heat mode (not in auto mode!) + /// The climate device is set to heat to reach the target temperature CLIMATE_MODE_HEAT = 3, - /// The climate device is manually set to fan only mode + /// The climate device only has the fan enabled, no heating or cooling is taking place CLIMATE_MODE_FAN_ONLY = 4, - /// The climate device is manually set to dry mode + /// The climate device is set to dry/humidity mode CLIMATE_MODE_DRY = 5, - /// The climate device is manually set to heat-cool mode + /** The climate device is adjusting the temperatre dynamically. + * For example, the target temperature can be adjusted based on a schedule, or learned behavior. + * The target temperature can't be adjusted when in this mode. + */ CLIMATE_MODE_AUTO = 6 }; @@ -27,15 +30,15 @@ enum ClimateMode : uint8_t { enum ClimateAction : uint8_t { /// The climate device is off (inactive or no power) CLIMATE_ACTION_OFF = 0, - /// The climate device is actively cooling (usually in cool or auto mode) + /// The climate device is actively cooling CLIMATE_ACTION_COOLING = 2, - /// The climate device is actively heating (usually in heat or auto mode) + /// The climate device is actively heating CLIMATE_ACTION_HEATING = 3, /// The climate device is idle (monitoring climate but no action needed) CLIMATE_ACTION_IDLE = 4, - /// The climate device is drying (either mode DRY or AUTO) + /// The climate device is drying CLIMATE_ACTION_DRYING = 5, - /// The climate device is in fan only mode (either mode FAN_ONLY or AUTO) + /// The climate device is in fan only mode CLIMATE_ACTION_FAN = 6, }; diff --git a/esphome/components/climate/climate_traits.h b/esphome/components/climate/climate_traits.h index b86a0c7774..fbd6f158e6 100644 --- a/esphome/components/climate/climate_traits.h +++ b/esphome/components/climate/climate_traits.h @@ -91,7 +91,7 @@ class ClimateTraits { ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead") void set_supports_fan_mode_diffuse(bool supported) { set_fan_mode_support_(CLIMATE_FAN_DIFFUSE, supported); } bool supports_fan_mode(ClimateFanMode fan_mode) const { return supported_fan_modes_.count(fan_mode); } - bool get_supports_fan_modes() const { return !supported_fan_modes_.empty(); } + bool get_supports_fan_modes() const { return !supported_fan_modes_.empty() || !supported_custom_fan_modes_.empty(); } const std::set get_supported_fan_modes() const { return supported_fan_modes_; } void set_supported_custom_fan_modes(std::set supported_custom_fan_modes) { @@ -127,13 +127,13 @@ class ClimateTraits { void set_supported_swing_modes(std::set modes) { supported_swing_modes_ = std::move(modes); } void add_supported_swing_mode(ClimateSwingMode mode) { supported_swing_modes_.insert(mode); } - ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead") + ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead") void set_supports_swing_mode_off(bool supported) { set_swing_mode_support_(CLIMATE_SWING_OFF, supported); } - ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead") + ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead") void set_supports_swing_mode_both(bool supported) { set_swing_mode_support_(CLIMATE_SWING_BOTH, supported); } - ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead") + ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead") void set_supports_swing_mode_vertical(bool supported) { set_swing_mode_support_(CLIMATE_SWING_VERTICAL, supported); } - ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead") + ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead") void set_supports_swing_mode_horizontal(bool supported) { set_swing_mode_support_(CLIMATE_SWING_HORIZONTAL, supported); } diff --git a/esphome/components/climate_ir/climate_ir.cpp b/esphome/components/climate_ir/climate_ir.cpp index 16a8e78414..8d5ae6053d 100644 --- a/esphome/components/climate_ir/climate_ir.cpp +++ b/esphome/components/climate_ir/climate_ir.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace climate_ir { -static const char *TAG = "climate_ir"; +static const char *const TAG = "climate_ir"; climate::ClimateTraits ClimateIR::traits() { auto traits = climate::ClimateTraits(); diff --git a/esphome/components/climate_ir/climate_ir.h b/esphome/components/climate_ir/climate_ir.h index ff04fa4e2f..677021da29 100644 --- a/esphome/components/climate_ir/climate_ir.h +++ b/esphome/components/climate_ir/climate_ir.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "esphome/components/climate/climate.h" #include "esphome/components/remote_base/remote_base.h" #include "esphome/components/remote_transmitter/remote_transmitter.h" @@ -26,8 +28,8 @@ class ClimateIR : public climate::Climate, public Component, public remote_base: this->temperature_step_ = temperature_step; this->supports_dry_ = supports_dry; this->supports_fan_only_ = supports_fan_only; - this->fan_modes_ = fan_modes; - this->swing_modes_ = swing_modes; + this->fan_modes_ = std::move(fan_modes); + this->swing_modes_ = std::move(swing_modes); } void setup() override; diff --git a/esphome/components/climate_ir_lg/climate_ir_lg.cpp b/esphome/components/climate_ir_lg/climate_ir_lg.cpp index d675883fcf..cbb1f7699b 100644 --- a/esphome/components/climate_ir_lg/climate_ir_lg.cpp +++ b/esphome/components/climate_ir_lg/climate_ir_lg.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace climate_ir_lg { -static const char *TAG = "climate.climate_ir_lg"; +static const char *const TAG = "climate.climate_ir_lg"; const uint32_t COMMAND_ON = 0x00000; const uint32_t COMMAND_ON_AI = 0x03000; @@ -94,7 +94,7 @@ void LgIrClimate::transmit_state() { // remote_state |= FAN_MODE_AUTO_DRY; } if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_HEAT) { - auto temp = (uint8_t) roundf(clamp(this->target_temperature, TEMP_MIN, TEMP_MAX)); + auto temp = (uint8_t) roundf(clamp(this->target_temperature, TEMP_MIN, TEMP_MAX)); remote_state |= ((temp - 15) << TEMP_SHIFT); } } diff --git a/esphome/components/coolix/coolix.cpp b/esphome/components/coolix/coolix.cpp index 012744b3e9..c9145e4ecf 100644 --- a/esphome/components/coolix/coolix.cpp +++ b/esphome/components/coolix/coolix.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace coolix { -static const char *TAG = "coolix.climate"; +static const char *const TAG = "coolix.climate"; const uint32_t COOLIX_OFF = 0xB27BE0; const uint32_t COOLIX_SWING = 0xB26BE0; @@ -84,7 +84,7 @@ void CoolixClimate::transmit_state() { } if (this->mode != climate::CLIMATE_MODE_OFF) { if (this->mode != climate::CLIMATE_MODE_FAN_ONLY) { - auto temp = (uint8_t) roundf(clamp(this->target_temperature, COOLIX_TEMP_MIN, COOLIX_TEMP_MAX)); + auto temp = (uint8_t) roundf(clamp(this->target_temperature, COOLIX_TEMP_MIN, COOLIX_TEMP_MAX)); remote_state |= COOLIX_TEMP_MAP[temp - COOLIX_TEMP_MIN]; } else { remote_state |= COOLIX_FAN_TEMP_CODE; diff --git a/esphome/components/cover/cover.cpp b/esphome/components/cover/cover.cpp index 8d87ecfd1a..2b4452f5b7 100644 --- a/esphome/components/cover/cover.cpp +++ b/esphome/components/cover/cover.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace cover { -static const char *TAG = "cover"; +static const char *const TAG = "cover"; const float COVER_OPEN = 1.0f; const float COVER_CLOSED = 0.0f; diff --git a/esphome/components/cover/cover.h b/esphome/components/cover/cover.h index 839cf9207e..8f30750fbd 100644 --- a/esphome/components/cover/cover.h +++ b/esphome/components/cover/cover.h @@ -12,14 +12,14 @@ const extern float COVER_OPEN; const extern float COVER_CLOSED; #define LOG_COVER(prefix, type, obj) \ - if (obj != nullptr) { \ - ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, obj->get_name().c_str()); \ - auto traits_ = obj->get_traits(); \ + if ((obj) != nullptr) { \ + ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \ + auto traits_ = (obj)->get_traits(); \ if (traits_.get_is_assumed_state()) { \ ESP_LOGCONFIG(TAG, "%s Assumed State: YES", prefix); \ } \ - if (!obj->get_device_class().empty()) { \ - ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, obj->get_device_class().c_str()); \ + if (!(obj)->get_device_class().empty()) { \ + ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \ } \ } @@ -110,15 +110,12 @@ class Cover : public Nameable { /// The current operation of the cover (idle, opening, closing). CoverOperation current_operation{COVER_OPERATION_IDLE}; - union { - /** The position of the cover from 0.0 (fully closed) to 1.0 (fully open). - * - * For binary covers this is always equals to 0.0 or 1.0 (see also COVER_OPEN and - * COVER_CLOSED constants). - */ - float position; - ESPDEPRECATED(".state is deprecated, please use .position instead") float state; - }; + /** The position of the cover from 0.0 (fully closed) to 1.0 (fully open). + * + * For binary covers this is always equals to 0.0 or 1.0 (see also COVER_OPEN and + * COVER_CLOSED constants). + */ + float position; /// The current tilt value of the cover from 0.0 to 1.0. float tilt{COVER_OPEN}; diff --git a/esphome/components/cs5460a/cs5460a.cpp b/esphome/components/cs5460a/cs5460a.cpp index 03cbd83513..a172bcdf56 100644 --- a/esphome/components/cs5460a/cs5460a.cpp +++ b/esphome/components/cs5460a/cs5460a.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace cs5460a { -static const char *TAG = "cs5460a"; +static const char *const TAG = "cs5460a"; void CS5460AComponent::write_register_(enum CS5460ARegister addr, uint32_t value) { this->write_byte(CMD_WRITE | (addr << 1)); diff --git a/esphome/components/cse7766/cse7766.cpp b/esphome/components/cse7766/cse7766.cpp index 6c014138fd..87bc4c4bdf 100644 --- a/esphome/components/cse7766/cse7766.cpp +++ b/esphome/components/cse7766/cse7766.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace cse7766 { -static const char *TAG = "cse7766"; +static const char *const TAG = "cse7766"; void CSE7766Component::loop() { const uint32_t now = millis(); diff --git a/esphome/components/ct_clamp/ct_clamp_sensor.cpp b/esphome/components/ct_clamp/ct_clamp_sensor.cpp index c1e3bec486..c27134d6ac 100644 --- a/esphome/components/ct_clamp/ct_clamp_sensor.cpp +++ b/esphome/components/ct_clamp/ct_clamp_sensor.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace ct_clamp { -static const char *TAG = "ct_clamp"; +static const char *const TAG = "ct_clamp"; void CTClampSensor::setup() { this->is_calibrating_offset_ = true; diff --git a/esphome/components/custom/binary_sensor/custom_binary_sensor.cpp b/esphome/components/custom/binary_sensor/custom_binary_sensor.cpp index cfd85e6f2a..ea83198568 100644 --- a/esphome/components/custom/binary_sensor/custom_binary_sensor.cpp +++ b/esphome/components/custom/binary_sensor/custom_binary_sensor.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace custom { -static const char *TAG = "custom.binary_sensor"; +static const char *const TAG = "custom.binary_sensor"; void CustomBinarySensorConstructor::dump_config() { for (auto *child : this->binary_sensors_) { diff --git a/esphome/components/custom/output/custom_output.h b/esphome/components/custom/output/custom_output.h index abebe16ee9..1b55d51e29 100644 --- a/esphome/components/custom/output/custom_output.h +++ b/esphome/components/custom/output/custom_output.h @@ -9,7 +9,9 @@ namespace custom { class CustomBinaryOutputConstructor { public: - CustomBinaryOutputConstructor(std::function()> init) { this->outputs_ = init(); } + CustomBinaryOutputConstructor(const std::function()> &init) { + this->outputs_ = init(); + } output::BinaryOutput *get_output(int i) { return this->outputs_[i]; } @@ -19,7 +21,9 @@ class CustomBinaryOutputConstructor { class CustomFloatOutputConstructor { public: - CustomFloatOutputConstructor(std::function()> init) { this->outputs_ = init(); } + CustomFloatOutputConstructor(const std::function()> &init) { + this->outputs_ = init(); + } output::FloatOutput *get_output(int i) { return this->outputs_[i]; } diff --git a/esphome/components/custom/sensor/custom_sensor.cpp b/esphome/components/custom/sensor/custom_sensor.cpp index 736a9110dd..e670f09530 100644 --- a/esphome/components/custom/sensor/custom_sensor.cpp +++ b/esphome/components/custom/sensor/custom_sensor.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace custom { -static const char *TAG = "custom.sensor"; +static const char *const TAG = "custom.sensor"; void CustomSensorConstructor::dump_config() { for (auto *child : this->sensors_) { diff --git a/esphome/components/custom/switch/custom_switch.cpp b/esphome/components/custom/switch/custom_switch.cpp index cc7f6b3640..6d0a8fa621 100644 --- a/esphome/components/custom/switch/custom_switch.cpp +++ b/esphome/components/custom/switch/custom_switch.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace custom { -static const char *TAG = "custom.switch"; +static const char *const TAG = "custom.switch"; void CustomSwitchConstructor::dump_config() { for (auto *child : this->switches_) { diff --git a/esphome/components/custom/switch/custom_switch.h b/esphome/components/custom/switch/custom_switch.h index b96074424d..186e7473fe 100644 --- a/esphome/components/custom/switch/custom_switch.h +++ b/esphome/components/custom/switch/custom_switch.h @@ -8,7 +8,7 @@ namespace custom { class CustomSwitchConstructor : public Component { public: - CustomSwitchConstructor(std::function()> init) { this->switches_ = init(); } + CustomSwitchConstructor(const std::function()> &init) { this->switches_ = init(); } switch_::Switch *get_switch(int i) { return this->switches_[i]; } diff --git a/esphome/components/custom/text_sensor/custom_text_sensor.cpp b/esphome/components/custom/text_sensor/custom_text_sensor.cpp index dec96387e7..618ba832a5 100644 --- a/esphome/components/custom/text_sensor/custom_text_sensor.cpp +++ b/esphome/components/custom/text_sensor/custom_text_sensor.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace custom { -static const char *TAG = "custom.text_sensor"; +static const char *const TAG = "custom.text_sensor"; void CustomTextSensorConstructor::dump_config() { for (auto *child : this->text_sensors_) { diff --git a/esphome/components/custom/text_sensor/custom_text_sensor.h b/esphome/components/custom/text_sensor/custom_text_sensor.h index aa8c7ddb03..f1e9c7665e 100644 --- a/esphome/components/custom/text_sensor/custom_text_sensor.h +++ b/esphome/components/custom/text_sensor/custom_text_sensor.h @@ -8,7 +8,7 @@ namespace custom { class CustomTextSensorConstructor : public Component { public: - CustomTextSensorConstructor(std::function()> init) { + CustomTextSensorConstructor(const std::function()> &init) { this->text_sensors_ = init(); } diff --git a/esphome/components/cwww/light.py b/esphome/components/cwww/light.py index 674c48d219..1a027a86b4 100644 --- a/esphome/components/cwww/light.py +++ b/esphome/components/cwww/light.py @@ -14,15 +14,18 @@ CWWWLightOutput = cwww_ns.class_("CWWWLightOutput", light.LightOutput) CONF_CONSTANT_BRIGHTNESS = "constant_brightness" -CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend( - { - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(CWWWLightOutput), - cv.Required(CONF_COLD_WHITE): cv.use_id(output.FloatOutput), - cv.Required(CONF_WARM_WHITE): cv.use_id(output.FloatOutput), - cv.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature, - cv.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): cv.color_temperature, - cv.Optional(CONF_CONSTANT_BRIGHTNESS, default=False): cv.boolean, - } +CONFIG_SCHEMA = cv.All( + light.RGB_LIGHT_SCHEMA.extend( + { + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(CWWWLightOutput), + cv.Required(CONF_COLD_WHITE): cv.use_id(output.FloatOutput), + cv.Required(CONF_WARM_WHITE): cv.use_id(output.FloatOutput), + cv.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature, + cv.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): cv.color_temperature, + cv.Optional(CONF_CONSTANT_BRIGHTNESS, default=False): cv.boolean, + } + ), + light.validate_color_temperature_channels, ) diff --git a/esphome/components/daikin/daikin.cpp b/esphome/components/daikin/daikin.cpp index dca3bffbac..5f8d0288e2 100644 --- a/esphome/components/daikin/daikin.cpp +++ b/esphome/components/daikin/daikin.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace daikin { -static const char *TAG = "daikin.climate"; +static const char *const TAG = "daikin.climate"; void DaikinClimate::transmit_state() { uint8_t remote_state[35] = {0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, 0x11, 0xDA, 0x27, 0x00, @@ -135,7 +135,7 @@ uint8_t DaikinClimate::temperature_() { case climate::CLIMATE_MODE_DRY: return 0xc0; default: - uint8_t temperature = (uint8_t) roundf(clamp(this->target_temperature, DAIKIN_TEMP_MIN, DAIKIN_TEMP_MAX)); + uint8_t temperature = (uint8_t) roundf(clamp(this->target_temperature, DAIKIN_TEMP_MIN, DAIKIN_TEMP_MAX)); return temperature << 1; } } diff --git a/esphome/components/dallas/dallas_component.cpp b/esphome/components/dallas/dallas_component.cpp index aa839e7331..7fc5e424f0 100644 --- a/esphome/components/dallas/dallas_component.cpp +++ b/esphome/components/dallas/dallas_component.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace dallas { -static const char *TAG = "dallas.sensor"; +static const char *const TAG = "dallas.sensor"; static const uint8_t DALLAS_MODEL_DS18S20 = 0x10; static const uint8_t DALLAS_MODEL_DS1822 = 0x22; diff --git a/esphome/components/dallas/esp_one_wire.cpp b/esphome/components/dallas/esp_one_wire.cpp index 92b7317f7c..702d1eddc2 100644 --- a/esphome/components/dallas/esp_one_wire.cpp +++ b/esphome/components/dallas/esp_one_wire.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace dallas { -static const char *TAG = "dallas.one_wire"; +static const char *const TAG = "dallas.one_wire"; const uint8_t ONE_WIRE_ROM_SELECT = 0x55; const int ONE_WIRE_ROM_SEARCH = 0xF0; diff --git a/esphome/components/debug/debug_component.cpp b/esphome/components/debug/debug_component.cpp index 4ffc034d50..668792c7b1 100644 --- a/esphome/components/debug/debug_component.cpp +++ b/esphome/components/debug/debug_component.cpp @@ -11,7 +11,7 @@ namespace esphome { namespace debug { -static const char *TAG = "debug"; +static const char *const TAG = "debug"; void DebugComponent::dump_config() { #ifndef ESPHOME_LOG_HAS_DEBUG diff --git a/esphome/components/deep_sleep/__init__.py b/esphome/components/deep_sleep/__init__.py index 7011081774..b7bf27e79a 100644 --- a/esphome/components/deep_sleep/__init__.py +++ b/esphome/components/deep_sleep/__init__.py @@ -6,7 +6,6 @@ from esphome.const import ( CONF_MODE, CONF_NUMBER, CONF_PINS, - CONF_RUN_CYCLES, CONF_RUN_DURATION, CONF_SLEEP_DURATION, CONF_WAKEUP_PIN, @@ -69,11 +68,6 @@ CONFIG_SCHEMA = cv.Schema( } ), ), - cv.Optional(CONF_RUN_CYCLES): cv.invalid( - "The run_cycles option has been removed in 1.11.0 as " - "it was essentially the same as a run_duration of 0s." - "Please use run_duration now." - ), } ).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/deep_sleep/deep_sleep_component.cpp b/esphome/components/deep_sleep/deep_sleep_component.cpp index 684d7e12bf..de5672759a 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.cpp +++ b/esphome/components/deep_sleep/deep_sleep_component.cpp @@ -5,9 +5,9 @@ namespace esphome { namespace deep_sleep { -static const char *TAG = "deep_sleep"; +static const char *const TAG = "deep_sleep"; -bool global_has_deep_sleep = false; +bool global_has_deep_sleep = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) void DeepSleepComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up Deep Sleep..."); diff --git a/esphome/components/deep_sleep/deep_sleep_component.h b/esphome/components/deep_sleep/deep_sleep_component.h index 09212d7d17..e163cf7709 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.h +++ b/esphome/components/deep_sleep/deep_sleep_component.h @@ -79,7 +79,7 @@ class DeepSleepComponent : public Component { bool prevent_{false}; }; -extern bool global_has_deep_sleep; +extern bool global_has_deep_sleep; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) template class EnterDeepSleepAction : public Action { public: diff --git a/esphome/components/dfplayer/dfplayer.cpp b/esphome/components/dfplayer/dfplayer.cpp index 6fed433dac..7df551f5d2 100644 --- a/esphome/components/dfplayer/dfplayer.cpp +++ b/esphome/components/dfplayer/dfplayer.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace dfplayer { -static const char* TAG = "dfplayer"; +static const char *const TAG = "dfplayer"; void DFPlayer::play_folder(uint16_t folder, uint16_t file) { if (folder < 100 && file < 256) { diff --git a/esphome/components/dfplayer/dfplayer.h b/esphome/components/dfplayer/dfplayer.h index 6742591926..5cd49c311d 100644 --- a/esphome/components/dfplayer/dfplayer.h +++ b/esphome/components/dfplayer/dfplayer.h @@ -103,7 +103,10 @@ class DFPlayer : public uart::UARTDevice, public Component { }; #define DFPLAYER_SIMPLE_ACTION(ACTION_CLASS, ACTION_METHOD) \ - template class ACTION_CLASS : public Action, public Parented { \ + template \ + class ACTION_CLASS : /* NOLINT */ \ + public Action, \ + public Parented { \ void play(Ts... x) override { this->parent_->ACTION_METHOD(); } \ }; diff --git a/esphome/components/dht/dht.cpp b/esphome/components/dht/dht.cpp index 9626260cf2..4a5c418e0a 100644 --- a/esphome/components/dht/dht.cpp +++ b/esphome/components/dht/dht.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace dht { -static const char *TAG = "dht"; +static const char *const TAG = "dht"; void DHT::setup() { ESP_LOGCONFIG(TAG, "Setting up DHT..."); @@ -94,11 +94,17 @@ bool HOT ICACHE_RAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, delayMicroseconds(40); } else if (this->model_ == DHT_MODEL_DHT22_TYPE2) { delayMicroseconds(2000); + } else if (this->model_ == DHT_MODEL_AM2302) { + delayMicroseconds(1000); } else { delayMicroseconds(800); } this->pin_->pin_mode(INPUT_PULLUP); - delayMicroseconds(40); + + // Host pull up 20-40us then DHT response 80us + // Start waiting for initial rising edge at the center when we + // expect the DHT response (30us+40us) + delayMicroseconds(70); uint8_t bit = 7; uint8_t byte = 0; @@ -116,8 +122,6 @@ bool HOT ICACHE_RAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, break; } } - if (error_code != 0) - break; start_time = micros(); uint32_t end_time = start_time; @@ -132,8 +136,6 @@ bool HOT ICACHE_RAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, break; } } - if (error_code != 0) - break; if (i < 0) continue; diff --git a/esphome/components/dht12/dht12.cpp b/esphome/components/dht12/dht12.cpp index 3c18f8055d..a5e1886918 100644 --- a/esphome/components/dht12/dht12.cpp +++ b/esphome/components/dht12/dht12.cpp @@ -8,7 +8,7 @@ namespace esphome { namespace dht12 { -static const char *TAG = "dht12"; +static const char *const TAG = "dht12"; void DHT12Component::update() { uint8_t data[5]; diff --git a/esphome/components/display/display_buffer.cpp b/esphome/components/display/display_buffer.cpp index 23f6faccb1..08050b2078 100644 --- a/esphome/components/display/display_buffer.cpp +++ b/esphome/components/display/display_buffer.cpp @@ -1,12 +1,14 @@ #include "display_buffer.h" + +#include "esphome/core/application.h" #include "esphome/core/color.h" #include "esphome/core/log.h" -#include "esphome/core/application.h" +#include namespace esphome { namespace display { -static const char *TAG = "display"; +static const char *const TAG = "display"; const Color COLOR_OFF(0, 0, 0, 0); const Color COLOR_ON(255, 255, 255, 255); @@ -524,7 +526,7 @@ void Animation::next_frame() { } } -DisplayPage::DisplayPage(const display_writer_t &writer) : writer_(writer) {} +DisplayPage::DisplayPage(display_writer_t writer) : writer_(std::move(writer)) {} void DisplayPage::show() { this->parent_->show_page(this); } void DisplayPage::show_next() { this->next_->show(); } void DisplayPage::show_prev() { this->prev_->show(); } diff --git a/esphome/components/display/display_buffer.h b/esphome/components/display/display_buffer.h index 0763262d4c..b89eab0dba 100644 --- a/esphome/components/display/display_buffer.h +++ b/esphome/components/display/display_buffer.h @@ -86,10 +86,10 @@ class DisplayOnPageChangeTrigger; using display_writer_t = std::function; #define LOG_DISPLAY(prefix, type, obj) \ - if (obj != nullptr) { \ + if ((obj) != nullptr) { \ ESP_LOGCONFIG(TAG, prefix type); \ - ESP_LOGCONFIG(TAG, "%s Rotations: %d °", prefix, obj->rotation_); \ - ESP_LOGCONFIG(TAG, "%s Dimensions: %dpx x %dpx", prefix, obj->get_width(), obj->get_height()); \ + ESP_LOGCONFIG(TAG, "%s Rotations: %d °", prefix, (obj)->rotation_); \ + ESP_LOGCONFIG(TAG, "%s Dimensions: %dpx x %dpx", prefix, (obj)->get_width(), (obj)->get_height()); \ } class DisplayBuffer { @@ -327,7 +327,7 @@ class DisplayBuffer { class DisplayPage { public: - DisplayPage(const display_writer_t &writer); + DisplayPage(display_writer_t writer); void show(); void show_next(); void show_prev(); diff --git a/esphome/components/ds1307/ds1307.cpp b/esphome/components/ds1307/ds1307.cpp index 2f33768132..d249e9743a 100644 --- a/esphome/components/ds1307/ds1307.cpp +++ b/esphome/components/ds1307/ds1307.cpp @@ -7,7 +7,7 @@ namespace esphome { namespace ds1307 { -static const char *TAG = "ds1307"; +static const char *const TAG = "ds1307"; void DS1307Component::setup() { ESP_LOGCONFIG(TAG, "Setting up DS1307..."); diff --git a/esphome/components/duty_cycle/duty_cycle_sensor.cpp b/esphome/components/duty_cycle/duty_cycle_sensor.cpp index 36147d0b63..8b7446b681 100644 --- a/esphome/components/duty_cycle/duty_cycle_sensor.cpp +++ b/esphome/components/duty_cycle/duty_cycle_sensor.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace duty_cycle { -static const char *TAG = "duty_cycle"; +static const char *const TAG = "duty_cycle"; void DutyCycleSensor::setup() { ESP_LOGCONFIG(TAG, "Setting up Duty Cycle Sensor '%s'...", this->get_name().c_str()); diff --git a/esphome/components/e131/e131.cpp b/esphome/components/e131/e131.cpp index 8a293e9067..fb72e5b470 100644 --- a/esphome/components/e131/e131.cpp +++ b/esphome/components/e131/e131.cpp @@ -14,7 +14,7 @@ namespace esphome { namespace e131 { -static const char *TAG = "e131"; +static const char *const TAG = "e131"; static const int PORT = 5568; E131Component::E131Component() {} diff --git a/esphome/components/e131/e131_addressable_light_effect.cpp b/esphome/components/e131/e131_addressable_light_effect.cpp index 5bf28f0f11..f280b5bc94 100644 --- a/esphome/components/e131/e131_addressable_light_effect.cpp +++ b/esphome/components/e131/e131_addressable_light_effect.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace e131 { -static const char *TAG = "e131_addressable_light_effect"; +static const char *const TAG = "e131_addressable_light_effect"; static const int MAX_DATA_SIZE = (sizeof(E131Packet::values) - 1); E131AddressableLightEffect::E131AddressableLightEffect(const std::string &name) : AddressableLightEffect(name) {} diff --git a/esphome/components/e131/e131_packet.cpp b/esphome/components/e131/e131_packet.cpp index ca68f5126d..14fdc084a6 100644 --- a/esphome/components/e131/e131_packet.cpp +++ b/esphome/components/e131/e131_packet.cpp @@ -8,7 +8,7 @@ namespace esphome { namespace e131 { -static const char *TAG = "e131"; +static const char *const TAG = "e131"; static const uint8_t ACN_ID[12] = {0x41, 0x53, 0x43, 0x2d, 0x45, 0x31, 0x2e, 0x31, 0x37, 0x00, 0x00, 0x00}; static const uint32_t VECTOR_ROOT = 4; @@ -51,7 +51,7 @@ union E131RawPacket { // We need to have at least one `1` value // Get the offset of `property_values[1]` -const long E131_MIN_PACKET_SIZE = reinterpret_cast(&((E131RawPacket *) nullptr)->property_values[1]); +const size_t E131_MIN_PACKET_SIZE = reinterpret_cast(&((E131RawPacket *) nullptr)->property_values[1]); bool E131Component::join_igmp_groups_() { if (listen_method_ != E131_MULTICAST) diff --git a/esphome/components/endstop/endstop_cover.cpp b/esphome/components/endstop/endstop_cover.cpp index 8e20cb6a29..cbc4b334d9 100644 --- a/esphome/components/endstop/endstop_cover.cpp +++ b/esphome/components/endstop/endstop_cover.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace endstop { -static const char *TAG = "endstop.cover"; +static const char *const TAG = "endstop.cover"; using namespace esphome::cover; diff --git a/esphome/components/esp32_ble/ble.cpp b/esphome/components/esp32_ble/ble.cpp index d245cb9e4d..a54ee48b30 100644 --- a/esphome/components/esp32_ble/ble.cpp +++ b/esphome/components/esp32_ble/ble.cpp @@ -15,7 +15,7 @@ namespace esphome { namespace esp32_ble { -static const char *TAG = "esp32_ble"; +static const char *const TAG = "esp32_ble"; void ESP32BLE::setup() { global_ble = this; diff --git a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp index b7810bd056..1b06fd787e 100644 --- a/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp +++ b/esphome/components/esp32_ble_beacon/esp32_ble_beacon.cpp @@ -13,7 +13,7 @@ namespace esphome { namespace esp32_ble_beacon { -static const char *TAG = "esp32_ble_beacon"; +static const char *const TAG = "esp32_ble_beacon"; static esp_ble_adv_params_t ble_adv_params = { .adv_int_min = 0x20, diff --git a/esphome/components/esp32_ble_tracker/__init__.py b/esphome/components/esp32_ble_tracker/__init__.py index 5c70ddb27f..18f1c46ff2 100644 --- a/esphome/components/esp32_ble_tracker/__init__.py +++ b/esphome/components/esp32_ble_tracker/__init__.py @@ -163,9 +163,6 @@ CONFIG_SCHEMA = cv.Schema( cv.Required(CONF_MANUFACTURER_ID): bt_uuid, } ), - cv.Optional("scan_interval"): cv.invalid( - "This option has been removed in 1.14 (Reason: " "it never had an effect)" - ), } ).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/esp32_ble_tracker/binary_sensor.py b/esphome/components/esp32_ble_tracker/binary_sensor.py deleted file mode 100644 index 3bea6d9900..0000000000 --- a/esphome/components/esp32_ble_tracker/binary_sensor.py +++ /dev/null @@ -1,3 +0,0 @@ -import esphome.config_validation as cv - -CONFIG_SCHEMA = cv.invalid("This platform has been renamed to ble_presence in 1.13") diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp index 7a5b023387..2a3403f88d 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.cpp @@ -19,7 +19,7 @@ namespace esphome { namespace esp32_ble_tracker { -static const char *TAG = "esp32_ble_tracker"; +static const char *const TAG = "esp32_ble_tracker"; ESP32BLETracker *global_esp32_ble_tracker = nullptr; diff --git a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h index 6f0c28a73c..0594c4a811 100644 --- a/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h +++ b/esphome/components/esp32_ble_tracker/esp32_ble_tracker.h @@ -85,12 +85,6 @@ class ESPBTDevice { int get_rssi() const { return rssi_; } const std::string &get_name() const { return this->name_; } - ESPDEPRECATED("Use get_tx_powers() instead") - optional get_tx_power() const { - if (this->tx_powers_.empty()) - return {}; - return this->tx_powers_[0]; - } const std::vector &get_tx_powers() const { return tx_powers_; } const optional &get_appearance() const { return appearance_; } diff --git a/esphome/components/esp32_camera/esp32_camera.cpp b/esphome/components/esp32_camera/esp32_camera.cpp index 6bb57a8304..500ddd67c9 100644 --- a/esphome/components/esp32_camera/esp32_camera.cpp +++ b/esphome/components/esp32_camera/esp32_camera.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace esp32_camera { -static const char *TAG = "esp32_camera"; +static const char *const TAG = "esp32_camera"; void ESP32Camera::setup() { global_esp32_camera = this; diff --git a/esphome/components/esp32_dac/esp32_dac.cpp b/esphome/components/esp32_dac/esp32_dac.cpp index cd0696b1ad..20a047f0ba 100644 --- a/esphome/components/esp32_dac/esp32_dac.cpp +++ b/esphome/components/esp32_dac/esp32_dac.cpp @@ -9,7 +9,7 @@ namespace esphome { namespace esp32_dac { -static const char *TAG = "esp32_dac"; +static const char *const TAG = "esp32_dac"; void ESP32DAC::setup() { ESP_LOGCONFIG(TAG, "Setting up ESP32 DAC Output..."); diff --git a/esphome/components/esp32_hall/esp32_hall.cpp b/esphome/components/esp32_hall/esp32_hall.cpp index d7964bcaa6..4bbf65e048 100644 --- a/esphome/components/esp32_hall/esp32_hall.cpp +++ b/esphome/components/esp32_hall/esp32_hall.cpp @@ -7,7 +7,7 @@ namespace esphome { namespace esp32_hall { -static const char *TAG = "esp32_hall"; +static const char *const TAG = "esp32_hall"; void ESP32HallSensor::update() { float value = (hallRead() / 4095.0f) * 10000.0f; diff --git a/esphome/components/esp32_improv/esp32_improv_component.cpp b/esphome/components/esp32_improv/esp32_improv_component.cpp index e076936771..65d253078c 100644 --- a/esphome/components/esp32_improv/esp32_improv_component.cpp +++ b/esphome/components/esp32_improv/esp32_improv_component.cpp @@ -10,7 +10,7 @@ namespace esphome { namespace esp32_improv { -static const char *TAG = "esp32_improv.component"; +static const char *const TAG = "esp32_improv.component"; ESP32ImprovComponent::ESP32ImprovComponent() { global_improv_component = this; } diff --git a/esphome/components/esp32_touch/esp32_touch.cpp b/esphome/components/esp32_touch/esp32_touch.cpp index ce0028159e..a990e632af 100644 --- a/esphome/components/esp32_touch/esp32_touch.cpp +++ b/esphome/components/esp32_touch/esp32_touch.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace esp32_touch { -static const char *TAG = "esp32_touch"; +static const char *const TAG = "esp32_touch"; void ESP32TouchComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up ESP32 Touch Hub..."); diff --git a/esphome/components/esp8266_pwm/esp8266_pwm.cpp b/esphome/components/esp8266_pwm/esp8266_pwm.cpp index b3fd2398f3..37a9f3efbf 100644 --- a/esphome/components/esp8266_pwm/esp8266_pwm.cpp +++ b/esphome/components/esp8266_pwm/esp8266_pwm.cpp @@ -11,7 +11,7 @@ namespace esphome { namespace esp8266_pwm { -static const char *TAG = "esp8266_pwm"; +static const char *const TAG = "esp8266_pwm"; void ESP8266PWM::setup() { ESP_LOGCONFIG(TAG, "Setting up ESP8266 PWM Output..."); diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index 94c9ddd2e9..95b7e40151 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -85,9 +85,6 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_ENABLE_MDNS, default=True): cv.boolean, cv.Optional(CONF_DOMAIN, default=".local"): cv.domain_name, cv.Optional(CONF_USE_ADDRESS): cv.string_strict, - cv.Optional("hostname"): cv.invalid( - "The hostname option has been removed in 1.11.0" - ), } ).extend(cv.COMPONENT_SCHEMA), _validate, diff --git a/esphome/components/ethernet/ethernet_component.cpp b/esphome/components/ethernet/ethernet_component.cpp index 8c96cca951..52c184eef6 100644 --- a/esphome/components/ethernet/ethernet_component.cpp +++ b/esphome/components/ethernet/ethernet_component.cpp @@ -9,6 +9,11 @@ #include #include +/// Macro for IDF version comparision +#ifndef ESP_IDF_VERSION_VAL +#define ESP_IDF_VERSION_VAL(major, minor, patch) (((major) << 16) | ((minor) << 8) | (patch)) +#endif + // Defined in WiFiGeneric.cpp, sets global initialized flag, starts network event task queue and calls // tcpip_adapter_init() extern void tcpipInit(); @@ -16,10 +21,17 @@ extern void tcpipInit(); namespace esphome { namespace ethernet { -static const char *TAG = "ethernet"; +static const char *const TAG = "ethernet"; EthernetComponent *global_eth_component; +#define ESPHL_ERROR_CHECK(err, message) \ + if (err != ESP_OK) { \ + ESP_LOGE(TAG, message ": (%d) %s", err, esp_err_to_name(err)); \ + this->mark_failed(); \ + return; \ + } + EthernetComponent::EthernetComponent() { global_eth_component = this; } void EthernetComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up Ethernet..."); @@ -31,103 +43,6 @@ void EthernetComponent::setup() { this->power_pin_->setup(); } - this->start_connect_(); - -#ifdef USE_MDNS - network_setup_mdns(); -#endif -} -void EthernetComponent::loop() { - const uint32_t now = millis(); - if (!this->connected_ && !this->last_connected_ && now - this->connect_begin_ > 15000) { - ESP_LOGW(TAG, "Connecting via ethernet failed! Re-connecting..."); - this->start_connect_(); - return; - } - - if (this->connected_ == this->last_connected_) - // nothing changed - return; - - if (this->connected_) { - // connection established - ESP_LOGI(TAG, "Connected via Ethernet!"); - this->dump_connect_params_(); - this->status_clear_warning(); - } else { - // connection lost - ESP_LOGW(TAG, "Connection via Ethernet lost! Re-connecting..."); - this->start_connect_(); - } - - this->last_connected_ = this->connected_; - - network_tick_mdns(); -} -void EthernetComponent::dump_config() { - ESP_LOGCONFIG(TAG, "Ethernet:"); - this->dump_connect_params_(); - LOG_PIN(" Power Pin: ", this->power_pin_); - ESP_LOGCONFIG(TAG, " MDC Pin: %u", this->mdc_pin_); - ESP_LOGCONFIG(TAG, " MDIO Pin: %u", this->mdio_pin_); - ESP_LOGCONFIG(TAG, " Type: %s", this->type_ == ETHERNET_TYPE_LAN8720 ? "LAN8720" : "TLK110"); -} -float EthernetComponent::get_setup_priority() const { return setup_priority::WIFI; } -bool EthernetComponent::can_proceed() { return this->is_connected(); } -IPAddress EthernetComponent::get_ip_address() { - tcpip_adapter_ip_info_t ip; - tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip); - return IPAddress(ip.ip.addr); -} - -void EthernetComponent::on_wifi_event_(system_event_id_t event, system_event_info_t info) { - const char *event_name; - - switch (event) { - case SYSTEM_EVENT_ETH_START: - event_name = "ETH started"; - break; - case SYSTEM_EVENT_ETH_STOP: - event_name = "ETH stopped"; - this->connected_ = false; - break; - case SYSTEM_EVENT_ETH_CONNECTED: - event_name = "ETH connected"; - break; - case SYSTEM_EVENT_ETH_DISCONNECTED: - event_name = "ETH disconnected"; - this->connected_ = false; - break; - case SYSTEM_EVENT_ETH_GOT_IP: - event_name = "ETH Got IP"; - this->connected_ = true; - break; - default: - return; - } - - ESP_LOGV(TAG, "[Ethernet event] %s (num=%d)", event_name, event); -} - -#define ESPHL_ERROR_CHECK(err, message) \ - if (err != ESP_OK) { \ - ESP_LOGE(TAG, message ": %d", err); \ - this->mark_failed(); \ - return; \ - } - -void EthernetComponent::start_connect_() { - this->connect_begin_ = millis(); - this->status_set_warning(); - - esp_err_t err; - if (this->initialized_) { - // already initialized - err = esp_eth_enable(); - ESPHL_ERROR_CHECK(err, "ETH enable error"); - return; - } - switch (this->type_) { case ETHERNET_TYPE_LAN8720: { memcpy(&this->eth_config, &phy_lan8720_default_ethernet_config, sizeof(eth_config_t)); @@ -155,16 +70,111 @@ void EthernetComponent::start_connect_() { tcpipInit(); + esp_err_t err; err = esp_eth_init(&this->eth_config); - if (err != ESP_OK) { - ESP_LOGE(TAG, "ETH init error: %d", err); - this->mark_failed(); - return; + ESPHL_ERROR_CHECK(err, "ETH init error"); + err = esp_eth_enable(); + ESPHL_ERROR_CHECK(err, "ETH enable error"); + +#ifdef USE_MDNS + network_setup_mdns(); +#endif +} +void EthernetComponent::loop() { + const uint32_t now = millis(); + + switch (this->state_) { + case EthernetComponentState::STOPPED: + if (this->started_) { + ESP_LOGI(TAG, "Starting ethernet connection"); + this->state_ = EthernetComponentState::CONNECTING; + this->start_connect_(); + } + break; + case EthernetComponentState::CONNECTING: + if (!this->started_) { + ESP_LOGI(TAG, "Stopped ethernet connection"); + this->state_ = EthernetComponentState::STOPPED; + } else if (this->connected_) { + // connection established + ESP_LOGI(TAG, "Connected via Ethernet!"); + this->state_ = EthernetComponentState::CONNECTED; + + this->dump_connect_params_(); + this->status_clear_warning(); + + network_tick_mdns(); + } else if (now - this->connect_begin_ > 15000) { + ESP_LOGW(TAG, "Connecting via ethernet failed! Re-connecting..."); + this->start_connect_(); + } + break; + case EthernetComponentState::CONNECTED: + if (!this->started_) { + ESP_LOGI(TAG, "Stopped ethernet connection"); + this->state_ = EthernetComponentState::STOPPED; + } else if (!this->connected_) { + ESP_LOGW(TAG, "Connection via Ethernet lost! Re-connecting..."); + this->state_ = EthernetComponentState::CONNECTING; + this->start_connect_(); + } + break; + } +} +void EthernetComponent::dump_config() { + ESP_LOGCONFIG(TAG, "Ethernet:"); + this->dump_connect_params_(); + LOG_PIN(" Power Pin: ", this->power_pin_); + ESP_LOGCONFIG(TAG, " MDC Pin: %u", this->mdc_pin_); + ESP_LOGCONFIG(TAG, " MDIO Pin: %u", this->mdio_pin_); + ESP_LOGCONFIG(TAG, " Type: %s", this->type_ == ETHERNET_TYPE_LAN8720 ? "LAN8720" : "TLK110"); +} +float EthernetComponent::get_setup_priority() const { return setup_priority::WIFI; } +bool EthernetComponent::can_proceed() { return this->is_connected(); } +IPAddress EthernetComponent::get_ip_address() { + tcpip_adapter_ip_info_t ip; + tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip); + return IPAddress(ip.ip.addr); +} + +void EthernetComponent::on_wifi_event_(system_event_id_t event, system_event_info_t info) { + const char *event_name; + + switch (event) { + case SYSTEM_EVENT_ETH_START: + event_name = "ETH started"; + this->started_ = true; + break; + case SYSTEM_EVENT_ETH_STOP: + event_name = "ETH stopped"; + this->started_ = false; + this->connected_ = false; + break; + case SYSTEM_EVENT_ETH_CONNECTED: + event_name = "ETH connected"; + break; + case SYSTEM_EVENT_ETH_DISCONNECTED: + event_name = "ETH disconnected"; + this->connected_ = false; + break; + case SYSTEM_EVENT_ETH_GOT_IP: + event_name = "ETH Got IP"; + this->connected_ = true; + break; + default: + return; } - this->initialized_ = true; + ESP_LOGV(TAG, "[Ethernet event] %s (num=%d)", event_name, event); +} - tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_ETH, App.get_name().c_str()); +void EthernetComponent::start_connect_() { + this->connect_begin_ = millis(); + this->status_set_warning(); + + esp_err_t err; + err = tcpip_adapter_set_hostname(TCPIP_ADAPTER_IF_ETH, App.get_name().c_str()); + ESPHL_ERROR_CHECK(err, "ETH set hostname error"); tcpip_adapter_ip_info_t info; if (this->manual_ip_.has_value()) { @@ -215,7 +225,7 @@ void EthernetComponent::eth_phy_power_enable_(bool enable) { delay(1); global_eth_component->orig_power_enable_fun_(enable); } -bool EthernetComponent::is_connected() { return this->connected_ && this->last_connected_; } +bool EthernetComponent::is_connected() { return this->state_ == EthernetComponentState::CONNECTED; } void EthernetComponent::dump_connect_params_() { tcpip_adapter_ip_info_t ip; tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip); diff --git a/esphome/components/ethernet/ethernet_component.h b/esphome/components/ethernet/ethernet_component.h index 2dbd8ccd9d..326cd1edea 100644 --- a/esphome/components/ethernet/ethernet_component.h +++ b/esphome/components/ethernet/ethernet_component.h @@ -26,6 +26,12 @@ struct ManualIP { IPAddress dns2; ///< The second DNS server. 0.0.0.0 for default. }; +enum class EthernetComponentState { + STOPPED, + CONNECTING, + CONNECTED, +}; + class EthernetComponent : public Component { public: EthernetComponent(); @@ -65,9 +71,9 @@ class EthernetComponent : public Component { eth_clock_mode_t clk_mode_{ETH_CLOCK_GPIO0_IN}; optional manual_ip_{}; - bool initialized_{false}; + bool started_{false}; bool connected_{false}; - bool last_connected_{false}; + EthernetComponentState state_{EthernetComponentState::STOPPED}; uint32_t connect_begin_; eth_config_t eth_config; eth_phy_power_enable_func orig_power_enable_fun_; diff --git a/esphome/components/exposure_notifications/exposure_notifications.cpp b/esphome/components/exposure_notifications/exposure_notifications.cpp index 1d8cf83a0e..9db181fbee 100644 --- a/esphome/components/exposure_notifications/exposure_notifications.cpp +++ b/esphome/components/exposure_notifications/exposure_notifications.cpp @@ -9,7 +9,7 @@ namespace exposure_notifications { using namespace esp32_ble_tracker; -static const char *TAG = "exposure_notifications"; +static const char *const TAG = "exposure_notifications"; bool ExposureNotificationTrigger::parse_device(const ESPBTDevice &device) { // See also https://blog.google/documents/70/Exposure_Notification_-_Bluetooth_Specification_v1.2.2.pdf diff --git a/esphome/components/external_components/__init__.py b/esphome/components/external_components/__init__.py index 1602ac3b07..bf44dc1929 100644 --- a/esphome/components/external_components/__init__.py +++ b/esphome/components/external_components/__init__.py @@ -109,7 +109,15 @@ def _compute_destination_path(key: str) -> Path: return base_dir / h.hexdigest()[:8] -def _handle_git_response(ret): +def _run_git_command(cmd): + try: + ret = subprocess.run(cmd, capture_output=True, check=False) + except FileNotFoundError as err: + raise cv.Invalid( + "git is not installed but required for external_components.\n" + "Please see https://git-scm.com/book/en/v2/Getting-Started-Installing-Git for installing git" + ) from err + if ret.returncode != 0 and ret.stderr: err_str = ret.stderr.decode("utf-8") lines = [x.strip() for x in err_str.splitlines()] @@ -118,46 +126,59 @@ def _handle_git_response(ret): raise cv.Invalid(err_str) +def _process_git_config(config: dict, refresh) -> str: + key = f"{config[CONF_URL]}@{config.get(CONF_REF)}" + repo_dir = _compute_destination_path(key) + if not repo_dir.is_dir(): + _LOGGER.info("Cloning %s", key) + _LOGGER.debug("Location: %s", repo_dir) + cmd = ["git", "clone", "--depth=1"] + if CONF_REF in config: + cmd += ["--branch", config[CONF_REF]] + cmd += ["--", config[CONF_URL], str(repo_dir)] + _run_git_command(cmd) + + else: + # Check refresh needed + file_timestamp = Path(repo_dir / ".git" / "FETCH_HEAD") + # On first clone, FETCH_HEAD does not exists + if not file_timestamp.exists(): + file_timestamp = Path(repo_dir / ".git" / "HEAD") + age = datetime.datetime.now() - datetime.datetime.fromtimestamp( + file_timestamp.stat().st_mtime + ) + if age.seconds > refresh.total_seconds: + _LOGGER.info("Updating %s", key) + _LOGGER.debug("Location: %s", repo_dir) + # Stash local changes (if any) + _run_git_command(["git", "stash", "push", "--include-untracked"]) + # Fetch remote ref + cmd = ["git", "fetch", "--", "origin"] + if CONF_REF in config: + cmd.append(config[CONF_REF]) + _run_git_command(cmd) + # Hard reset to FETCH_HEAD (short-lived git ref corresponding to most recent fetch) + _run_git_command(["git", "reset", "--hard", "FETCH_HEAD"]) + + if (repo_dir / "esphome" / "components").is_dir(): + components_dir = repo_dir / "esphome" / "components" + elif (repo_dir / "components").is_dir(): + components_dir = repo_dir / "components" + else: + raise cv.Invalid( + "Could not find components folder for source. Please check the source contains a 'components' or 'esphome/components' folder" + ) + + return components_dir + + def _process_single_config(config: dict): conf = config[CONF_SOURCE] if conf[CONF_TYPE] == TYPE_GIT: - key = f"{conf[CONF_URL]}@{conf.get(CONF_REF)}" - repo_dir = _compute_destination_path(key) - if not repo_dir.is_dir(): - cmd = ["git", "clone", "--depth=1"] - if CONF_REF in conf: - cmd += ["--branch", conf[CONF_REF]] - cmd += [conf[CONF_URL], str(repo_dir)] - ret = subprocess.run(cmd, capture_output=True, check=False) - _handle_git_response(ret) - - else: - # Check refresh needed - file_timestamp = Path(repo_dir / ".git" / "FETCH_HEAD") - # On first clone, FETCH_HEAD does not exists - if not file_timestamp.exists(): - file_timestamp = Path(repo_dir / ".git" / "HEAD") - age = datetime.datetime.now() - datetime.datetime.fromtimestamp( - file_timestamp.stat().st_mtime + with cv.prepend_path([CONF_SOURCE]): + components_dir = _process_git_config( + config[CONF_SOURCE], config[CONF_REFRESH] ) - if age.seconds > config[CONF_REFRESH].total_seconds: - _LOGGER.info("Executing git pull %s", key) - cmd = ["git", "pull"] - ret = subprocess.run( - cmd, cwd=repo_dir, capture_output=True, check=False - ) - _handle_git_response(ret) - - if (repo_dir / "esphome" / "components").is_dir(): - components_dir = repo_dir / "esphome" / "components" - elif (repo_dir / "components").is_dir(): - components_dir = repo_dir / "components" - else: - raise cv.Invalid( - "Could not find components folder for source. Please check the source contains a 'components' or 'esphome/components' folder", - [CONF_SOURCE], - ) - elif conf[CONF_TYPE] == TYPE_LOCAL: components_dir = Path(CORE.relative_config_path(conf[CONF_PATH])) else: diff --git a/esphome/components/ezo/ezo.cpp b/esphome/components/ezo/ezo.cpp index 97caa1495b..90b1e4ace9 100644 --- a/esphome/components/ezo/ezo.cpp +++ b/esphome/components/ezo/ezo.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace ezo { -static const char *TAG = "ezo.sensor"; +static const char *const TAG = "ezo.sensor"; static const uint16_t EZO_STATE_WAIT = 1; static const uint16_t EZO_STATE_SEND_TEMP = 2; diff --git a/esphome/components/ezo/ezo.h b/esphome/components/ezo/ezo.h index b2b59e38d4..d46d193ae7 100644 --- a/esphome/components/ezo/ezo.h +++ b/esphome/components/ezo/ezo.h @@ -18,8 +18,8 @@ class EZOSensor : public sensor::Sensor, public PollingComponent, public i2c::I2 void set_tempcomp_value(float temp); protected: - unsigned long start_time_ = 0; - unsigned long wait_time_ = 0; + uint32_t start_time_ = 0; + uint32_t wait_time_ = 0; uint16_t state_ = 0; float tempcomp_; }; diff --git a/esphome/components/fan/automation.cpp b/esphome/components/fan/automation.cpp index 3c9ca9b6ed..79e583fc57 100644 --- a/esphome/components/fan/automation.cpp +++ b/esphome/components/fan/automation.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace fan { -static const char *TAG = "fan.automation"; +static const char *const TAG = "fan.automation"; } // namespace fan } // namespace esphome diff --git a/esphome/components/fan/fan_helpers.cpp b/esphome/components/fan/fan_helpers.cpp index be16e6bb64..09be20991b 100644 --- a/esphome/components/fan/fan_helpers.cpp +++ b/esphome/components/fan/fan_helpers.cpp @@ -6,7 +6,7 @@ namespace fan { FanSpeed speed_level_to_enum(int speed_level, int supported_speed_levels) { const auto speed_ratio = static_cast(speed_level) / (supported_speed_levels + 1); - const auto legacy_level = static_cast(clamp(ceilf(speed_ratio * 3), 1, 3)); + const auto legacy_level = clamp(static_cast(ceilf(speed_ratio * 3)), 1, 3); return static_cast(legacy_level - 1); } diff --git a/esphome/components/fan/fan_state.cpp b/esphome/components/fan/fan_state.cpp index 5a3a7ecebc..9b4ae53937 100644 --- a/esphome/components/fan/fan_state.cpp +++ b/esphome/components/fan/fan_state.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace fan { -static const char *TAG = "fan"; +static const char *const TAG = "fan"; const FanTraits &FanState::get_traits() const { return this->traits_; } void FanState::set_traits(const FanTraits &traits) { this->traits_ = traits; } @@ -54,7 +54,7 @@ void FanStateCall::perform() const { } if (this->speed_.has_value()) { const int speed_count = this->state_->get_traits().supported_speed_count(); - this->state_->speed = static_cast(clamp(*this->speed_, 1, speed_count)); + this->state_->speed = clamp(*this->speed_, 1, speed_count); } FanStateRTCState saved{}; diff --git a/esphome/components/fastled_base/fastled_light.cpp b/esphome/components/fastled_base/fastled_light.cpp index 8420748dd5..4d791f5709 100644 --- a/esphome/components/fastled_base/fastled_light.cpp +++ b/esphome/components/fastled_base/fastled_light.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace fastled_base { -static const char *TAG = "fastled"; +static const char *const TAG = "fastled"; void FastLEDLightOutput::setup() { ESP_LOGCONFIG(TAG, "Setting up FastLED light..."); diff --git a/esphome/components/fingerprint_grow/fingerprint_grow.cpp b/esphome/components/fingerprint_grow/fingerprint_grow.cpp index 370bdc782b..b0c0be59af 100644 --- a/esphome/components/fingerprint_grow/fingerprint_grow.cpp +++ b/esphome/components/fingerprint_grow/fingerprint_grow.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace fingerprint_grow { -static const char* TAG = "fingerprint_grow"; +static const char *const TAG = "fingerprint_grow"; // Based on Adafruit's library: https://github.com/adafruit/Adafruit-Fingerprint-Sensor-Library diff --git a/esphome/components/font/__init__.py b/esphome/components/font/__init__.py index 11bbedd80b..16a7d8a612 100644 --- a/esphome/components/font/__init__.py +++ b/esphome/components/font/__init__.py @@ -110,7 +110,7 @@ async def to_code(config): _, (offset_x, offset_y) = font.font.getsize(glyph) width, height = mask.size width8 = ((width + 7) // 8) * 8 - glyph_data = [0 for _ in range(height * width8 // 8)] # noqa: F812 + glyph_data = [0] * (height * width8 // 8) for y in range(height): for x in range(width): if not mask.getpixel((x, y)): diff --git a/esphome/components/fujitsu_general/fujitsu_general.cpp b/esphome/components/fujitsu_general/fujitsu_general.cpp index 2e93a98e52..8d789bbcfc 100644 --- a/esphome/components/fujitsu_general/fujitsu_general.cpp +++ b/esphome/components/fujitsu_general/fujitsu_general.cpp @@ -5,10 +5,11 @@ namespace fujitsu_general { // bytes' bits are reversed for fujitsu, so nibbles are ordered 1, 0, 3, 2, 5, 4, etc... -#define SET_NIBBLE(message, nibble, value) (message[nibble / 2] |= (value & 0b00001111) << ((nibble % 2) ? 0 : 4)) -#define GET_NIBBLE(message, nibble) ((message[nibble / 2] >> ((nibble % 2) ? 0 : 4)) & 0b00001111) +#define SET_NIBBLE(message, nibble, value) \ + ((message)[(nibble) / 2] |= ((value) &0b00001111) << (((nibble) % 2) ? 0 : 4)) +#define GET_NIBBLE(message, nibble) (((message)[(nibble) / 2] >> (((nibble) % 2) ? 0 : 4)) & 0b00001111) -static const char* TAG = "fujitsu_general.climate"; +static const char *const TAG = "fujitsu_general.climate"; // Common header const uint8_t FUJITSU_GENERAL_COMMON_LENGTH = 6; @@ -109,7 +110,7 @@ void FujitsuGeneralClimate::transmit_state() { // Set temperature uint8_t temperature_clamped = - (uint8_t) roundf(clamp(this->target_temperature, FUJITSU_GENERAL_TEMP_MIN, FUJITSU_GENERAL_TEMP_MAX)); + (uint8_t) roundf(clamp(this->target_temperature, FUJITSU_GENERAL_TEMP_MIN, FUJITSU_GENERAL_TEMP_MAX)); uint8_t temperature_offset = temperature_clamped - FUJITSU_GENERAL_TEMP_MIN; SET_NIBBLE(remote_state, FUJITSU_GENERAL_TEMPERATURE_NIBBLE, temperature_offset); @@ -202,7 +203,7 @@ void FujitsuGeneralClimate::transmit_off_() { this->power_ = false; } -void FujitsuGeneralClimate::transmit_(uint8_t const* message, uint8_t length) { +void FujitsuGeneralClimate::transmit_(uint8_t const *message, uint8_t length) { ESP_LOGV(TAG, "Transmit message length %d", length); auto transmit = this->transmitter_->transmit(); @@ -231,7 +232,7 @@ void FujitsuGeneralClimate::transmit_(uint8_t const* message, uint8_t length) { transmit.perform(); } -uint8_t FujitsuGeneralClimate::checksum_state_(uint8_t const* message) { +uint8_t FujitsuGeneralClimate::checksum_state_(uint8_t const *message) { uint8_t checksum = 0; for (uint8_t i = 7; i < FUJITSU_GENERAL_STATE_MESSAGE_LENGTH - 1; ++i) { checksum += message[i]; @@ -239,7 +240,7 @@ uint8_t FujitsuGeneralClimate::checksum_state_(uint8_t const* message) { return 256 - checksum; } -uint8_t FujitsuGeneralClimate::checksum_util_(uint8_t const* message) { return 255 - message[5]; } +uint8_t FujitsuGeneralClimate::checksum_util_(uint8_t const *message) { return 255 - message[5]; } bool FujitsuGeneralClimate::on_receive(remote_base::RemoteReceiveData data) { ESP_LOGV(TAG, "Received IR message"); diff --git a/esphome/components/fujitsu_general/fujitsu_general.h b/esphome/components/fujitsu_general/fujitsu_general.h index e97615f739..7a26cd7b6b 100644 --- a/esphome/components/fujitsu_general/fujitsu_general.h +++ b/esphome/components/fujitsu_general/fujitsu_general.h @@ -66,13 +66,13 @@ class FujitsuGeneralClimate : public climate_ir::ClimateIR { bool on_receive(remote_base::RemoteReceiveData data) override; /// Transmit message as IR pulses - void transmit_(uint8_t const* message, uint8_t length); + void transmit_(uint8_t const *message, uint8_t length); /// Calculate checksum for a state message - uint8_t checksum_state_(uint8_t const* message); + uint8_t checksum_state_(uint8_t const *message); /// Calculate cecksum for a util message - uint8_t checksum_util_(uint8_t const* message); + uint8_t checksum_util_(uint8_t const *message); // true if currently on - fujitsus transmit an on flag on when the remote moves from off to on bool power_{false}; diff --git a/esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp b/esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp index f95778af4c..cf4b088580 100644 --- a/esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp +++ b/esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace gpio { -static const char *TAG = "gpio.binary_sensor"; +static const char *const TAG = "gpio.binary_sensor"; void GPIOBinarySensor::setup() { this->pin_->setup(); diff --git a/esphome/components/gpio/output/gpio_binary_output.cpp b/esphome/components/gpio/output/gpio_binary_output.cpp index b64492f497..a7dd9ab188 100644 --- a/esphome/components/gpio/output/gpio_binary_output.cpp +++ b/esphome/components/gpio/output/gpio_binary_output.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace gpio { -static const char *TAG = "gpio.output"; +static const char *const TAG = "gpio.output"; void GPIOBinaryOutput::dump_config() { ESP_LOGCONFIG(TAG, "GPIO Binary Output:"); diff --git a/esphome/components/gpio/switch/gpio_switch.cpp b/esphome/components/gpio/switch/gpio_switch.cpp index 2475d43e93..410a3818d6 100644 --- a/esphome/components/gpio/switch/gpio_switch.cpp +++ b/esphome/components/gpio/switch/gpio_switch.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace gpio { -static const char *TAG = "switch.gpio"; +static const char *const TAG = "switch.gpio"; float GPIOSwitch::get_setup_priority() const { return setup_priority::HARDWARE; } void GPIOSwitch::setup() { diff --git a/esphome/components/gps/gps.cpp b/esphome/components/gps/gps.cpp index ba0afdf7cc..1e8ca94e9e 100644 --- a/esphome/components/gps/gps.cpp +++ b/esphome/components/gps/gps.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace gps { -static const char *TAG = "gps"; +static const char *const TAG = "gps"; TinyGPSPlus &GPSListener::get_tiny_gps() { return this->parent_->get_tiny_gps(); } diff --git a/esphome/components/gps/time/gps_time.cpp b/esphome/components/gps/time/gps_time.cpp index 468ad09bac..5352c7e059 100644 --- a/esphome/components/gps/time/gps_time.cpp +++ b/esphome/components/gps/time/gps_time.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace gps { -static const char *TAG = "gps.time"; +static const char *const TAG = "gps.time"; void GPSTime::from_tiny_gps_(TinyGPSPlus &tiny_gps) { if (!tiny_gps.time.isValid() || !tiny_gps.date.isValid()) diff --git a/esphome/components/havells_solar/__init__.py b/esphome/components/havells_solar/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/havells_solar/havells_solar.cpp b/esphome/components/havells_solar/havells_solar.cpp new file mode 100644 index 0000000000..f029df10ad --- /dev/null +++ b/esphome/components/havells_solar/havells_solar.cpp @@ -0,0 +1,165 @@ +#include "havells_solar.h" +#include "havells_solar_registers.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace havells_solar { + +static const char *const TAG = "havells_solar"; + +static const uint8_t MODBUS_CMD_READ_IN_REGISTERS = 0x03; +static const uint8_t MODBUS_REGISTER_COUNT = 48; // 48 x 16-bit registers + +void HavellsSolar::on_modbus_data(const std::vector &data) { + if (data.size() < MODBUS_REGISTER_COUNT * 2) { + ESP_LOGW(TAG, "Invalid size for HavellsSolar!"); + return; + } + + /* Usage: returns the float value of 1 register read by modbus + Arg1: Register address * number of bytes per register + Arg2: Multiplier for final register value + */ + auto havells_solar_get_2_registers = [&](size_t i, float unit) -> float { + uint32_t temp = encode_uint32(data[i], data[i + 1], data[i + 2], data[i + 3]); + return temp * unit; + }; + + /* Usage: returns the float value of 2 registers read by modbus + Arg1: Register address * number of bytes per register + Arg2: Multiplier for final register value + */ + auto havells_solar_get_1_register = [&](size_t i, float unit) -> float { + uint16_t temp = encode_uint16(data[i], data[i + 1]); + return temp * unit; + }; + + for (uint8_t i = 0; i < 3; i++) { + auto phase = this->phases_[i]; + if (!phase.setup) + continue; + + float voltage = havells_solar_get_1_register(HAVELLS_PHASE_1_VOLTAGE * 2 + (i * 4), ONE_DEC_UNIT); + float current = havells_solar_get_1_register(HAVELLS_PHASE_1_CURRENT * 2 + (i * 4), TWO_DEC_UNIT); + + if (phase.voltage_sensor_ != nullptr) + phase.voltage_sensor_->publish_state(voltage); + if (phase.current_sensor_ != nullptr) + phase.current_sensor_->publish_state(current); + } + + for (uint8_t i = 0; i < 2; i++) { + auto pv = this->pvs_[i]; + if (!pv.setup) + continue; + + float voltage = havells_solar_get_1_register(HAVELLS_PV_1_VOLTAGE * 2 + (i * 4), ONE_DEC_UNIT); + float current = havells_solar_get_1_register(HAVELLS_PV_1_CURRENT * 2 + (i * 4), TWO_DEC_UNIT); + float active_power = havells_solar_get_1_register(HAVELLS_PV_1_POWER * 2 + (i * 2), MULTIPLY_TEN_UNIT); + float voltage_sampled_by_secondary_cpu = + havells_solar_get_1_register(HAVELLS_PV1_VOLTAGE_SAMPLED_BY_SECONDARY_CPU * 2 + (i * 2), ONE_DEC_UNIT); + float insulation_of_p_to_ground = + havells_solar_get_1_register(HAVELLS_PV1_INSULATION_OF_P_TO_GROUND * 2 + (i * 2), NO_DEC_UNIT); + + if (pv.voltage_sensor_ != nullptr) + pv.voltage_sensor_->publish_state(voltage); + if (pv.current_sensor_ != nullptr) + pv.current_sensor_->publish_state(current); + if (pv.active_power_sensor_ != nullptr) + pv.active_power_sensor_->publish_state(active_power); + if (pv.voltage_sampled_by_secondary_cpu_sensor_ != nullptr) + pv.voltage_sampled_by_secondary_cpu_sensor_->publish_state(voltage_sampled_by_secondary_cpu); + if (pv.insulation_of_p_to_ground_sensor_ != nullptr) + pv.insulation_of_p_to_ground_sensor_->publish_state(insulation_of_p_to_ground); + } + + float frequency = havells_solar_get_1_register(HAVELLS_GRID_FREQUENCY * 2, TWO_DEC_UNIT); + float active_power = havells_solar_get_1_register(HAVELLS_SYSTEM_ACTIVE_POWER * 2, MULTIPLY_TEN_UNIT); + float reactive_power = havells_solar_get_1_register(HAVELLS_SYSTEM_REACTIVE_POWER * 2, TWO_DEC_UNIT); + float today_production = havells_solar_get_1_register(HAVELLS_TODAY_PRODUCTION * 2, TWO_DEC_UNIT); + float total_energy_production = havells_solar_get_2_registers(HAVELLS_TOTAL_ENERGY_PRODUCTION * 2, NO_DEC_UNIT); + float total_generation_time = havells_solar_get_2_registers(HAVELLS_TOTAL_GENERATION_TIME * 2, NO_DEC_UNIT); + float today_generation_time = havells_solar_get_1_register(HAVELLS_TODAY_GENERATION_TIME * 2, NO_DEC_UNIT); + float inverter_module_temp = havells_solar_get_1_register(HAVELLS_INVERTER_MODULE_TEMP * 2, NO_DEC_UNIT); + float inverter_inner_temp = havells_solar_get_1_register(HAVELLS_INVERTER_INNER_TEMP * 2, NO_DEC_UNIT); + float inverter_bus_voltage = havells_solar_get_1_register(HAVELLS_INVERTER_BUS_VOLTAGE * 2, NO_DEC_UNIT); + float insulation_pv_n_to_ground = havells_solar_get_1_register(HAVELLS_INSULATION_OF_PV_N_TO_GROUND * 2, NO_DEC_UNIT); + float gfci_value = havells_solar_get_1_register(HAVELLS_GFCI_VALUE * 2, NO_DEC_UNIT); + float dci_of_r = havells_solar_get_1_register(HAVELLS_DCI_OF_R * 2, NO_DEC_UNIT); + float dci_of_s = havells_solar_get_1_register(HAVELLS_DCI_OF_S * 2, NO_DEC_UNIT); + float dci_of_t = havells_solar_get_1_register(HAVELLS_DCI_OF_T * 2, NO_DEC_UNIT); + + if (this->frequency_sensor_ != nullptr) + this->frequency_sensor_->publish_state(frequency); + if (this->active_power_sensor_ != nullptr) + this->active_power_sensor_->publish_state(active_power); + if (this->reactive_power_sensor_ != nullptr) + this->reactive_power_sensor_->publish_state(reactive_power); + if (this->today_production_sensor_ != nullptr) + this->today_production_sensor_->publish_state(today_production); + if (this->total_energy_production_sensor_ != nullptr) + this->total_energy_production_sensor_->publish_state(total_energy_production); + if (this->total_generation_time_sensor_ != nullptr) + this->total_generation_time_sensor_->publish_state(total_generation_time); + if (this->today_generation_time_sensor_ != nullptr) + this->today_generation_time_sensor_->publish_state(today_generation_time); + if (this->inverter_module_temp_sensor_ != nullptr) + this->inverter_module_temp_sensor_->publish_state(inverter_module_temp); + if (this->inverter_inner_temp_sensor_ != nullptr) + this->inverter_inner_temp_sensor_->publish_state(inverter_inner_temp); + if (this->inverter_bus_voltage_sensor_ != nullptr) + this->inverter_bus_voltage_sensor_->publish_state(inverter_bus_voltage); + if (this->insulation_pv_n_to_ground_sensor_ != nullptr) + this->insulation_pv_n_to_ground_sensor_->publish_state(insulation_pv_n_to_ground); + if (this->gfci_value_sensor_ != nullptr) + this->gfci_value_sensor_->publish_state(gfci_value); + if (this->dci_of_r_sensor_ != nullptr) + this->dci_of_r_sensor_->publish_state(dci_of_r); + if (this->dci_of_s_sensor_ != nullptr) + this->dci_of_s_sensor_->publish_state(dci_of_s); + if (this->dci_of_t_sensor_ != nullptr) + this->dci_of_t_sensor_->publish_state(dci_of_t); +} + +void HavellsSolar::update() { this->send(MODBUS_CMD_READ_IN_REGISTERS, 0, MODBUS_REGISTER_COUNT); } +void HavellsSolar::dump_config() { + ESP_LOGCONFIG(TAG, "HAVELLS Solar:"); + ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_); + for (uint8_t i = 0; i < 3; i++) { + auto phase = this->phases_[i]; + if (!phase.setup) + continue; + ESP_LOGCONFIG(TAG, " Phase %c", i + 'A'); + LOG_SENSOR(" ", "Voltage", phase.voltage_sensor_); + LOG_SENSOR(" ", "Current", phase.current_sensor_); + } + for (uint8_t i = 0; i < 2; i++) { + auto pv = this->pvs_[i]; + if (!pv.setup) + continue; + ESP_LOGCONFIG(TAG, " PV %d", i + 1); + LOG_SENSOR(" ", "Voltage", pv.voltage_sensor_); + LOG_SENSOR(" ", "Current", pv.current_sensor_); + LOG_SENSOR(" ", "Active Power", pv.active_power_sensor_); + LOG_SENSOR(" ", "Voltage Sampled By Secondary CPU", pv.voltage_sampled_by_secondary_cpu_sensor_); + LOG_SENSOR(" ", "Insulation Of PV+ To Ground", pv.insulation_of_p_to_ground_sensor_); + } + LOG_SENSOR(" ", "Frequency", this->frequency_sensor_); + LOG_SENSOR(" ", "Active Power", this->active_power_sensor_); + LOG_SENSOR(" ", "Reactive Power", this->reactive_power_sensor_); + LOG_SENSOR(" ", "Today Generation", this->today_production_sensor_); + LOG_SENSOR(" ", "Total Generation", this->total_energy_production_sensor_); + LOG_SENSOR(" ", "Total Generation Time", this->total_generation_time_sensor_); + LOG_SENSOR(" ", "Today Generation Time", this->today_generation_time_sensor_); + LOG_SENSOR(" ", "Inverter Module Temp", this->inverter_module_temp_sensor_); + LOG_SENSOR(" ", "Inverter Inner Temp", this->inverter_inner_temp_sensor_); + LOG_SENSOR(" ", "Inverter Bus Voltage", this->inverter_bus_voltage_sensor_); + LOG_SENSOR(" ", "Insulation Of PV- To Ground", this->insulation_pv_n_to_ground_sensor_); + LOG_SENSOR(" ", "GFCI Value", this->gfci_value_sensor_); + LOG_SENSOR(" ", "DCI Of R", this->dci_of_r_sensor_); + LOG_SENSOR(" ", "DCI Of S", this->dci_of_s_sensor_); + LOG_SENSOR(" ", "DCI Of T", this->dci_of_t_sensor_); +} + +} // namespace havells_solar +} // namespace esphome diff --git a/esphome/components/havells_solar/havells_solar.h b/esphome/components/havells_solar/havells_solar.h new file mode 100644 index 0000000000..2ccc8be3d4 --- /dev/null +++ b/esphome/components/havells_solar/havells_solar.h @@ -0,0 +1,115 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/modbus/modbus.h" + +namespace esphome { +namespace havells_solar { + +class HavellsSolar : public PollingComponent, public modbus::ModbusDevice { + public: + void set_voltage_sensor(uint8_t phase, sensor::Sensor *voltage_sensor) { + this->phases_[phase].setup = true; + this->phases_[phase].voltage_sensor_ = voltage_sensor; + } + void set_current_sensor(uint8_t phase, sensor::Sensor *current_sensor) { + this->phases_[phase].setup = true; + this->phases_[phase].current_sensor_ = current_sensor; + } + void set_voltage_sensor_pv(uint8_t pv, sensor::Sensor *voltage_sensor) { + this->pvs_[pv].setup = true; + this->pvs_[pv].voltage_sensor_ = voltage_sensor; + } + void set_current_sensor_pv(uint8_t pv, sensor::Sensor *current_sensor) { + this->pvs_[pv].setup = true; + this->pvs_[pv].current_sensor_ = current_sensor; + } + void set_active_power_sensor_pv(uint8_t pv, sensor::Sensor *active_power_sensor) { + this->pvs_[pv].setup = true; + this->pvs_[pv].active_power_sensor_ = active_power_sensor; + } + void set_voltage_sampled_by_secondary_cpu_sensor_pv(uint8_t pv, + sensor::Sensor *voltage_sampled_by_secondary_cpu_sensor) { + this->pvs_[pv].setup = true; + this->pvs_[pv].voltage_sampled_by_secondary_cpu_sensor_ = voltage_sampled_by_secondary_cpu_sensor; + } + void set_insulation_of_p_to_ground_sensor_pv(uint8_t pv, sensor::Sensor *insulation_of_p_to_ground_sensor) { + this->pvs_[pv].setup = true; + this->pvs_[pv].insulation_of_p_to_ground_sensor_ = insulation_of_p_to_ground_sensor; + } + void set_frequency_sensor(sensor::Sensor *frequency_sensor) { this->frequency_sensor_ = frequency_sensor; } + void set_active_power_sensor(sensor::Sensor *active_power_sensor) { + this->active_power_sensor_ = active_power_sensor; + } + void set_reactive_power_sensor(sensor::Sensor *reactive_power_sensor) { + this->reactive_power_sensor_ = reactive_power_sensor; + } + void set_today_production_sensor(sensor::Sensor *today_production_sensor) { + this->today_production_sensor_ = today_production_sensor; + } + void set_total_energy_production_sensor(sensor::Sensor *total_energy_production_sensor) { + this->total_energy_production_sensor_ = total_energy_production_sensor; + } + void set_total_generation_time_sensor(sensor::Sensor *total_generation_time_sensor) { + this->total_generation_time_sensor_ = total_generation_time_sensor; + } + void set_today_generation_time_sensor(sensor::Sensor *today_generation_time_sensor) { + this->today_generation_time_sensor_ = today_generation_time_sensor; + } + void set_inverter_module_temp_sensor(sensor::Sensor *inverter_module_temp_sensor) { + this->inverter_module_temp_sensor_ = inverter_module_temp_sensor; + } + void set_inverter_inner_temp_sensor(sensor::Sensor *inverter_inner_temp_sensor) { + this->inverter_inner_temp_sensor_ = inverter_inner_temp_sensor; + } + void set_inverter_bus_voltage_sensor(sensor::Sensor *inverter_bus_voltage_sensor) { + this->inverter_bus_voltage_sensor_ = inverter_bus_voltage_sensor; + } + void set_insulation_pv_n_to_ground_sensor(sensor::Sensor *insulation_pv_n_to_ground_sensor) { + this->insulation_pv_n_to_ground_sensor_ = insulation_pv_n_to_ground_sensor; + } + void set_gfci_value_sensor(sensor::Sensor *gfci_value_sensor) { this->gfci_value_sensor_ = gfci_value_sensor; } + void set_dci_of_r_sensor(sensor::Sensor *dci_of_r_sensor) { this->dci_of_r_sensor_ = dci_of_r_sensor; } + void set_dci_of_s_sensor(sensor::Sensor *dci_of_s_sensor) { this->dci_of_s_sensor_ = dci_of_s_sensor; } + void set_dci_of_t_sensor(sensor::Sensor *dci_of_t_sensor) { this->dci_of_t_sensor_ = dci_of_t_sensor; } + + void update() override; + + void on_modbus_data(const std::vector &data) override; + + void dump_config() override; + + protected: + struct HAVELLSPhase { + bool setup{false}; + sensor::Sensor *voltage_sensor_{nullptr}; + sensor::Sensor *current_sensor_{nullptr}; + } phases_[3]; + struct HAVELLSPV { + bool setup{false}; + sensor::Sensor *voltage_sensor_{nullptr}; + sensor::Sensor *current_sensor_{nullptr}; + sensor::Sensor *active_power_sensor_{nullptr}; + sensor::Sensor *voltage_sampled_by_secondary_cpu_sensor_{nullptr}; + sensor::Sensor *insulation_of_p_to_ground_sensor_{nullptr}; + } pvs_[2]; + sensor::Sensor *frequency_sensor_{nullptr}; + sensor::Sensor *active_power_sensor_{nullptr}; + sensor::Sensor *reactive_power_sensor_{nullptr}; + sensor::Sensor *today_production_sensor_{nullptr}; + sensor::Sensor *total_energy_production_sensor_{nullptr}; + sensor::Sensor *total_generation_time_sensor_{nullptr}; + sensor::Sensor *today_generation_time_sensor_{nullptr}; + sensor::Sensor *inverter_module_temp_sensor_{nullptr}; + sensor::Sensor *inverter_inner_temp_sensor_{nullptr}; + sensor::Sensor *inverter_bus_voltage_sensor_{nullptr}; + sensor::Sensor *insulation_pv_n_to_ground_sensor_{nullptr}; + sensor::Sensor *gfci_value_sensor_{nullptr}; + sensor::Sensor *dci_of_r_sensor_{nullptr}; + sensor::Sensor *dci_of_s_sensor_{nullptr}; + sensor::Sensor *dci_of_t_sensor_{nullptr}; +}; + +} // namespace havells_solar +} // namespace esphome diff --git a/esphome/components/havells_solar/havells_solar_registers.h b/esphome/components/havells_solar/havells_solar_registers.h new file mode 100644 index 0000000000..8e1cb3ec7a --- /dev/null +++ b/esphome/components/havells_solar/havells_solar_registers.h @@ -0,0 +1,49 @@ +#pragma once +namespace esphome { +namespace havells_solar { + +static const float TWO_DEC_UNIT = 0.01; +static const float ONE_DEC_UNIT = 0.1; +static const float NO_DEC_UNIT = 1; +static const float MULTIPLY_TEN_UNIT = 10; + +/* PV Input Message */ +static const uint16_t HAVELLS_PV_1_VOLTAGE = 0x0006; +static const uint16_t HAVELLS_PV_1_CURRENT = 0x0007; +static const uint16_t HAVELLS_PV_2_VOLTAGE = 0x0008; +static const uint16_t HAVELLS_PV_2_CURRENT = 0x0009; +static const uint16_t HAVELLS_PV_1_POWER = 0x000A; +static const uint16_t HAVELLS_PV_2_POWER = 0x000B; + +/* Output Grid Message */ +static const uint16_t HAVELLS_SYSTEM_ACTIVE_POWER = 0x000C; +static const uint16_t HAVELLS_SYSTEM_REACTIVE_POWER = 0x000D; +static const uint16_t HAVELLS_GRID_FREQUENCY = 0x000E; +static const uint16_t HAVELLS_PHASE_1_VOLTAGE = 0x000F; +static const uint16_t HAVELLS_PHASE_1_CURRENT = 0x0010; +static const uint16_t HAVELLS_PHASE_2_VOLTAGE = 0x0011; +static const uint16_t HAVELLS_PHASE_2_CURRENT = 0x0012; +static const uint16_t HAVELLS_PHASE_3_VOLTAGE = 0x0013; +static const uint16_t HAVELLS_PHASE_3_CURRENT = 0x0014; + +/* Inverter Generation message */ +static const uint16_t HAVELLS_TOTAL_ENERGY_PRODUCTION = 0x0015; +static const uint16_t HAVELLS_TOTAL_GENERATION_TIME = 0x0017; +static const uint16_t HAVELLS_TODAY_PRODUCTION = 0x0019; +static const uint16_t HAVELLS_TODAY_GENERATION_TIME = 0x001A; + +/* Inverter inner message */ +static const uint16_t HAVELLS_INVERTER_MODULE_TEMP = 0x001B; +static const uint16_t HAVELLS_INVERTER_INNER_TEMP = 0x001C; +static const uint16_t HAVELLS_INVERTER_BUS_VOLTAGE = 0x001D; +static const uint16_t HAVELLS_PV1_VOLTAGE_SAMPLED_BY_SECONDARY_CPU = 0x001E; +static const uint16_t HAVELLS_PV2_VOLTAGE_SAMPLED_BY_SECONDARY_CPU = 0x001F; +static const uint16_t HAVELLS_PV1_INSULATION_OF_P_TO_GROUND = 0x0024; +static const uint16_t HAVELLS_PV2_INSULATION_OF_P_TO_GROUND = 0x0025; +static const uint16_t HAVELLS_INSULATION_OF_PV_N_TO_GROUND = 0x0026; +static const uint16_t HAVELLS_GFCI_VALUE = 0x002A; +static const uint16_t HAVELLS_DCI_OF_R = 0x002B; +static const uint16_t HAVELLS_DCI_OF_S = 0x002C; +static const uint16_t HAVELLS_DCI_OF_T = 0x002D; +} // namespace havells_solar +} // namespace esphome diff --git a/esphome/components/havells_solar/sensor.py b/esphome/components/havells_solar/sensor.py new file mode 100644 index 0000000000..1926d4d68a --- /dev/null +++ b/esphome/components/havells_solar/sensor.py @@ -0,0 +1,296 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor, modbus +from esphome.const import ( + CONF_ACTIVE_POWER, + CONF_CURRENT, + CONF_FREQUENCY, + CONF_ID, + CONF_REACTIVE_POWER, + CONF_VOLTAGE, + DEVICE_CLASS_CURRENT, + DEVICE_CLASS_EMPTY, + DEVICE_CLASS_ENERGY, + DEVICE_CLASS_POWER, + DEVICE_CLASS_VOLTAGE, + ICON_CURRENT_AC, + ICON_EMPTY, + LAST_RESET_TYPE_AUTO, + STATE_CLASS_MEASUREMENT, + STATE_CLASS_NONE, + UNIT_AMPERE, + UNIT_DEGREES, + UNIT_HERTZ, + UNIT_MINUTE, + UNIT_VOLT, + UNIT_VOLT_AMPS_REACTIVE, + UNIT_WATT, +) + +CONF_PHASE_A = "phase_a" +CONF_PHASE_B = "phase_b" +CONF_PHASE_C = "phase_c" +CONF_ENERGY_PRODUCTION_DAY = "energy_production_day" +CONF_TOTAL_ENERGY_PRODUCTION = "total_energy_production" +CONF_TOTAL_GENERATION_TIME = "total_generation_time" +CONF_TODAY_GENERATION_TIME = "today_generation_time" +CONF_PV1 = "pv1" +CONF_PV2 = "pv2" +UNIT_KILOWATT_HOURS = "kWh" +UNIT_HOURS = "h" +UNIT_KOHM = "kΩ" +UNIT_MILLIAMPERE = "mA" + + +CONF_INVERTER_MODULE_TEMP = "inverter_module_temp" +CONF_INVERTER_INNER_TEMP = "inverter_inner_temp" +CONF_INVERTER_BUS_VOLTAGE = "inverter_bus_voltage" +CONF_VOLTAGE_SAMPLED_BY_SECONDARY_CPU = "voltage_sampled_by_secondary_cpu" +CONF_INSULATION_OF_P_TO_GROUND = "insulation_of_p_to_ground" +CONF_INSULATION_OF_PV_N_TO_GROUND = "insulation_of_pv_n_to_ground" +CONF_GFCI_VALUE = "gfci_value" +CONF_DCI_OF_R = "dci_of_r" +CONF_DCI_OF_S = "dci_of_s" +CONF_DCI_OF_T = "dci_of_t" + + +AUTO_LOAD = ["modbus"] +CODEOWNERS = ["@sourabhjaiswal"] + +havells_solar_ns = cg.esphome_ns.namespace("havells_solar") +HavellsSolar = havells_solar_ns.class_( + "HavellsSolar", cg.PollingComponent, modbus.ModbusDevice +) + +PHASE_SENSORS = { + CONF_VOLTAGE: sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE), + CONF_CURRENT: sensor.sensor_schema( + UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT, STATE_CLASS_MEASUREMENT + ), +} +PV_SENSORS = { + CONF_VOLTAGE: sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE), + CONF_CURRENT: sensor.sensor_schema( + UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT, STATE_CLASS_MEASUREMENT + ), + CONF_ACTIVE_POWER: sensor.sensor_schema( + UNIT_WATT, ICON_EMPTY, 0, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT + ), + CONF_VOLTAGE_SAMPLED_BY_SECONDARY_CPU: sensor.sensor_schema( + UNIT_VOLT, ICON_EMPTY, 0, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT + ), + CONF_INSULATION_OF_P_TO_GROUND: sensor.sensor_schema( + UNIT_KOHM, ICON_EMPTY, 0, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT + ), +} + +PHASE_SCHEMA = cv.Schema( + {cv.Optional(sensor): schema for sensor, schema in PHASE_SENSORS.items()} +) +PV_SCHEMA = cv.Schema( + {cv.Optional(sensor): schema for sensor, schema in PV_SENSORS.items()} +) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(HavellsSolar), + cv.Optional(CONF_PHASE_A): PHASE_SCHEMA, + cv.Optional(CONF_PHASE_B): PHASE_SCHEMA, + cv.Optional(CONF_PHASE_C): PHASE_SCHEMA, + cv.Optional(CONF_PV1): PV_SCHEMA, + cv.Optional(CONF_PV2): PV_SCHEMA, + cv.Optional(CONF_FREQUENCY): sensor.sensor_schema( + UNIT_HERTZ, + ICON_CURRENT_AC, + 2, + DEVICE_CLASS_EMPTY, + STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_ACTIVE_POWER): sensor.sensor_schema( + UNIT_WATT, ICON_EMPTY, 0, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT + ), + cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema( + UNIT_VOLT_AMPS_REACTIVE, + ICON_EMPTY, + 2, + DEVICE_CLASS_POWER, + STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_ENERGY_PRODUCTION_DAY): sensor.sensor_schema( + UNIT_KILOWATT_HOURS, + ICON_EMPTY, + 2, + DEVICE_CLASS_ENERGY, + STATE_CLASS_MEASUREMENT, + LAST_RESET_TYPE_AUTO, + ), + cv.Optional(CONF_TOTAL_ENERGY_PRODUCTION): sensor.sensor_schema( + UNIT_KILOWATT_HOURS, + ICON_EMPTY, + 0, + DEVICE_CLASS_ENERGY, + STATE_CLASS_MEASUREMENT, + LAST_RESET_TYPE_AUTO, + ), + cv.Optional(CONF_TOTAL_GENERATION_TIME): sensor.sensor_schema( + UNIT_HOURS, + ICON_EMPTY, + 0, + DEVICE_CLASS_EMPTY, + STATE_CLASS_NONE, + ), + cv.Optional(CONF_TODAY_GENERATION_TIME): sensor.sensor_schema( + UNIT_MINUTE, + ICON_EMPTY, + 0, + DEVICE_CLASS_EMPTY, + STATE_CLASS_NONE, + ), + cv.Optional(CONF_INVERTER_MODULE_TEMP): sensor.sensor_schema( + UNIT_DEGREES, + ICON_EMPTY, + 0, + DEVICE_CLASS_EMPTY, + STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_INVERTER_INNER_TEMP): sensor.sensor_schema( + UNIT_DEGREES, + ICON_EMPTY, + 0, + DEVICE_CLASS_EMPTY, + STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_INVERTER_BUS_VOLTAGE): sensor.sensor_schema( + UNIT_VOLT, + ICON_EMPTY, + 0, + DEVICE_CLASS_EMPTY, + STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_INSULATION_OF_PV_N_TO_GROUND): sensor.sensor_schema( + UNIT_KOHM, + ICON_EMPTY, + 0, + DEVICE_CLASS_EMPTY, + STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_GFCI_VALUE): sensor.sensor_schema( + UNIT_MILLIAMPERE, + ICON_EMPTY, + 0, + DEVICE_CLASS_EMPTY, + STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_DCI_OF_R): sensor.sensor_schema( + UNIT_MILLIAMPERE, + ICON_EMPTY, + 0, + DEVICE_CLASS_EMPTY, + STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_DCI_OF_S): sensor.sensor_schema( + UNIT_MILLIAMPERE, + ICON_EMPTY, + 0, + DEVICE_CLASS_EMPTY, + STATE_CLASS_MEASUREMENT, + ), + cv.Optional(CONF_DCI_OF_T): sensor.sensor_schema( + UNIT_MILLIAMPERE, + ICON_EMPTY, + 0, + DEVICE_CLASS_EMPTY, + STATE_CLASS_MEASUREMENT, + ), + } + ) + .extend(cv.polling_component_schema("10s")) + .extend(modbus.modbus_device_schema(0x01)) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await modbus.register_modbus_device(var, config) + + if CONF_FREQUENCY in config: + sens = await sensor.new_sensor(config[CONF_FREQUENCY]) + cg.add(var.set_frequency_sensor(sens)) + + if CONF_ACTIVE_POWER in config: + sens = await sensor.new_sensor(config[CONF_ACTIVE_POWER]) + cg.add(var.set_active_power_sensor(sens)) + + if CONF_REACTIVE_POWER in config: + sens = await sensor.new_sensor(config[CONF_REACTIVE_POWER]) + cg.add(var.set_reactive_power_sensor(sens)) + + if CONF_ENERGY_PRODUCTION_DAY in config: + sens = await sensor.new_sensor(config[CONF_ENERGY_PRODUCTION_DAY]) + cg.add(var.set_today_production_sensor(sens)) + + if CONF_TOTAL_ENERGY_PRODUCTION in config: + sens = await sensor.new_sensor(config[CONF_TOTAL_ENERGY_PRODUCTION]) + cg.add(var.set_total_energy_production_sensor(sens)) + + if CONF_TOTAL_GENERATION_TIME in config: + sens = await sensor.new_sensor(config[CONF_TOTAL_GENERATION_TIME]) + cg.add(var.set_total_generation_time_sensor(sens)) + + if CONF_TODAY_GENERATION_TIME in config: + sens = await sensor.new_sensor(config[CONF_TODAY_GENERATION_TIME]) + cg.add(var.set_today_generation_time_sensor(sens)) + + if CONF_INVERTER_MODULE_TEMP in config: + sens = await sensor.new_sensor(config[CONF_INVERTER_MODULE_TEMP]) + cg.add(var.set_inverter_module_temp_sensor(sens)) + + if CONF_INVERTER_INNER_TEMP in config: + sens = await sensor.new_sensor(config[CONF_INVERTER_INNER_TEMP]) + cg.add(var.set_inverter_inner_temp_sensor(sens)) + + if CONF_INVERTER_BUS_VOLTAGE in config: + sens = await sensor.new_sensor(config[CONF_INVERTER_BUS_VOLTAGE]) + cg.add(var.set_inverter_bus_voltage_sensor(sens)) + + if CONF_INSULATION_OF_PV_N_TO_GROUND in config: + sens = await sensor.new_sensor(config[CONF_INSULATION_OF_PV_N_TO_GROUND]) + cg.add(var.set_insulation_pv_n_to_ground_sensor(sens)) + + if CONF_GFCI_VALUE in config: + sens = await sensor.new_sensor(config[CONF_GFCI_VALUE]) + cg.add(var.set_gfci_value_sensor(sens)) + + if CONF_DCI_OF_R in config: + sens = await sensor.new_sensor(config[CONF_DCI_OF_R]) + cg.add(var.set_dci_of_r_sensor(sens)) + + if CONF_DCI_OF_S in config: + sens = await sensor.new_sensor(config[CONF_DCI_OF_S]) + cg.add(var.set_dci_of_s_sensor(sens)) + + if CONF_DCI_OF_T in config: + sens = await sensor.new_sensor(config[CONF_DCI_OF_T]) + cg.add(var.set_dci_of_t_sensor(sens)) + + for i, phase in enumerate([CONF_PHASE_A, CONF_PHASE_B, CONF_PHASE_C]): + if phase not in config: + continue + + phase_config = config[phase] + for sensor_type in PHASE_SENSORS: + if sensor_type in phase_config: + sens = await sensor.new_sensor(phase_config[sensor_type]) + cg.add(getattr(var, f"set_{sensor_type}_sensor")(i, sens)) + + for i, pv in enumerate([CONF_PV1, CONF_PV2]): + if pv not in config: + continue + + pv_config = config[pv] + for sensor_type in pv_config: + if sensor_type in pv_config: + sens = await sensor.new_sensor(pv_config[sensor_type]) + cg.add(getattr(var, f"set_{sensor_type}_sensor_pv")(i, sens)) diff --git a/esphome/components/hdc1080/hdc1080.cpp b/esphome/components/hdc1080/hdc1080.cpp index 915c44b155..507ac77a28 100644 --- a/esphome/components/hdc1080/hdc1080.cpp +++ b/esphome/components/hdc1080/hdc1080.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace hdc1080 { -static const char *TAG = "hdc1080"; +static const char *const TAG = "hdc1080"; static const uint8_t HDC1080_ADDRESS = 0x40; // 0b1000000 from datasheet static const uint8_t HDC1080_CMD_CONFIGURATION = 0x02; diff --git a/esphome/components/hitachi_ac344/hitachi_ac344.cpp b/esphome/components/hitachi_ac344/hitachi_ac344.cpp index 86f82d8bbb..1e5bca1396 100644 --- a/esphome/components/hitachi_ac344/hitachi_ac344.cpp +++ b/esphome/components/hitachi_ac344/hitachi_ac344.cpp @@ -3,7 +3,7 @@ namespace esphome { namespace hitachi_ac344 { -static const char *TAG = "climate.hitachi_ac344"; +static const char *const TAG = "climate.hitachi_ac344"; void set_bits(uint8_t *const dst, const uint8_t offset, const uint8_t nbits, const uint8_t data) { if (offset >= 8 || !nbits) diff --git a/esphome/components/hitachi_ac344/hitachi_ac344.h b/esphome/components/hitachi_ac344/hitachi_ac344.h index 1d0719f11b..c34f033d92 100644 --- a/esphome/components/hitachi_ac344/hitachi_ac344.h +++ b/esphome/components/hitachi_ac344/hitachi_ac344.h @@ -73,7 +73,7 @@ const uint8_t HITACHI_AC344_MILDEWPROOF_OFFSET = 2; // Mask 0b00000x00 const uint16_t HITACHI_AC344_STATE_LENGTH = 43; const uint16_t HITACHI_AC344_BITS = HITACHI_AC344_STATE_LENGTH * 8; -#define GETBIT8(a, b) (a & ((uint8_t) 1 << b)) +#define GETBIT8(a, b) ((a) & ((uint8_t) 1 << (b))) #define GETBITS8(data, offset, size) (((data) & (((uint8_t) UINT8_MAX >> (8 - (size))) << (offset))) >> (offset)) class HitachiClimate : public climate_ir::ClimateIR { diff --git a/esphome/components/hlw8012/hlw8012.cpp b/esphome/components/hlw8012/hlw8012.cpp index 379dbe578e..79c25a45b0 100644 --- a/esphome/components/hlw8012/hlw8012.cpp +++ b/esphome/components/hlw8012/hlw8012.cpp @@ -4,17 +4,34 @@ namespace esphome { namespace hlw8012 { -static const char *TAG = "hlw8012"; +static const char *const TAG = "hlw8012"; +// valid for HLW8012 and CSE7759 static const uint32_t HLW8012_CLOCK_FREQUENCY = 3579000; -static const float HLW8012_REFERENCE_VOLTAGE = 2.43f; void HLW8012Component::setup() { + float reference_voltage = 0; ESP_LOGCONFIG(TAG, "Setting up HLW8012..."); this->sel_pin_->setup(); this->sel_pin_->digital_write(this->current_mode_); this->cf_store_.pulse_counter_setup(this->cf_pin_); this->cf1_store_.pulse_counter_setup(this->cf1_pin_); + + // Initialize multipliers + if (this->sensor_model_ == HLW8012_SENSOR_MODEL_BL0937) { + reference_voltage = 1.218f; + this->power_multiplier_ = + reference_voltage * reference_voltage * this->voltage_divider_ / this->current_resistor_ / 1721506.0f; + this->current_multiplier_ = reference_voltage / this->current_resistor_ / 94638.0f; + this->voltage_multiplier_ = reference_voltage * this->voltage_divider_ / 15397.0f; + } else { + // HLW8012 and CSE7759 have same reference specs + reference_voltage = 2.43f; + this->power_multiplier_ = reference_voltage * reference_voltage * this->voltage_divider_ / this->current_resistor_ * + 64.0f / 24.0f / HLW8012_CLOCK_FREQUENCY; + this->current_multiplier_ = reference_voltage / this->current_resistor_ * 512.0f / 24.0f / HLW8012_CLOCK_FREQUENCY; + this->voltage_multiplier_ = reference_voltage * this->voltage_divider_ * 256.0f / HLW8012_CLOCK_FREQUENCY; + } } void HLW8012Component::dump_config() { ESP_LOGCONFIG(TAG, "HLW8012:"); @@ -49,25 +66,18 @@ void HLW8012Component::update() { return; } - const float v_ref_squared = HLW8012_REFERENCE_VOLTAGE * HLW8012_REFERENCE_VOLTAGE; - const float power_multiplier_micros = - 64000000.0f * v_ref_squared * this->voltage_divider_ / this->current_resistor_ / 24.0f / HLW8012_CLOCK_FREQUENCY; - float power = cf_hz * power_multiplier_micros / 1000000.0f; + float power = cf_hz * this->power_multiplier_; if (this->change_mode_at_ != 0) { // Only read cf1 after one cycle. Apparently it's quite unstable after being changed. if (this->current_mode_) { - const float current_multiplier_micros = - 512000000.0f * HLW8012_REFERENCE_VOLTAGE / this->current_resistor_ / 24.0f / HLW8012_CLOCK_FREQUENCY; - float current = cf1_hz * current_multiplier_micros / 1000000.0f; + float current = cf1_hz * this->current_multiplier_; ESP_LOGD(TAG, "Got power=%.1fW, current=%.1fA", power, current); if (this->current_sensor_ != nullptr) { this->current_sensor_->publish_state(current); } } else { - const float voltage_multiplier_micros = - 256000000.0f * HLW8012_REFERENCE_VOLTAGE * this->voltage_divider_ / HLW8012_CLOCK_FREQUENCY; - float voltage = cf1_hz * voltage_multiplier_micros / 1000000.0f; + float voltage = cf1_hz * this->voltage_multiplier_; ESP_LOGD(TAG, "Got power=%.1fW, voltage=%.1fV", power, voltage); if (this->voltage_sensor_ != nullptr) { this->voltage_sensor_->publish_state(voltage); @@ -81,7 +91,7 @@ void HLW8012Component::update() { if (this->energy_sensor_ != nullptr) { cf_total_pulses_ += raw_cf; - float energy = cf_total_pulses_ * power_multiplier_micros / 3600 / 1000000.0f; + float energy = cf_total_pulses_ * this->power_multiplier_ / 3600; this->energy_sensor_->publish_state(energy); } diff --git a/esphome/components/hlw8012/hlw8012.h b/esphome/components/hlw8012/hlw8012.h index af1f2e9a8c..52fb03c020 100644 --- a/esphome/components/hlw8012/hlw8012.h +++ b/esphome/components/hlw8012/hlw8012.h @@ -10,6 +10,12 @@ namespace hlw8012 { enum HLW8012InitialMode { HLW8012_INITIAL_MODE_CURRENT = 0, HLW8012_INITIAL_MODE_VOLTAGE }; +enum HLW8012SensorModels { + HLW8012_SENSOR_MODEL_HLW8012 = 0, + HLW8012_SENSOR_MODEL_CSE7759, + HLW8012_SENSOR_MODEL_BL0937 +}; + class HLW8012Component : public PollingComponent { public: void setup() override; @@ -20,6 +26,7 @@ class HLW8012Component : public PollingComponent { void set_initial_mode(HLW8012InitialMode initial_mode) { current_mode_ = initial_mode == HLW8012_INITIAL_MODE_CURRENT; } + void set_sensor_model(HLW8012SensorModels sensor_model) { sensor_model_ = sensor_model; } void set_change_mode_every(uint32_t change_mode_every) { change_mode_every_ = change_mode_every; } void set_current_resistor(float current_resistor) { current_resistor_ = current_resistor; } void set_voltage_divider(float voltage_divider) { voltage_divider_ = voltage_divider; } @@ -38,6 +45,7 @@ class HLW8012Component : public PollingComponent { uint32_t change_mode_every_{8}; float current_resistor_{0.001}; float voltage_divider_{2351}; + HLW8012SensorModels sensor_model_{HLW8012_SENSOR_MODEL_HLW8012}; uint64_t cf_total_pulses_{0}; GPIOPin *sel_pin_; GPIOPin *cf_pin_; @@ -48,6 +56,10 @@ class HLW8012Component : public PollingComponent { sensor::Sensor *current_sensor_{nullptr}; sensor::Sensor *power_sensor_{nullptr}; sensor::Sensor *energy_sensor_{nullptr}; + + float voltage_multiplier_{0.0f}; + float current_multiplier_{0.0f}; + float power_multiplier_{0.0f}; }; } // namespace hlw8012 diff --git a/esphome/components/hlw8012/sensor.py b/esphome/components/hlw8012/sensor.py index 6454a9fcc9..face32872e 100644 --- a/esphome/components/hlw8012/sensor.py +++ b/esphome/components/hlw8012/sensor.py @@ -11,6 +11,7 @@ from esphome.const import ( CONF_POWER, CONF_ENERGY, CONF_SEL_PIN, + CONF_MODEL, CONF_VOLTAGE, CONF_VOLTAGE_DIVIDER, DEVICE_CLASS_CURRENT, @@ -18,8 +19,8 @@ from esphome.const import ( DEVICE_CLASS_POWER, DEVICE_CLASS_VOLTAGE, ICON_EMPTY, + LAST_RESET_TYPE_AUTO, STATE_CLASS_MEASUREMENT, - STATE_CLASS_NONE, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT, @@ -31,11 +32,19 @@ AUTO_LOAD = ["pulse_counter"] hlw8012_ns = cg.esphome_ns.namespace("hlw8012") HLW8012Component = hlw8012_ns.class_("HLW8012Component", cg.PollingComponent) HLW8012InitialMode = hlw8012_ns.enum("HLW8012InitialMode") +HLW8012SensorModels = hlw8012_ns.enum("HLW8012SensorModels") + INITIAL_MODES = { CONF_CURRENT: HLW8012InitialMode.HLW8012_INITIAL_MODE_CURRENT, CONF_VOLTAGE: HLW8012InitialMode.HLW8012_INITIAL_MODE_VOLTAGE, } +MODELS = { + "HLW8012": HLW8012SensorModels.HLW8012_SENSOR_MODEL_HLW8012, + "CSE7759": HLW8012SensorModels.HLW8012_SENSOR_MODEL_CSE7759, + "BL0937": HLW8012SensorModels.HLW8012_SENSOR_MODEL_BL0937, +} + CONF_CF1_PIN = "cf1_pin" CONF_CF_PIN = "cf_pin" CONFIG_SCHEMA = cv.Schema( @@ -58,10 +67,16 @@ CONFIG_SCHEMA = cv.Schema( UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT ), cv.Optional(CONF_ENERGY): sensor.sensor_schema( - UNIT_WATT_HOURS, ICON_EMPTY, 1, DEVICE_CLASS_ENERGY, STATE_CLASS_NONE + UNIT_WATT_HOURS, + ICON_EMPTY, + 1, + DEVICE_CLASS_ENERGY, + STATE_CLASS_MEASUREMENT, + LAST_RESET_TYPE_AUTO, ), cv.Optional(CONF_CURRENT_RESISTOR, default=0.001): cv.resistance, cv.Optional(CONF_VOLTAGE_DIVIDER, default=2351): cv.positive_float, + cv.Optional(CONF_MODEL, default="HLW8012"): cv.enum(MODELS, upper=True), cv.Optional(CONF_CHANGE_MODE_EVERY, default=8): cv.All( cv.uint32_t, cv.Range(min=1) ), @@ -99,3 +114,4 @@ async def to_code(config): cg.add(var.set_voltage_divider(config[CONF_VOLTAGE_DIVIDER])) cg.add(var.set_change_mode_every(config[CONF_CHANGE_MODE_EVERY])) cg.add(var.set_initial_mode(INITIAL_MODES[config[CONF_INITIAL_MODE]])) + cg.add(var.set_sensor_model(config[CONF_MODEL])) diff --git a/esphome/components/hm3301/abstract_aqi_calculator.h b/esphome/components/hm3301/abstract_aqi_calculator.h index f160a91148..f2573ff108 100644 --- a/esphome/components/hm3301/abstract_aqi_calculator.h +++ b/esphome/components/hm3301/abstract_aqi_calculator.h @@ -1,7 +1,5 @@ #pragma once -#include "Arduino.h" - namespace esphome { namespace hm3301 { diff --git a/esphome/components/hm3301/aqi_calculator.cpp b/esphome/components/hm3301/aqi_calculator.h similarity index 99% rename from esphome/components/hm3301/aqi_calculator.cpp rename to esphome/components/hm3301/aqi_calculator.h index 41e538a399..627ee686fc 100644 --- a/esphome/components/hm3301/aqi_calculator.cpp +++ b/esphome/components/hm3301/aqi_calculator.h @@ -1,3 +1,5 @@ +#pragma once + #include "abstract_aqi_calculator.h" namespace esphome { diff --git a/esphome/components/hm3301/aqi_calculator_factory.h b/esphome/components/hm3301/aqi_calculator_factory.h index 483a158822..55608b6e51 100644 --- a/esphome/components/hm3301/aqi_calculator_factory.h +++ b/esphome/components/hm3301/aqi_calculator_factory.h @@ -1,8 +1,7 @@ #pragma once -#include "Arduino.h" -#include "caqi_calculator.cpp" -#include "aqi_calculator.cpp" +#include "caqi_calculator.h" +#include "aqi_calculator.h" namespace esphome { namespace hm3301 { diff --git a/esphome/components/hm3301/caqi_calculator.cpp b/esphome/components/hm3301/caqi_calculator.h similarity index 99% rename from esphome/components/hm3301/caqi_calculator.cpp rename to esphome/components/hm3301/caqi_calculator.h index 511179cb71..403bac2713 100644 --- a/esphome/components/hm3301/caqi_calculator.cpp +++ b/esphome/components/hm3301/caqi_calculator.h @@ -1,3 +1,5 @@ +#pragma once + #include "esphome/core/log.h" #include "abstract_aqi_calculator.h" diff --git a/esphome/components/hm3301/hm3301.cpp b/esphome/components/hm3301/hm3301.cpp index cbce714012..5612867d1b 100644 --- a/esphome/components/hm3301/hm3301.cpp +++ b/esphome/components/hm3301/hm3301.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace hm3301 { -static const char *TAG = "hm3301.sensor"; +static const char *const TAG = "hm3301.sensor"; static const uint8_t PM_1_0_VALUE_INDEX = 5; static const uint8_t PM_2_5_VALUE_INDEX = 6; diff --git a/esphome/components/hmc5883l/hmc5883l.cpp b/esphome/components/hmc5883l/hmc5883l.cpp index 9094548bb3..de3903d7e2 100644 --- a/esphome/components/hmc5883l/hmc5883l.cpp +++ b/esphome/components/hmc5883l/hmc5883l.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace hmc5883l { -static const char *TAG = "hmc5883l"; +static const char *const TAG = "hmc5883l"; static const uint8_t HMC5883L_ADDRESS = 0x1E; static const uint8_t HMC5883L_REGISTER_CONFIG_A = 0x00; static const uint8_t HMC5883L_REGISTER_CONFIG_B = 0x01; diff --git a/esphome/components/homeassistant/binary_sensor/homeassistant_binary_sensor.cpp b/esphome/components/homeassistant/binary_sensor/homeassistant_binary_sensor.cpp index cf2415d9c0..cea02f072a 100644 --- a/esphome/components/homeassistant/binary_sensor/homeassistant_binary_sensor.cpp +++ b/esphome/components/homeassistant/binary_sensor/homeassistant_binary_sensor.cpp @@ -5,33 +5,34 @@ namespace esphome { namespace homeassistant { -static const char *TAG = "homeassistant.binary_sensor"; +static const char *const TAG = "homeassistant.binary_sensor"; void HomeassistantBinarySensor::setup() { - api::global_api_server->subscribe_home_assistant_state(this->entity_id_, this->attribute_, [this](std::string state) { - auto val = parse_on_off(state.c_str()); - switch (val) { - case PARSE_NONE: - case PARSE_TOGGLE: - ESP_LOGW(TAG, "Can't convert '%s' to binary state!", state.c_str()); - break; - case PARSE_ON: - case PARSE_OFF: - bool new_state = val == PARSE_ON; - if (this->attribute_.has_value()) { - ESP_LOGD(TAG, "'%s::%s': Got attribute state %s", this->entity_id_.c_str(), this->attribute_.value().c_str(), - ONOFF(new_state)); - } else { - ESP_LOGD(TAG, "'%s': Got state %s", this->entity_id_.c_str(), ONOFF(new_state)); + api::global_api_server->subscribe_home_assistant_state( + this->entity_id_, this->attribute_, [this](const std::string &state) { + auto val = parse_on_off(state.c_str()); + switch (val) { + case PARSE_NONE: + case PARSE_TOGGLE: + ESP_LOGW(TAG, "Can't convert '%s' to binary state!", state.c_str()); + break; + case PARSE_ON: + case PARSE_OFF: + bool new_state = val == PARSE_ON; + if (this->attribute_.has_value()) { + ESP_LOGD(TAG, "'%s::%s': Got attribute state %s", this->entity_id_.c_str(), + this->attribute_.value().c_str(), ONOFF(new_state)); + } else { + ESP_LOGD(TAG, "'%s': Got state %s", this->entity_id_.c_str(), ONOFF(new_state)); + } + if (this->initial_) + this->publish_initial_state(new_state); + else + this->publish_state(new_state); + break; } - if (this->initial_) - this->publish_initial_state(new_state); - else - this->publish_state(new_state); - break; - } - this->initial_ = false; - }); + this->initial_ = false; + }); } void HomeassistantBinarySensor::dump_config() { LOG_BINARY_SENSOR("", "Homeassistant Binary Sensor", this); diff --git a/esphome/components/homeassistant/sensor/homeassistant_sensor.cpp b/esphome/components/homeassistant/sensor/homeassistant_sensor.cpp index 5f7916403e..b6f2acdbe4 100644 --- a/esphome/components/homeassistant/sensor/homeassistant_sensor.cpp +++ b/esphome/components/homeassistant/sensor/homeassistant_sensor.cpp @@ -5,25 +5,26 @@ namespace esphome { namespace homeassistant { -static const char *TAG = "homeassistant.sensor"; +static const char *const TAG = "homeassistant.sensor"; void HomeassistantSensor::setup() { - api::global_api_server->subscribe_home_assistant_state(this->entity_id_, this->attribute_, [this](std::string state) { - auto val = parse_float(state); - if (!val.has_value()) { - ESP_LOGW(TAG, "Can't convert '%s' to number!", state.c_str()); - this->publish_state(NAN); - return; - } + api::global_api_server->subscribe_home_assistant_state( + this->entity_id_, this->attribute_, [this](const std::string &state) { + auto val = parse_float(state); + if (!val.has_value()) { + ESP_LOGW(TAG, "Can't convert '%s' to number!", state.c_str()); + this->publish_state(NAN); + return; + } - if (this->attribute_.has_value()) { - ESP_LOGD(TAG, "'%s::%s': Got attribute state %.2f", this->entity_id_.c_str(), this->attribute_.value().c_str(), - *val); - } else { - ESP_LOGD(TAG, "'%s': Got state %.2f", this->entity_id_.c_str(), *val); - } - this->publish_state(*val); - }); + if (this->attribute_.has_value()) { + ESP_LOGD(TAG, "'%s::%s': Got attribute state %.2f", this->entity_id_.c_str(), + this->attribute_.value().c_str(), *val); + } else { + ESP_LOGD(TAG, "'%s': Got state %.2f", this->entity_id_.c_str(), *val); + } + this->publish_state(*val); + }); } void HomeassistantSensor::dump_config() { LOG_SENSOR("", "Homeassistant Sensor", this); diff --git a/esphome/components/homeassistant/text_sensor/homeassistant_text_sensor.cpp b/esphome/components/homeassistant/text_sensor/homeassistant_text_sensor.cpp index 07b06cad3c..9b933fbbbe 100644 --- a/esphome/components/homeassistant/text_sensor/homeassistant_text_sensor.cpp +++ b/esphome/components/homeassistant/text_sensor/homeassistant_text_sensor.cpp @@ -5,18 +5,19 @@ namespace esphome { namespace homeassistant { -static const char *TAG = "homeassistant.text_sensor"; +static const char *const TAG = "homeassistant.text_sensor"; void HomeassistantTextSensor::setup() { - api::global_api_server->subscribe_home_assistant_state(this->entity_id_, this->attribute_, [this](std::string state) { - if (this->attribute_.has_value()) { - ESP_LOGD(TAG, "'%s::%s': Got attribute state '%s'", this->entity_id_.c_str(), this->attribute_.value().c_str(), - state.c_str()); - } else { - ESP_LOGD(TAG, "'%s': Got state '%s'", this->entity_id_.c_str(), state.c_str()); - } - this->publish_state(state); - }); + api::global_api_server->subscribe_home_assistant_state( + this->entity_id_, this->attribute_, [this](const std::string &state) { + if (this->attribute_.has_value()) { + ESP_LOGD(TAG, "'%s::%s': Got attribute state '%s'", this->entity_id_.c_str(), + this->attribute_.value().c_str(), state.c_str()); + } else { + ESP_LOGD(TAG, "'%s': Got state '%s'", this->entity_id_.c_str(), state.c_str()); + } + this->publish_state(state); + }); } void HomeassistantTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Homeassistant Text Sensor", this); diff --git a/esphome/components/homeassistant/time/homeassistant_time.cpp b/esphome/components/homeassistant/time/homeassistant_time.cpp index 9ace8cf67f..9f5239404a 100644 --- a/esphome/components/homeassistant/time/homeassistant_time.cpp +++ b/esphome/components/homeassistant/time/homeassistant_time.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace homeassistant { -static const char *TAG = "homeassistant.time"; +static const char *const TAG = "homeassistant.time"; void HomeassistantTime::dump_config() { ESP_LOGCONFIG(TAG, "Home Assistant Time:"); @@ -17,6 +17,6 @@ void HomeassistantTime::setup() { global_homeassistant_time = this; } void HomeassistantTime::update() { api::global_api_server->request_time(); } -HomeassistantTime *global_homeassistant_time = nullptr; +HomeassistantTime *global_homeassistant_time = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace homeassistant } // namespace esphome diff --git a/esphome/components/homeassistant/time/homeassistant_time.h b/esphome/components/homeassistant/time/homeassistant_time.h index 94f4704c2f..36e28ea16b 100644 --- a/esphome/components/homeassistant/time/homeassistant_time.h +++ b/esphome/components/homeassistant/time/homeassistant_time.h @@ -16,7 +16,7 @@ class HomeassistantTime : public time::RealTimeClock { float get_setup_priority() const override; }; -extern HomeassistantTime *global_homeassistant_time; +extern HomeassistantTime *global_homeassistant_time; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace homeassistant } // namespace esphome diff --git a/esphome/components/http_request/http_request.cpp b/esphome/components/http_request/http_request.cpp index 79f698441e..0fafa8cd86 100644 --- a/esphome/components/http_request/http_request.cpp +++ b/esphome/components/http_request/http_request.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace http_request { -static const char *TAG = "http_request"; +static const char *const TAG = "http_request"; void HttpRequestComponent::dump_config() { ESP_LOGCONFIG(TAG, "HTTP Request:"); @@ -13,8 +13,8 @@ void HttpRequestComponent::dump_config() { } void HttpRequestComponent::set_url(std::string url) { - this->url_ = url; - this->secure_ = url.compare(0, 6, "https:") == 0; + this->url_ = std::move(url); + this->secure_ = this->url_.compare(0, 6, "https:") == 0; if (!this->last_url_.empty() && this->url_ != this->last_url_) { // Close connection if url has been changed diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index f2c688d2f9..27919fe75d 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -1,10 +1,11 @@ #pragma once +#include "esphome/components/json/json_util.h" +#include "esphome/core/automation.h" +#include "esphome/core/component.h" #include #include -#include "esphome/core/component.h" -#include "esphome/core/automation.h" -#include "esphome/components/json/json_util.h" +#include #ifdef ARDUINO_ARCH_ESP32 #include @@ -33,8 +34,8 @@ class HttpRequestComponent : public Component { void set_method(const char *method) { this->method_ = method; } void set_useragent(const char *useragent) { this->useragent_ = useragent; } void set_timeout(uint16_t timeout) { this->timeout_ = timeout; } - void set_body(std::string body) { this->body_ = body; } - void set_headers(std::list
headers) { this->headers_ = headers; } + void set_body(std::string body) { this->body_ = std::move(body); } + void set_headers(std::list
headers) { this->headers_ = std::move(headers); } void send(const std::vector &response_triggers); void close(); const char *get_string(); diff --git a/esphome/components/htu21d/htu21d.cpp b/esphome/components/htu21d/htu21d.cpp index 45926dae36..a954b2ad59 100644 --- a/esphome/components/htu21d/htu21d.cpp +++ b/esphome/components/htu21d/htu21d.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace htu21d { -static const char *TAG = "htu21d"; +static const char *const TAG = "htu21d"; static const uint8_t HTU21D_ADDRESS = 0x40; static const uint8_t HTU21D_REGISTER_RESET = 0xFE; diff --git a/esphome/components/hx711/hx711.cpp b/esphome/components/hx711/hx711.cpp index 605f534f91..62adc4ae86 100644 --- a/esphome/components/hx711/hx711.cpp +++ b/esphome/components/hx711/hx711.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace hx711 { -static const char *TAG = "hx711"; +static const char *const TAG = "hx711"; void HX711Sensor::setup() { ESP_LOGCONFIG(TAG, "Setting up HX711 '%s'...", this->name_.c_str()); diff --git a/esphome/components/i2c/i2c.cpp b/esphome/components/i2c/i2c.cpp index 2111711264..77dfcedc04 100644 --- a/esphome/components/i2c/i2c.cpp +++ b/esphome/components/i2c/i2c.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace i2c { -static const char *TAG = "i2c"; +static const char *const TAG = "i2c"; I2CComponent::I2CComponent() { #ifdef ARDUINO_ARCH_ESP32 diff --git a/esphome/components/ili9341/ili9341_display.cpp b/esphome/components/ili9341/ili9341_display.cpp index 48a95f4df0..e973671acc 100644 --- a/esphome/components/ili9341/ili9341_display.cpp +++ b/esphome/components/ili9341/ili9341_display.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace ili9341 { -static const char *TAG = "ili9341"; +static const char *const TAG = "ili9341"; void ILI9341Display::setup_pins_() { this->init_internal_(this->get_buffer_length_()); diff --git a/esphome/components/improv/improv.h b/esphome/components/improv/improv.h index 8a44812cec..47afecfc2b 100644 --- a/esphome/components/improv/improv.h +++ b/esphome/components/improv/improv.h @@ -1,18 +1,18 @@ #pragma once -#include -#include #include "WString.h" +#include +#include #include namespace improv { -static const char *SERVICE_UUID = "00467768-6228-2272-4663-277478268000"; -static const char *STATUS_UUID = "00467768-6228-2272-4663-277478268001"; -static const char *ERROR_UUID = "00467768-6228-2272-4663-277478268002"; -static const char *RPC_COMMAND_UUID = "00467768-6228-2272-4663-277478268003"; -static const char *RPC_RESULT_UUID = "00467768-6228-2272-4663-277478268004"; -static const char *CAPABILITIES_UUID = "00467768-6228-2272-4663-277478268005"; +static const char *const SERVICE_UUID = "00467768-6228-2272-4663-277478268000"; +static const char *const STATUS_UUID = "00467768-6228-2272-4663-277478268001"; +static const char *const ERROR_UUID = "00467768-6228-2272-4663-277478268002"; +static const char *const RPC_COMMAND_UUID = "00467768-6228-2272-4663-277478268003"; +static const char *const RPC_RESULT_UUID = "00467768-6228-2272-4663-277478268004"; +static const char *const CAPABILITIES_UUID = "00467768-6228-2272-4663-277478268005"; enum Error : uint8_t { ERROR_NONE = 0x00, diff --git a/esphome/components/ina219/ina219.cpp b/esphome/components/ina219/ina219.cpp index 44d6501e36..506b7e06ed 100644 --- a/esphome/components/ina219/ina219.cpp +++ b/esphome/components/ina219/ina219.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace ina219 { -static const char *TAG = "ina219"; +static const char *const TAG = "ina219"; // | A0 | A1 | Address | // | GND | GND | 0x40 | diff --git a/esphome/components/ina226/ina226.cpp b/esphome/components/ina226/ina226.cpp index cbb06d73b6..701a833041 100644 --- a/esphome/components/ina226/ina226.cpp +++ b/esphome/components/ina226/ina226.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace ina226 { -static const char *TAG = "ina226"; +static const char *const TAG = "ina226"; // | A0 | A1 | Address | // | GND | GND | 0x40 | diff --git a/esphome/components/ina3221/ina3221.cpp b/esphome/components/ina3221/ina3221.cpp index 17492433e3..f2fcdb21eb 100644 --- a/esphome/components/ina3221/ina3221.cpp +++ b/esphome/components/ina3221/ina3221.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace ina3221 { -static const char *TAG = "ina3221"; +static const char *const TAG = "ina3221"; static const uint8_t INA3221_REGISTER_CONFIG = 0x00; static const uint8_t INA3221_REGISTER_CHANNEL1_SHUNT_VOLTAGE = 0x01; diff --git a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp index a1c1c5d201..45be7c1acf 100644 --- a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp +++ b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.cpp @@ -6,11 +6,12 @@ namespace esphome { namespace inkbird_ibsth1_mini { -static const char *TAG = "inkbird_ibsth1_mini"; +static const char *const TAG = "inkbird_ibsth1_mini"; void InkbirdIBSTH1_MINI::dump_config() { ESP_LOGCONFIG(TAG, "Inkbird IBS TH1 MINI"); LOG_SENSOR(" ", "Temperature", this->temperature_); + LOG_SENSOR(" ", "External Temperature", this->external_temperature_); LOG_SENSOR(" ", "Humidity", this->humidity_); LOG_SENSOR(" ", "Battery Level", this->battery_level_); } @@ -54,7 +55,7 @@ bool InkbirdIBSTH1_MINI::parse_device(const esp32_ble_tracker::ESPBTDevice &devi ESP_LOGVV(TAG, "parse_device(): manufacturer data element length is expected to be of length 7"); return false; } - if ((mnfData.data[2] != 0) || (mnfData.data[6] != 8)) { + if (mnfData.data[6] != 8) { ESP_LOGVV(TAG, "parse_device(): unexpected data"); return false; } @@ -63,13 +64,36 @@ bool InkbirdIBSTH1_MINI::parse_device(const esp32_ble_tracker::ESPBTDevice &devi // data[5] is a battery level // data[0] and data[1] is humidity * 100 (in pct) // uuid is a temperature * 100 (in Celcius) + // when data[2] == 0 temperature is from internal sensor (IBS-TH1 or IBS-TH1 Mini) + // when data[2] == 1 temperature is from external sensor (IBS-TH1 only) + + // Create empty variables to pass automatic checks + auto temperature = NAN; + auto external_temperature = NAN; + + // Read bluetooth data into variable + auto measured_temperature = mnfData.uuid.get_uuid().uuid.uuid16 / 100.0f; + + // Set temperature or external_temperature based on which sensor is in use + if (mnfData.data[2] == 0) { + temperature = measured_temperature; + } else if (mnfData.data[2] == 1) { + external_temperature = measured_temperature; + } else { + ESP_LOGVV(TAG, "parse_device(): unknown sensor type"); + return false; + } + auto battery_level = mnfData.data[5]; - auto temperature = mnfData.uuid.get_uuid().uuid.uuid16 / 100.0f; auto humidity = ((mnfData.data[1] << 8) + mnfData.data[0]) / 100.0f; - if (this->temperature_ != nullptr) { + // Send temperature only if the value is set + if (!isnan(temperature) && this->temperature_ != nullptr) { this->temperature_->publish_state(temperature); } + if (!isnan(external_temperature) && this->external_temperature_ != nullptr) { + this->external_temperature_->publish_state(external_temperature); + } if (this->humidity_ != nullptr) { this->humidity_->publish_state(humidity); } diff --git a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h index 38e72dad17..c3a9f7062d 100644 --- a/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h +++ b/esphome/components/inkbird_ibsth1_mini/inkbird_ibsth1_mini.h @@ -18,12 +18,14 @@ class InkbirdIBSTH1_MINI : public Component, public esp32_ble_tracker::ESPBTDevi void dump_config() override; float get_setup_priority() const override { return setup_priority::DATA; } void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; } + void set_external_temperature(sensor::Sensor *external_temperature) { external_temperature_ = external_temperature; } void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; } void set_battery_level(sensor::Sensor *battery_level) { battery_level_ = battery_level; } protected: uint64_t address_; sensor::Sensor *temperature_{nullptr}; + sensor::Sensor *external_temperature_{nullptr}; sensor::Sensor *humidity_{nullptr}; sensor::Sensor *battery_level_{nullptr}; }; diff --git a/esphome/components/inkbird_ibsth1_mini/sensor.py b/esphome/components/inkbird_ibsth1_mini/sensor.py index 044e7fe67d..aaaaddb890 100644 --- a/esphome/components/inkbird_ibsth1_mini/sensor.py +++ b/esphome/components/inkbird_ibsth1_mini/sensor.py @@ -19,6 +19,8 @@ from esphome.const import ( CODEOWNERS = ["@fkirill"] DEPENDENCIES = ["esp32_ble_tracker"] +CONF_EXTERNAL_TEMPERATURE = "external_temperature" + inkbird_ibsth1_mini_ns = cg.esphome_ns.namespace("inkbird_ibsth1_mini") InkbirdUBSTH1_MINI = inkbird_ibsth1_mini_ns.class_( "InkbirdIBSTH1_MINI", esp32_ble_tracker.ESPBTDeviceListener, cg.Component @@ -36,6 +38,13 @@ CONFIG_SCHEMA = ( DEVICE_CLASS_TEMPERATURE, STATE_CLASS_MEASUREMENT, ), + cv.Optional(CONF_EXTERNAL_TEMPERATURE): sensor.sensor_schema( + UNIT_CELSIUS, + ICON_EMPTY, + 1, + DEVICE_CLASS_TEMPERATURE, + STATE_CLASS_MEASUREMENT, + ), cv.Optional(CONF_HUMIDITY): sensor.sensor_schema( UNIT_PERCENT, ICON_EMPTY, @@ -67,6 +76,9 @@ async def to_code(config): if CONF_TEMPERATURE in config: sens = await sensor.new_sensor(config[CONF_TEMPERATURE]) cg.add(var.set_temperature(sens)) + if CONF_EXTERNAL_TEMPERATURE in config: + sens = await sensor.new_sensor(config[CONF_EXTERNAL_TEMPERATURE]) + cg.add(var.set_external_temperature(sens)) if CONF_HUMIDITY in config: sens = await sensor.new_sensor(config[CONF_HUMIDITY]) cg.add(var.set_humidity(sens)) diff --git a/esphome/components/inkplate6/inkplate.cpp b/esphome/components/inkplate6/inkplate.cpp index d60f97c36d..089b4791a6 100644 --- a/esphome/components/inkplate6/inkplate.cpp +++ b/esphome/components/inkplate6/inkplate.cpp @@ -8,7 +8,7 @@ namespace esphome { namespace inkplate6 { -static const char *TAG = "inkplate"; +static const char *const TAG = "inkplate"; void Inkplate6::setup() { this->initialize_(); diff --git a/esphome/components/integration/integration_sensor.cpp b/esphome/components/integration/integration_sensor.cpp index 806c0ce567..bfcf8d3561 100644 --- a/esphome/components/integration/integration_sensor.cpp +++ b/esphome/components/integration/integration_sensor.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace integration { -static const char *TAG = "integration"; +static const char *const TAG = "integration"; void IntegrationSensor::setup() { if (this->restore_) { diff --git a/esphome/components/json/json_util.cpp b/esphome/components/json/json_util.cpp index b82ddf90f0..4720c8f9c9 100644 --- a/esphome/components/json/json_util.cpp +++ b/esphome/components/json/json_util.cpp @@ -4,10 +4,10 @@ namespace esphome { namespace json { -static const char *TAG = "json"; +static const char *const TAG = "json"; -static char *global_json_build_buffer = nullptr; -static size_t global_json_build_buffer_size = 0; +static char *global_json_build_buffer = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static size_t global_json_build_buffer_size = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) void reserve_global_json_build_buffer(size_t required_size) { if (global_json_build_buffer_size == 0 || global_json_build_buffer_size < required_size) { @@ -123,7 +123,7 @@ void VectorJsonBuffer::reserve(size_t size) { // NOLINT size_t VectorJsonBuffer::size() const { return this->size_; } -VectorJsonBuffer global_json_buffer; +VectorJsonBuffer global_json_buffer; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace json } // namespace esphome diff --git a/esphome/components/json/json_util.h b/esphome/components/json/json_util.h index d7b5267064..aafd039b9a 100644 --- a/esphome/components/json/json_util.h +++ b/esphome/components/json/json_util.h @@ -56,7 +56,7 @@ class VectorJsonBuffer : public ArduinoJson::Internals::JsonBufferBase free_blocks_; }; -extern VectorJsonBuffer global_json_buffer; +extern VectorJsonBuffer global_json_buffer; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace json } // namespace esphome diff --git a/esphome/components/lcd_base/lcd_display.cpp b/esphome/components/lcd_base/lcd_display.cpp index 50bd818cdb..95b349b9c1 100644 --- a/esphome/components/lcd_base/lcd_display.cpp +++ b/esphome/components/lcd_base/lcd_display.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace lcd_base { -static const char *TAG = "lcd"; +static const char *const TAG = "lcd"; // First set bit determines command, bits after that are the data. static const uint8_t LCD_DISPLAY_COMMAND_CLEAR_DISPLAY = 0x01; diff --git a/esphome/components/lcd_gpio/gpio_lcd_display.cpp b/esphome/components/lcd_gpio/gpio_lcd_display.cpp index 96d074bec8..5c1656ec3e 100644 --- a/esphome/components/lcd_gpio/gpio_lcd_display.cpp +++ b/esphome/components/lcd_gpio/gpio_lcd_display.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace lcd_gpio { -static const char *TAG = "lcd_gpio"; +static const char *const TAG = "lcd_gpio"; void GPIOLCDDisplay::setup() { ESP_LOGCONFIG(TAG, "Setting up GPIO LCD Display..."); diff --git a/esphome/components/lcd_pcf8574/pcf8574_display.cpp b/esphome/components/lcd_pcf8574/pcf8574_display.cpp index e3002da25d..4830b6f223 100644 --- a/esphome/components/lcd_pcf8574/pcf8574_display.cpp +++ b/esphome/components/lcd_pcf8574/pcf8574_display.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace lcd_pcf8574 { -static const char *TAG = "lcd_pcf8574"; +static const char *const TAG = "lcd_pcf8574"; static const uint8_t LCD_DISPLAY_BACKLIGHT_ON = 0x08; static const uint8_t LCD_DISPLAY_BACKLIGHT_OFF = 0x00; diff --git a/esphome/components/ledc/ledc_output.cpp b/esphome/components/ledc/ledc_output.cpp index d4e3327bb1..0575dbee6a 100644 --- a/esphome/components/ledc/ledc_output.cpp +++ b/esphome/components/ledc/ledc_output.cpp @@ -8,7 +8,7 @@ namespace esphome { namespace ledc { -static const char *TAG = "ledc.output"; +static const char *const TAG = "ledc.output"; void LEDCOutput::write_state(float state) { if (this->pin_->is_inverted()) diff --git a/esphome/components/ledc/output.py b/esphome/components/ledc/output.py index 150c5fa410..83b1c6f096 100644 --- a/esphome/components/ledc/output.py +++ b/esphome/components/ledc/output.py @@ -3,7 +3,6 @@ from esphome.components import output import esphome.config_validation as cv import esphome.codegen as cg from esphome.const import ( - CONF_BIT_DEPTH, CONF_CHANNEL, CONF_FREQUENCY, CONF_ID, @@ -50,10 +49,6 @@ CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend( cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema, cv.Optional(CONF_FREQUENCY, default="1kHz"): cv.frequency, cv.Optional(CONF_CHANNEL): cv.int_range(min=0, max=15), - cv.Optional(CONF_BIT_DEPTH): cv.invalid( - "The bit_depth option has been removed in v1.14, the " - "best bit depth is now automatically calculated." - ), } ).extend(cv.COMPONENT_SCHEMA) diff --git a/esphome/components/light/__init__.py b/esphome/components/light/__init__.py index 276bf8073d..119ff3703c 100644 --- a/esphome/components/light/__init__.py +++ b/esphome/components/light/__init__.py @@ -16,6 +16,8 @@ from esphome.const import ( CONF_ON_TURN_OFF, CONF_ON_TURN_ON, CONF_TRIGGER_ID, + CONF_COLD_WHITE_COLOR_TEMPERATURE, + CONF_WARM_WHITE_COLOR_TEMPERATURE, ) from esphome.core import coroutine_with_priority from .automation import light_control_to_code # noqa @@ -104,6 +106,18 @@ ADDRESSABLE_LIGHT_SCHEMA = RGB_LIGHT_SCHEMA.extend( ) +def validate_color_temperature_channels(value): + if ( + value[CONF_COLD_WHITE_COLOR_TEMPERATURE] + >= value[CONF_WARM_WHITE_COLOR_TEMPERATURE] + ): + raise cv.Invalid( + "Color temperature of the cold white channel must be colder than that of the warm white channel.", + path=[CONF_COLD_WHITE_COLOR_TEMPERATURE], + ) + return value + + async def setup_light_core_(light_var, output_var, config): cg.add(light_var.set_restore_mode(config[CONF_RESTORE_MODE])) if CONF_INTERNAL in config: diff --git a/esphome/components/light/addressable_light.cpp b/esphome/components/light/addressable_light.cpp index 236e5cede6..4ef293cd03 100644 --- a/esphome/components/light/addressable_light.cpp +++ b/esphome/components/light/addressable_light.cpp @@ -4,159 +4,7 @@ namespace esphome { namespace light { -static const char *TAG = "light.addressable"; - -Color ESPHSVColor::to_rgb() const { - // based on FastLED's hsv rainbow to rgb - const uint8_t hue = this->hue; - const uint8_t sat = this->saturation; - const uint8_t val = this->value; - // upper 3 hue bits are for branch selection, lower 5 are for values - const uint8_t offset8 = (hue & 0x1F) << 3; // 0..248 - // third of the offset, 255/3 = 85 (actually only up to 82; 164) - const uint8_t third = esp_scale8(offset8, 85); - const uint8_t two_thirds = esp_scale8(offset8, 170); - Color rgb(255, 255, 255, 0); - switch (hue >> 5) { - case 0b000: - rgb.r = 255 - third; - rgb.g = third; - rgb.b = 0; - break; - case 0b001: - rgb.r = 171; - rgb.g = 85 + third; - rgb.b = 0; - break; - case 0b010: - rgb.r = 171 - two_thirds; - rgb.g = 170 + third; - rgb.b = 0; - break; - case 0b011: - rgb.r = 0; - rgb.g = 255 - third; - rgb.b = third; - break; - case 0b100: - rgb.r = 0; - rgb.g = 171 - two_thirds; - rgb.b = 85 + two_thirds; - break; - case 0b101: - rgb.r = third; - rgb.g = 0; - rgb.b = 255 - third; - break; - case 0b110: - rgb.r = 85 + third; - rgb.g = 0; - rgb.b = 171 - third; - break; - case 0b111: - rgb.r = 170 + third; - rgb.g = 0; - rgb.b = 85 - third; - break; - default: - break; - } - // low saturation -> add uniform color to orig. hue - // high saturation -> use hue directly - // scales with square of saturation - // (r,g,b) = (r,g,b) * sat + (1 - sat)^2 - rgb *= sat; - const uint8_t desat = 255 - sat; - rgb += esp_scale8(desat, desat); - // (r,g,b) = (r,g,b) * val - rgb *= val; - return rgb; -} - -void ESPRangeView::set(const Color &color) { - for (int32_t i = this->begin_; i < this->end_; i++) { - (*this->parent_)[i] = color; - } -} -ESPColorView ESPRangeView::operator[](int32_t index) const { - index = interpret_index(index, this->size()) + this->begin_; - return (*this->parent_)[index]; -} -ESPRangeIterator ESPRangeView::begin() { return {*this, this->begin_}; } -ESPRangeIterator ESPRangeView::end() { return {*this, this->end_}; } -void ESPRangeView::set_red(uint8_t red) { - for (auto c : *this) - c.set_red(red); -} -void ESPRangeView::set_green(uint8_t green) { - for (auto c : *this) - c.set_green(green); -} -void ESPRangeView::set_blue(uint8_t blue) { - for (auto c : *this) - c.set_blue(blue); -} -void ESPRangeView::set_white(uint8_t white) { - for (auto c : *this) - c.set_white(white); -} -void ESPRangeView::set_effect_data(uint8_t effect_data) { - for (auto c : *this) - c.set_effect_data(effect_data); -} -void ESPRangeView::fade_to_white(uint8_t amnt) { - for (auto c : *this) - c.fade_to_white(amnt); -} -void ESPRangeView::fade_to_black(uint8_t amnt) { - for (auto c : *this) - c.fade_to_black(amnt); -} -void ESPRangeView::lighten(uint8_t delta) { - for (auto c : *this) - c.lighten(delta); -} -void ESPRangeView::darken(uint8_t delta) { - for (auto c : *this) - c.darken(delta); -} -ESPRangeView &ESPRangeView::operator=(const ESPRangeView &rhs) { - // If size doesn't match, error (todo warning) - if (rhs.size() != this->size()) - return *this; - - if (this->parent_ != rhs.parent_) { - for (int32_t i = 0; i < this->size(); i++) - (*this)[i].set(rhs[i].get()); - return *this; - } - - // If both equal, already done - if (rhs.begin_ == this->begin_) - return *this; - - if (rhs.begin_ > this->begin_) { - // Copy from left - for (int32_t i = 0; i < this->size(); i++) { - (*this)[i].set(rhs[i].get()); - } - } else { - // Copy from right - for (int32_t i = this->size() - 1; i >= 0; i--) { - (*this)[i].set(rhs[i].get()); - } - } - - return *this; -} - -ESPColorView ESPRangeIterator::operator*() const { return this->range_.parent_->get(this->i_); } - -int32_t HOT interpret_index(int32_t index, int32_t size) { - if (index < 0) - return size + index; - return index; -} +static const char *const TAG = "light.addressable"; void AddressableLight::call_setup() { this->setup(); @@ -177,10 +25,10 @@ void AddressableLight::call_setup() { } Color esp_color_from_light_color_values(LightColorValues val) { - auto r = static_cast(roundf(val.get_red() * 255.0f)); - auto g = static_cast(roundf(val.get_green() * 255.0f)); - auto b = static_cast(roundf(val.get_blue() * 255.0f)); - auto w = static_cast(roundf(val.get_white() * val.get_state() * 255.0f)); + auto r = static_cast(roundf(val.get_color_brightness() * val.get_red() * 255.0f)); + auto g = static_cast(roundf(val.get_color_brightness() * val.get_green() * 255.0f)); + auto b = static_cast(roundf(val.get_color_brightness() * val.get_blue() * 255.0f)); + auto w = static_cast(roundf(val.get_white() * 255.0f)); return Color(r, g, b, w); } @@ -220,10 +68,7 @@ void AddressableLight::write_state(LightState *state) { // our transition will handle brightness, disable brightness in correction. this->correction_.set_local_brightness(255); - uint8_t orig_w = target_color.w; target_color *= static_cast(roundf(end_values.get_brightness() * end_values.get_state() * 255.0f)); - // w is not scaled by brightness - target_color.w = orig_w; float denom = (1.0f - new_smoothed); float alpha = denom == 0.0f ? 0.0f : (new_smoothed - prev_smoothed) / denom; @@ -254,23 +99,5 @@ void AddressableLight::write_state(LightState *state) { this->schedule_show(); } -void ESPColorCorrection::calculate_gamma_table(float gamma) { - for (uint16_t i = 0; i < 256; i++) { - // corrected = val ^ gamma - auto corrected = static_cast(roundf(255.0f * gamma_correct(i / 255.0f, gamma))); - this->gamma_table_[i] = corrected; - } - if (gamma == 0.0f) { - for (uint16_t i = 0; i < 256; i++) - this->gamma_reverse_table_[i] = i; - return; - } - for (uint16_t i = 0; i < 256; i++) { - // val = corrected ^ (1/gamma) - auto uncorrected = static_cast(roundf(255.0f * powf(i / 255.0f, 1.0f / gamma))); - this->gamma_reverse_table_[i] = uncorrected; - } -} - } // namespace light } // namespace esphome diff --git a/esphome/components/light/addressable_light.h b/esphome/components/light/addressable_light.h index 39bd905c65..460cb88935 100644 --- a/esphome/components/light/addressable_light.h +++ b/esphome/components/light/addressable_light.h @@ -3,6 +3,9 @@ #include "esphome/core/component.h" #include "esphome/core/defines.h" #include "esphome/core/color.h" +#include "esp_color_correction.h" +#include "esp_color_view.h" +#include "esp_range_view.h" #include "light_output.h" #include "light_state.h" @@ -15,266 +18,6 @@ namespace light { using ESPColor = Color; -struct ESPHSVColor { - union { - struct { - union { - uint8_t hue; - uint8_t h; - }; - union { - uint8_t saturation; - uint8_t s; - }; - union { - uint8_t value; - uint8_t v; - }; - }; - uint8_t raw[3]; - }; - inline ESPHSVColor() ALWAYS_INLINE : h(0), s(0), v(0) { // NOLINT - } - inline ESPHSVColor(uint8_t hue, uint8_t saturation, uint8_t value) ALWAYS_INLINE : hue(hue), - saturation(saturation), - value(value) {} - Color to_rgb() const; -}; - -class ESPColorCorrection { - public: - ESPColorCorrection() : max_brightness_(255, 255, 255, 255) {} - void set_max_brightness(const Color &max_brightness) { this->max_brightness_ = max_brightness; } - void set_local_brightness(uint8_t local_brightness) { this->local_brightness_ = local_brightness; } - void calculate_gamma_table(float gamma); - inline Color color_correct(Color color) const ALWAYS_INLINE { - // corrected = (uncorrected * max_brightness * local_brightness) ^ gamma - return Color(this->color_correct_red(color.red), this->color_correct_green(color.green), - this->color_correct_blue(color.blue), this->color_correct_white(color.white)); - } - inline uint8_t color_correct_red(uint8_t red) const ALWAYS_INLINE { - uint8_t res = esp_scale8(esp_scale8(red, this->max_brightness_.red), this->local_brightness_); - return this->gamma_table_[res]; - } - inline uint8_t color_correct_green(uint8_t green) const ALWAYS_INLINE { - uint8_t res = esp_scale8(esp_scale8(green, this->max_brightness_.green), this->local_brightness_); - return this->gamma_table_[res]; - } - inline uint8_t color_correct_blue(uint8_t blue) const ALWAYS_INLINE { - uint8_t res = esp_scale8(esp_scale8(blue, this->max_brightness_.blue), this->local_brightness_); - return this->gamma_table_[res]; - } - inline uint8_t color_correct_white(uint8_t white) const ALWAYS_INLINE { - // do not scale white value with brightness - uint8_t res = esp_scale8(white, this->max_brightness_.white); - return this->gamma_table_[res]; - } - inline Color color_uncorrect(Color color) const ALWAYS_INLINE { - // uncorrected = corrected^(1/gamma) / (max_brightness * local_brightness) - return Color(this->color_uncorrect_red(color.red), this->color_uncorrect_green(color.green), - this->color_uncorrect_blue(color.blue), this->color_uncorrect_white(color.white)); - } - inline uint8_t color_uncorrect_red(uint8_t red) const ALWAYS_INLINE { - if (this->max_brightness_.red == 0 || this->local_brightness_ == 0) - return 0; - uint16_t uncorrected = this->gamma_reverse_table_[red] * 255UL; - uint8_t res = ((uncorrected / this->max_brightness_.red) * 255UL) / this->local_brightness_; - return res; - } - inline uint8_t color_uncorrect_green(uint8_t green) const ALWAYS_INLINE { - if (this->max_brightness_.green == 0 || this->local_brightness_ == 0) - return 0; - uint16_t uncorrected = this->gamma_reverse_table_[green] * 255UL; - uint8_t res = ((uncorrected / this->max_brightness_.green) * 255UL) / this->local_brightness_; - return res; - } - inline uint8_t color_uncorrect_blue(uint8_t blue) const ALWAYS_INLINE { - if (this->max_brightness_.blue == 0 || this->local_brightness_ == 0) - return 0; - uint16_t uncorrected = this->gamma_reverse_table_[blue] * 255UL; - uint8_t res = ((uncorrected / this->max_brightness_.blue) * 255UL) / this->local_brightness_; - return res; - } - inline uint8_t color_uncorrect_white(uint8_t white) const ALWAYS_INLINE { - if (this->max_brightness_.white == 0) - return 0; - uint16_t uncorrected = this->gamma_reverse_table_[white] * 255UL; - uint8_t res = uncorrected / this->max_brightness_.white; - return res; - } - - protected: - uint8_t gamma_table_[256]; - uint8_t gamma_reverse_table_[256]; - Color max_brightness_; - uint8_t local_brightness_{255}; -}; - -class ESPColorSettable { - public: - virtual void set(const Color &color) = 0; - virtual void set_red(uint8_t red) = 0; - virtual void set_green(uint8_t green) = 0; - virtual void set_blue(uint8_t blue) = 0; - virtual void set_white(uint8_t white) = 0; - virtual void set_effect_data(uint8_t effect_data) = 0; - virtual void fade_to_white(uint8_t amnt) = 0; - virtual void fade_to_black(uint8_t amnt) = 0; - virtual void lighten(uint8_t delta) = 0; - virtual void darken(uint8_t delta) = 0; - void set(const ESPHSVColor &color) { this->set_hsv(color); } - void set_hsv(const ESPHSVColor &color) { - Color rgb = color.to_rgb(); - this->set_rgb(rgb.r, rgb.g, rgb.b); - } - void set_rgb(uint8_t red, uint8_t green, uint8_t blue) { - this->set_red(red); - this->set_green(green); - this->set_blue(blue); - } - void set_rgbw(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) { - this->set_rgb(red, green, blue); - this->set_white(white); - } -}; - -class ESPColorView : public ESPColorSettable { - public: - ESPColorView(uint8_t *red, uint8_t *green, uint8_t *blue, uint8_t *white, uint8_t *effect_data, - const ESPColorCorrection *color_correction) - : red_(red), - green_(green), - blue_(blue), - white_(white), - effect_data_(effect_data), - color_correction_(color_correction) {} - ESPColorView &operator=(const Color &rhs) { - this->set(rhs); - return *this; - } - ESPColorView &operator=(const ESPHSVColor &rhs) { - this->set_hsv(rhs); - return *this; - } - void set(const Color &color) override { this->set_rgbw(color.r, color.g, color.b, color.w); } - void set_red(uint8_t red) override { *this->red_ = this->color_correction_->color_correct_red(red); } - void set_green(uint8_t green) override { *this->green_ = this->color_correction_->color_correct_green(green); } - void set_blue(uint8_t blue) override { *this->blue_ = this->color_correction_->color_correct_blue(blue); } - void set_white(uint8_t white) override { - if (this->white_ == nullptr) - return; - *this->white_ = this->color_correction_->color_correct_white(white); - } - void set_effect_data(uint8_t effect_data) override { - if (this->effect_data_ == nullptr) - return; - *this->effect_data_ = effect_data; - } - void fade_to_white(uint8_t amnt) override { this->set(this->get().fade_to_white(amnt)); } - void fade_to_black(uint8_t amnt) override { this->set(this->get().fade_to_black(amnt)); } - void lighten(uint8_t delta) override { this->set(this->get().lighten(delta)); } - void darken(uint8_t delta) override { this->set(this->get().darken(delta)); } - Color get() const { return Color(this->get_red(), this->get_green(), this->get_blue(), this->get_white()); } - uint8_t get_red() const { return this->color_correction_->color_uncorrect_red(*this->red_); } - uint8_t get_red_raw() const { return *this->red_; } - uint8_t get_green() const { return this->color_correction_->color_uncorrect_green(*this->green_); } - uint8_t get_green_raw() const { return *this->green_; } - uint8_t get_blue() const { return this->color_correction_->color_uncorrect_blue(*this->blue_); } - uint8_t get_blue_raw() const { return *this->blue_; } - uint8_t get_white() const { - if (this->white_ == nullptr) - return 0; - return this->color_correction_->color_uncorrect_white(*this->white_); - } - uint8_t get_white_raw() const { - if (this->white_ == nullptr) - return 0; - return *this->white_; - } - uint8_t get_effect_data() const { - if (this->effect_data_ == nullptr) - return 0; - return *this->effect_data_; - } - void raw_set_color_correction(const ESPColorCorrection *color_correction) { - this->color_correction_ = color_correction; - } - - protected: - uint8_t *const red_; - uint8_t *const green_; - uint8_t *const blue_; - uint8_t *const white_; - uint8_t *const effect_data_; - const ESPColorCorrection *color_correction_; -}; - -class AddressableLight; - -int32_t interpret_index(int32_t index, int32_t size); - -class ESPRangeIterator; - -class ESPRangeView : public ESPColorSettable { - public: - ESPRangeView(AddressableLight *parent, int32_t begin, int32_t an_end) : parent_(parent), begin_(begin), end_(an_end) { - if (this->end_ < this->begin_) { - this->end_ = this->begin_; - } - } - - ESPColorView operator[](int32_t index) const; - ESPRangeIterator begin(); - ESPRangeIterator end(); - - void set(const Color &color) override; - ESPRangeView &operator=(const Color &rhs) { - this->set(rhs); - return *this; - } - ESPRangeView &operator=(const ESPColorView &rhs) { - this->set(rhs.get()); - return *this; - } - ESPRangeView &operator=(const ESPHSVColor &rhs) { - this->set_hsv(rhs); - return *this; - } - ESPRangeView &operator=(const ESPRangeView &rhs); - void set_red(uint8_t red) override; - void set_green(uint8_t green) override; - void set_blue(uint8_t blue) override; - void set_white(uint8_t white) override; - void set_effect_data(uint8_t effect_data) override; - void fade_to_white(uint8_t amnt) override; - void fade_to_black(uint8_t amnt) override; - void lighten(uint8_t delta) override; - void darken(uint8_t delta) override; - int32_t size() const { return this->end_ - this->begin_; } - - protected: - friend ESPRangeIterator; - - AddressableLight *parent_; - int32_t begin_; - int32_t end_; -}; - -class ESPRangeIterator { - public: - ESPRangeIterator(const ESPRangeView &range, int32_t i) : range_(range), i_(i) {} - ESPRangeIterator operator++() { - this->i_++; - return *this; - } - bool operator!=(const ESPRangeIterator &other) const { return this->i_ != other.i_; } - ESPColorView operator*() const; - - protected: - ESPRangeView range_; - int32_t i_; -}; - class AddressableLight : public LightOutput, public Component { public: virtual int32_t size() const = 0; diff --git a/esphome/components/light/addressable_light_effect.h b/esphome/components/light/addressable_light_effect.h index 8d4d37ec34..d1ea9e3ff0 100644 --- a/esphome/components/light/addressable_light_effect.h +++ b/esphome/components/light/addressable_light_effect.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "esphome/core/component.h" #include "esphome/components/light/light_state.h" #include "esphome/components/light/addressable_light.h" @@ -51,9 +53,9 @@ class AddressableLightEffect : public LightEffect { class AddressableLambdaLightEffect : public AddressableLightEffect { public: AddressableLambdaLightEffect(const std::string &name, - const std::function &f, + std::function f, uint32_t update_interval) - : AddressableLightEffect(name), f_(f), update_interval_(update_interval) {} + : AddressableLightEffect(name), f_(std::move(f)), update_interval_(update_interval) {} void start() override { this->initial_run_ = true; } void apply(AddressableLight &it, const Color ¤t_color) override { const uint32_t now = millis(); diff --git a/esphome/components/light/automation.h b/esphome/components/light/automation.h index d1fb2a0bcb..e99d5c58bd 100644 --- a/esphome/components/light/automation.h +++ b/esphome/components/light/automation.h @@ -31,6 +31,7 @@ template class LightControlAction : public Action { TEMPLATABLE_VALUE(uint32_t, transition_length) TEMPLATABLE_VALUE(uint32_t, flash_length) TEMPLATABLE_VALUE(float, brightness) + TEMPLATABLE_VALUE(float, color_brightness) TEMPLATABLE_VALUE(float, red) TEMPLATABLE_VALUE(float, green) TEMPLATABLE_VALUE(float, blue) @@ -42,6 +43,7 @@ template class LightControlAction : public Action { auto call = this->parent_->make_call(); call.set_state(this->state_.optional_value(x...)); call.set_brightness(this->brightness_.optional_value(x...)); + call.set_color_brightness(this->color_brightness_.optional_value(x...)); call.set_red(this->red_.optional_value(x...)); call.set_green(this->green_.optional_value(x...)); call.set_blue(this->blue_.optional_value(x...)); @@ -139,6 +141,7 @@ template class AddressableSet : public Action { TEMPLATABLE_VALUE(int32_t, range_from) TEMPLATABLE_VALUE(int32_t, range_to) + TEMPLATABLE_VALUE(uint8_t, color_brightness) TEMPLATABLE_VALUE(uint8_t, red) TEMPLATABLE_VALUE(uint8_t, green) TEMPLATABLE_VALUE(uint8_t, blue) @@ -148,13 +151,16 @@ template class AddressableSet : public Action { auto *out = (AddressableLight *) this->parent_->get_output(); int32_t range_from = this->range_from_.value_or(x..., 0); int32_t range_to = this->range_to_.value_or(x..., out->size() - 1) + 1; + uint8_t remote_color_brightness = + static_cast(roundf(this->parent_->remote_values.get_color_brightness() * 255.0f)); + uint8_t color_brightness = this->color_brightness_.value_or(x..., remote_color_brightness); auto range = out->range(range_from, range_to); if (this->red_.has_value()) - range.set_red(this->red_.value(x...)); + range.set_red(esp_scale8(this->red_.value(x...), color_brightness)); if (this->green_.has_value()) - range.set_green(this->green_.value(x...)); + range.set_green(esp_scale8(this->green_.value(x...), color_brightness)); if (this->blue_.has_value()) - range.set_blue(this->blue_.value(x...)); + range.set_blue(esp_scale8(this->blue_.value(x...), color_brightness)); if (this->white_.has_value()) range.set_white(this->white_.value(x...)); out->schedule_show(); diff --git a/esphome/components/light/automation.py b/esphome/components/light/automation.py index 3fb3126f14..dd1148131a 100644 --- a/esphome/components/light/automation.py +++ b/esphome/components/light/automation.py @@ -8,6 +8,7 @@ from esphome.const import ( CONF_FLASH_LENGTH, CONF_EFFECT, CONF_BRIGHTNESS, + CONF_COLOR_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, @@ -63,6 +64,7 @@ LIGHT_CONTROL_ACTION_SCHEMA = cv.Schema( ), cv.Exclusive(CONF_EFFECT, "transformer"): cv.templatable(cv.string), cv.Optional(CONF_BRIGHTNESS): cv.templatable(cv.percentage), + cv.Optional(CONF_COLOR_BRIGHTNESS): cv.templatable(cv.percentage), cv.Optional(CONF_RED): cv.templatable(cv.percentage), cv.Optional(CONF_GREEN): cv.templatable(cv.percentage), cv.Optional(CONF_BLUE): cv.templatable(cv.percentage), @@ -114,6 +116,9 @@ async def light_control_to_code(config, action_id, template_arg, args): if CONF_BRIGHTNESS in config: template_ = await cg.templatable(config[CONF_BRIGHTNESS], args, float) cg.add(var.set_brightness(template_)) + if CONF_COLOR_BRIGHTNESS in config: + template_ = await cg.templatable(config[CONF_COLOR_BRIGHTNESS], args, float) + cg.add(var.set_color_brightness(template_)) if CONF_RED in config: template_ = await cg.templatable(config[CONF_RED], args, float) cg.add(var.set_red(template_)) diff --git a/esphome/components/light/base_light_effects.h b/esphome/components/light/base_light_effects.h index 775ee363af..eb92fec642 100644 --- a/esphome/components/light/base_light_effects.h +++ b/esphome/components/light/base_light_effects.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "light_effect.h" #include "esphome/core/automation.h" @@ -85,8 +87,8 @@ class RandomLightEffect : public LightEffect { class LambdaLightEffect : public LightEffect { public: - LambdaLightEffect(const std::string &name, const std::function &f, uint32_t update_interval) - : LightEffect(name), f_(f), update_interval_(update_interval) {} + LambdaLightEffect(const std::string &name, std::function f, uint32_t update_interval) + : LightEffect(name), f_(std::move(f)), update_interval_(update_interval) {} void apply() override { const uint32_t now = millis(); diff --git a/esphome/components/light/effects.py b/esphome/components/light/effects.py index c213de0ae6..f6ce812c34 100644 --- a/esphome/components/light/effects.py +++ b/esphome/components/light/effects.py @@ -12,6 +12,7 @@ from esphome.const import ( CONF_STATE, CONF_DURATION, CONF_BRIGHTNESS, + CONF_COLOR_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, @@ -211,6 +212,7 @@ async def random_effect_to_code(config, effect_id): { cv.Optional(CONF_STATE, default=True): cv.boolean, cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, + cv.Optional(CONF_COLOR_BRIGHTNESS, default=1.0): cv.percentage, cv.Optional(CONF_RED, default=1.0): cv.percentage, cv.Optional(CONF_GREEN, default=1.0): cv.percentage, cv.Optional(CONF_BLUE, default=1.0): cv.percentage, @@ -223,6 +225,7 @@ async def random_effect_to_code(config, effect_id): cv.has_at_least_one_key( CONF_STATE, CONF_BRIGHTNESS, + CONF_COLOR_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, @@ -245,6 +248,7 @@ async def strobe_effect_to_code(config, effect_id): LightColorValues( color[CONF_STATE], color[CONF_BRIGHTNESS], + color[CONF_COLOR_BRIGHTNESS], color[CONF_RED], color[CONF_GREEN], color[CONF_BLUE], diff --git a/esphome/components/light/esp_color_correction.cpp b/esphome/components/light/esp_color_correction.cpp new file mode 100644 index 0000000000..19a2af3da1 --- /dev/null +++ b/esphome/components/light/esp_color_correction.cpp @@ -0,0 +1,26 @@ +#include "esp_color_correction.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace light { + +void ESPColorCorrection::calculate_gamma_table(float gamma) { + for (uint16_t i = 0; i < 256; i++) { + // corrected = val ^ gamma + auto corrected = static_cast(roundf(255.0f * gamma_correct(i / 255.0f, gamma))); + this->gamma_table_[i] = corrected; + } + if (gamma == 0.0f) { + for (uint16_t i = 0; i < 256; i++) + this->gamma_reverse_table_[i] = i; + return; + } + for (uint16_t i = 0; i < 256; i++) { + // val = corrected ^ (1/gamma) + auto uncorrected = static_cast(roundf(255.0f * powf(i / 255.0f, 1.0f / gamma))); + this->gamma_reverse_table_[i] = uncorrected; + } +} + +} // namespace light +} // namespace esphome diff --git a/esphome/components/light/esp_color_correction.h b/esphome/components/light/esp_color_correction.h new file mode 100644 index 0000000000..8788246cfc --- /dev/null +++ b/esphome/components/light/esp_color_correction.h @@ -0,0 +1,77 @@ +#pragma once + +#include "esphome/core/color.h" + +namespace esphome { +namespace light { + +class ESPColorCorrection { + public: + ESPColorCorrection() : max_brightness_(255, 255, 255, 255) {} + void set_max_brightness(const Color &max_brightness) { this->max_brightness_ = max_brightness; } + void set_local_brightness(uint8_t local_brightness) { this->local_brightness_ = local_brightness; } + void calculate_gamma_table(float gamma); + inline Color color_correct(Color color) const ALWAYS_INLINE { + // corrected = (uncorrected * max_brightness * local_brightness) ^ gamma + return Color(this->color_correct_red(color.red), this->color_correct_green(color.green), + this->color_correct_blue(color.blue), this->color_correct_white(color.white)); + } + inline uint8_t color_correct_red(uint8_t red) const ALWAYS_INLINE { + uint8_t res = esp_scale8(esp_scale8(red, this->max_brightness_.red), this->local_brightness_); + return this->gamma_table_[res]; + } + inline uint8_t color_correct_green(uint8_t green) const ALWAYS_INLINE { + uint8_t res = esp_scale8(esp_scale8(green, this->max_brightness_.green), this->local_brightness_); + return this->gamma_table_[res]; + } + inline uint8_t color_correct_blue(uint8_t blue) const ALWAYS_INLINE { + uint8_t res = esp_scale8(esp_scale8(blue, this->max_brightness_.blue), this->local_brightness_); + return this->gamma_table_[res]; + } + inline uint8_t color_correct_white(uint8_t white) const ALWAYS_INLINE { + uint8_t res = esp_scale8(esp_scale8(white, this->max_brightness_.white), this->local_brightness_); + return this->gamma_table_[res]; + } + inline Color color_uncorrect(Color color) const ALWAYS_INLINE { + // uncorrected = corrected^(1/gamma) / (max_brightness * local_brightness) + return Color(this->color_uncorrect_red(color.red), this->color_uncorrect_green(color.green), + this->color_uncorrect_blue(color.blue), this->color_uncorrect_white(color.white)); + } + inline uint8_t color_uncorrect_red(uint8_t red) const ALWAYS_INLINE { + if (this->max_brightness_.red == 0 || this->local_brightness_ == 0) + return 0; + uint16_t uncorrected = this->gamma_reverse_table_[red] * 255UL; + uint8_t res = ((uncorrected / this->max_brightness_.red) * 255UL) / this->local_brightness_; + return res; + } + inline uint8_t color_uncorrect_green(uint8_t green) const ALWAYS_INLINE { + if (this->max_brightness_.green == 0 || this->local_brightness_ == 0) + return 0; + uint16_t uncorrected = this->gamma_reverse_table_[green] * 255UL; + uint8_t res = ((uncorrected / this->max_brightness_.green) * 255UL) / this->local_brightness_; + return res; + } + inline uint8_t color_uncorrect_blue(uint8_t blue) const ALWAYS_INLINE { + if (this->max_brightness_.blue == 0 || this->local_brightness_ == 0) + return 0; + uint16_t uncorrected = this->gamma_reverse_table_[blue] * 255UL; + uint8_t res = ((uncorrected / this->max_brightness_.blue) * 255UL) / this->local_brightness_; + return res; + } + inline uint8_t color_uncorrect_white(uint8_t white) const ALWAYS_INLINE { + if (this->max_brightness_.white == 0 || this->local_brightness_ == 0) + return 0; + uint16_t uncorrected = this->gamma_reverse_table_[white] * 255UL; + uint8_t res = ((uncorrected / this->max_brightness_.white) * 255UL) / this->local_brightness_; + return res; + } + + protected: + uint8_t gamma_table_[256]; + uint8_t gamma_reverse_table_[256]; + Color max_brightness_; + uint8_t local_brightness_{255}; +}; + +} // namespace light +} // namespace esphome diff --git a/esphome/components/light/esp_color_view.h b/esphome/components/light/esp_color_view.h new file mode 100644 index 0000000000..35117e7dd8 --- /dev/null +++ b/esphome/components/light/esp_color_view.h @@ -0,0 +1,110 @@ +#pragma once + +#include "esphome/core/color.h" +#include "esp_hsv_color.h" +#include "esp_color_correction.h" + +namespace esphome { +namespace light { + +class ESPColorSettable { + public: + virtual void set(const Color &color) = 0; + virtual void set_red(uint8_t red) = 0; + virtual void set_green(uint8_t green) = 0; + virtual void set_blue(uint8_t blue) = 0; + virtual void set_white(uint8_t white) = 0; + virtual void set_effect_data(uint8_t effect_data) = 0; + virtual void fade_to_white(uint8_t amnt) = 0; + virtual void fade_to_black(uint8_t amnt) = 0; + virtual void lighten(uint8_t delta) = 0; + virtual void darken(uint8_t delta) = 0; + void set(const ESPHSVColor &color) { this->set_hsv(color); } + void set_hsv(const ESPHSVColor &color) { + Color rgb = color.to_rgb(); + this->set_rgb(rgb.r, rgb.g, rgb.b); + } + void set_rgb(uint8_t red, uint8_t green, uint8_t blue) { + this->set_red(red); + this->set_green(green); + this->set_blue(blue); + } + void set_rgbw(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) { + this->set_rgb(red, green, blue); + this->set_white(white); + } +}; + +class ESPColorView : public ESPColorSettable { + public: + ESPColorView(uint8_t *red, uint8_t *green, uint8_t *blue, uint8_t *white, uint8_t *effect_data, + const ESPColorCorrection *color_correction) + : red_(red), + green_(green), + blue_(blue), + white_(white), + effect_data_(effect_data), + color_correction_(color_correction) {} + ESPColorView &operator=(const Color &rhs) { + this->set(rhs); + return *this; + } + ESPColorView &operator=(const ESPHSVColor &rhs) { + this->set_hsv(rhs); + return *this; + } + void set(const Color &color) override { this->set_rgbw(color.r, color.g, color.b, color.w); } + void set_red(uint8_t red) override { *this->red_ = this->color_correction_->color_correct_red(red); } + void set_green(uint8_t green) override { *this->green_ = this->color_correction_->color_correct_green(green); } + void set_blue(uint8_t blue) override { *this->blue_ = this->color_correction_->color_correct_blue(blue); } + void set_white(uint8_t white) override { + if (this->white_ == nullptr) + return; + *this->white_ = this->color_correction_->color_correct_white(white); + } + void set_effect_data(uint8_t effect_data) override { + if (this->effect_data_ == nullptr) + return; + *this->effect_data_ = effect_data; + } + void fade_to_white(uint8_t amnt) override { this->set(this->get().fade_to_white(amnt)); } + void fade_to_black(uint8_t amnt) override { this->set(this->get().fade_to_black(amnt)); } + void lighten(uint8_t delta) override { this->set(this->get().lighten(delta)); } + void darken(uint8_t delta) override { this->set(this->get().darken(delta)); } + Color get() const { return Color(this->get_red(), this->get_green(), this->get_blue(), this->get_white()); } + uint8_t get_red() const { return this->color_correction_->color_uncorrect_red(*this->red_); } + uint8_t get_red_raw() const { return *this->red_; } + uint8_t get_green() const { return this->color_correction_->color_uncorrect_green(*this->green_); } + uint8_t get_green_raw() const { return *this->green_; } + uint8_t get_blue() const { return this->color_correction_->color_uncorrect_blue(*this->blue_); } + uint8_t get_blue_raw() const { return *this->blue_; } + uint8_t get_white() const { + if (this->white_ == nullptr) + return 0; + return this->color_correction_->color_uncorrect_white(*this->white_); + } + uint8_t get_white_raw() const { + if (this->white_ == nullptr) + return 0; + return *this->white_; + } + uint8_t get_effect_data() const { + if (this->effect_data_ == nullptr) + return 0; + return *this->effect_data_; + } + void raw_set_color_correction(const ESPColorCorrection *color_correction) { + this->color_correction_ = color_correction; + } + + protected: + uint8_t *const red_; + uint8_t *const green_; + uint8_t *const blue_; + uint8_t *const white_; + uint8_t *const effect_data_; + const ESPColorCorrection *color_correction_; +}; + +} // namespace light +} // namespace esphome diff --git a/esphome/components/light/esp_hsv_color.cpp b/esphome/components/light/esp_hsv_color.cpp new file mode 100644 index 0000000000..450c2e11ce --- /dev/null +++ b/esphome/components/light/esp_hsv_color.cpp @@ -0,0 +1,74 @@ +#include "esp_hsv_color.h" + +namespace esphome { +namespace light { + +Color ESPHSVColor::to_rgb() const { + // based on FastLED's hsv rainbow to rgb + const uint8_t hue = this->hue; + const uint8_t sat = this->saturation; + const uint8_t val = this->value; + // upper 3 hue bits are for branch selection, lower 5 are for values + const uint8_t offset8 = (hue & 0x1F) << 3; // 0..248 + // third of the offset, 255/3 = 85 (actually only up to 82; 164) + const uint8_t third = esp_scale8(offset8, 85); + const uint8_t two_thirds = esp_scale8(offset8, 170); + Color rgb(255, 255, 255, 0); + switch (hue >> 5) { + case 0b000: + rgb.r = 255 - third; + rgb.g = third; + rgb.b = 0; + break; + case 0b001: + rgb.r = 171; + rgb.g = 85 + third; + rgb.b = 0; + break; + case 0b010: + rgb.r = 171 - two_thirds; + rgb.g = 170 + third; + rgb.b = 0; + break; + case 0b011: + rgb.r = 0; + rgb.g = 255 - third; + rgb.b = third; + break; + case 0b100: + rgb.r = 0; + rgb.g = 171 - two_thirds; + rgb.b = 85 + two_thirds; + break; + case 0b101: + rgb.r = third; + rgb.g = 0; + rgb.b = 255 - third; + break; + case 0b110: + rgb.r = 85 + third; + rgb.g = 0; + rgb.b = 171 - third; + break; + case 0b111: + rgb.r = 170 + third; + rgb.g = 0; + rgb.b = 85 - third; + break; + default: + break; + } + // low saturation -> add uniform color to orig. hue + // high saturation -> use hue directly + // scales with square of saturation + // (r,g,b) = (r,g,b) * sat + (1 - sat)^2 + rgb *= sat; + const uint8_t desat = 255 - sat; + rgb += esp_scale8(desat, desat); + // (r,g,b) = (r,g,b) * val + rgb *= val; + return rgb; +} + +} // namespace light +} // namespace esphome diff --git a/esphome/components/light/esp_hsv_color.h b/esphome/components/light/esp_hsv_color.h new file mode 100644 index 0000000000..e0aa388875 --- /dev/null +++ b/esphome/components/light/esp_hsv_color.h @@ -0,0 +1,36 @@ +#pragma once + +#include "esphome/core/helpers.h" +#include "esphome/core/color.h" + +namespace esphome { +namespace light { + +struct ESPHSVColor { + union { + struct { + union { + uint8_t hue; + uint8_t h; + }; + union { + uint8_t saturation; + uint8_t s; + }; + union { + uint8_t value; + uint8_t v; + }; + }; + uint8_t raw[3]; + }; + inline ESPHSVColor() ALWAYS_INLINE : h(0), s(0), v(0) { // NOLINT + } + inline ESPHSVColor(uint8_t hue, uint8_t saturation, uint8_t value) ALWAYS_INLINE : hue(hue), + saturation(saturation), + value(value) {} + Color to_rgb() const; +}; + +} // namespace light +} // namespace esphome diff --git a/esphome/components/light/esp_range_view.cpp b/esphome/components/light/esp_range_view.cpp new file mode 100644 index 0000000000..e1f0a507bd --- /dev/null +++ b/esphome/components/light/esp_range_view.cpp @@ -0,0 +1,96 @@ +#include "esp_range_view.h" +#include "addressable_light.h" + +namespace esphome { +namespace light { + +int32_t HOT interpret_index(int32_t index, int32_t size) { + if (index < 0) + return size + index; + return index; +} + +ESPColorView ESPRangeView::operator[](int32_t index) const { + index = interpret_index(index, this->size()) + this->begin_; + return (*this->parent_)[index]; +} +ESPRangeIterator ESPRangeView::begin() { return {*this, this->begin_}; } +ESPRangeIterator ESPRangeView::end() { return {*this, this->end_}; } + +void ESPRangeView::set(const Color &color) { + for (int32_t i = this->begin_; i < this->end_; i++) { + (*this->parent_)[i] = color; + } +} + +void ESPRangeView::set_red(uint8_t red) { + for (auto c : *this) + c.set_red(red); +} +void ESPRangeView::set_green(uint8_t green) { + for (auto c : *this) + c.set_green(green); +} +void ESPRangeView::set_blue(uint8_t blue) { + for (auto c : *this) + c.set_blue(blue); +} +void ESPRangeView::set_white(uint8_t white) { + for (auto c : *this) + c.set_white(white); +} +void ESPRangeView::set_effect_data(uint8_t effect_data) { + for (auto c : *this) + c.set_effect_data(effect_data); +} + +void ESPRangeView::fade_to_white(uint8_t amnt) { + for (auto c : *this) + c.fade_to_white(amnt); +} +void ESPRangeView::fade_to_black(uint8_t amnt) { + for (auto c : *this) + c.fade_to_black(amnt); +} +void ESPRangeView::lighten(uint8_t delta) { + for (auto c : *this) + c.lighten(delta); +} +void ESPRangeView::darken(uint8_t delta) { + for (auto c : *this) + c.darken(delta); +} +ESPRangeView &ESPRangeView::operator=(const ESPRangeView &rhs) { // NOLINT + // If size doesn't match, error (todo warning) + if (rhs.size() != this->size()) + return *this; + + if (this->parent_ != rhs.parent_) { + for (int32_t i = 0; i < this->size(); i++) + (*this)[i].set(rhs[i].get()); + return *this; + } + + // If both equal, already done + if (rhs.begin_ == this->begin_) + return *this; + + if (rhs.begin_ > this->begin_) { + // Copy from left + for (int32_t i = 0; i < this->size(); i++) { + (*this)[i].set(rhs[i].get()); + } + } else { + // Copy from right + for (int32_t i = this->size() - 1; i >= 0; i--) { + (*this)[i].set(rhs[i].get()); + } + } + + return *this; +} + +ESPColorView ESPRangeIterator::operator*() const { return this->range_.parent_->get(this->i_); } + +} // namespace light +} // namespace esphome diff --git a/esphome/components/light/esp_range_view.h b/esphome/components/light/esp_range_view.h new file mode 100644 index 0000000000..f2cc347176 --- /dev/null +++ b/esphome/components/light/esp_range_view.h @@ -0,0 +1,75 @@ +#pragma once + +#include "esp_color_view.h" +#include "esp_hsv_color.h" + +namespace esphome { +namespace light { + +int32_t interpret_index(int32_t index, int32_t size); + +class AddressableLight; +class ESPRangeIterator; + +class ESPRangeView : public ESPColorSettable { + public: + ESPRangeView(AddressableLight *parent, int32_t begin, int32_t end) + : parent_(parent), begin_(begin), end_(end < begin ? begin : end) {} + + int32_t size() const { return this->end_ - this->begin_; } + ESPColorView operator[](int32_t index) const; + ESPRangeIterator begin(); + ESPRangeIterator end(); + + void set(const Color &color) override; + void set(const ESPHSVColor &color) { this->set(color.to_rgb()); } + void set_red(uint8_t red) override; + void set_green(uint8_t green) override; + void set_blue(uint8_t blue) override; + void set_white(uint8_t white) override; + void set_effect_data(uint8_t effect_data) override; + + void fade_to_white(uint8_t amnt) override; + void fade_to_black(uint8_t amnt) override; + void lighten(uint8_t delta) override; + void darken(uint8_t delta) override; + + ESPRangeView &operator=(const Color &rhs) { + this->set(rhs); + return *this; + } + ESPRangeView &operator=(const ESPColorView &rhs) { + this->set(rhs.get()); + return *this; + } + ESPRangeView &operator=(const ESPHSVColor &rhs) { + this->set_hsv(rhs); + return *this; + } + ESPRangeView &operator=(const ESPRangeView &rhs); + + protected: + friend ESPRangeIterator; + + AddressableLight *parent_; + int32_t begin_; + int32_t end_; +}; + +class ESPRangeIterator { + public: + ESPRangeIterator(const ESPRangeView &range, int32_t i) : range_(range), i_(i) {} + ESPRangeIterator operator++() { + this->i_++; + return *this; + } + bool operator!=(const ESPRangeIterator &other) const { return this->i_ != other.i_; } + ESPColorView operator*() const; + + protected: + ESPRangeView range_; + int32_t i_; +}; + +} // namespace light +} // namespace esphome diff --git a/esphome/components/light/light_call.cpp b/esphome/components/light/light_call.cpp new file mode 100644 index 0000000000..d7e8ce6298 --- /dev/null +++ b/esphome/components/light/light_call.cpp @@ -0,0 +1,570 @@ +#include "light_call.h" +#include "light_state.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace light { + +static const char *const TAG = "light"; + +#ifdef USE_JSON +LightCall &LightCall::parse_color_json(JsonObject &root) { + if (root.containsKey("state")) { + auto val = parse_on_off(root["state"]); + switch (val) { + case PARSE_ON: + this->set_state(true); + break; + case PARSE_OFF: + this->set_state(false); + break; + case PARSE_TOGGLE: + this->set_state(!this->parent_->remote_values.is_on()); + break; + case PARSE_NONE: + break; + } + } + + if (root.containsKey("brightness")) { + this->set_brightness(float(root["brightness"]) / 255.0f); + } + + if (root.containsKey("color")) { + JsonObject &color = root["color"]; + // HA also encodes brightness information in the r, g, b values, so extract that and set it as color brightness. + float max_rgb = 0.0f; + if (color.containsKey("r")) { + float r = float(color["r"]) / 255.0f; + max_rgb = fmaxf(max_rgb, r); + this->set_red(r); + } + if (color.containsKey("g")) { + float g = float(color["g"]) / 255.0f; + max_rgb = fmaxf(max_rgb, g); + this->set_green(g); + } + if (color.containsKey("b")) { + float b = float(color["b"]) / 255.0f; + max_rgb = fmaxf(max_rgb, b); + this->set_blue(b); + } + if (color.containsKey("r") || color.containsKey("g") || color.containsKey("b")) { + this->set_color_brightness(max_rgb); + } + } + + if (root.containsKey("white_value")) { + this->set_white(float(root["white_value"]) / 255.0f); + } + + if (root.containsKey("color_temp")) { + this->set_color_temperature(float(root["color_temp"])); + } + + return *this; +} +LightCall &LightCall::parse_json(JsonObject &root) { + this->parse_color_json(root); + + if (root.containsKey("flash")) { + auto length = uint32_t(float(root["flash"]) * 1000); + this->set_flash_length(length); + } + + if (root.containsKey("transition")) { + auto length = uint32_t(float(root["transition"]) * 1000); + this->set_transition_length(length); + } + + if (root.containsKey("effect")) { + const char *effect = root["effect"]; + this->set_effect(effect); + } + + return *this; +} +#endif + +void LightCall::perform() { + // use remote values for fallback + const char *name = this->parent_->get_name().c_str(); + if (this->publish_) { + ESP_LOGD(TAG, "'%s' Setting:", name); + } + + LightColorValues v = this->validate_(); + + if (this->publish_) { + // Only print state when it's being changed + bool current_state = this->parent_->remote_values.is_on(); + if (this->state_.value_or(current_state) != current_state) { + ESP_LOGD(TAG, " State: %s", ONOFF(v.is_on())); + } + + if (this->brightness_.has_value()) { + ESP_LOGD(TAG, " Brightness: %.0f%%", v.get_brightness() * 100.0f); + } + + if (this->color_temperature_.has_value()) { + ESP_LOGD(TAG, " Color Temperature: %.1f mireds", v.get_color_temperature()); + } + + if (this->red_.has_value() || this->green_.has_value() || this->blue_.has_value()) { + ESP_LOGD(TAG, " Red=%.0f%%, Green=%.0f%%, Blue=%.0f%%", v.get_red() * 100.0f, v.get_green() * 100.0f, + v.get_blue() * 100.0f); + } + if (this->white_.has_value()) { + ESP_LOGD(TAG, " White Value: %.0f%%", v.get_white() * 100.0f); + } + } + + if (this->has_flash_()) { + // FLASH + if (this->publish_) { + ESP_LOGD(TAG, " Flash Length: %.1fs", *this->flash_length_ / 1e3f); + } + + this->parent_->start_flash_(v, *this->flash_length_); + } else if (this->has_transition_()) { + // TRANSITION + if (this->publish_) { + ESP_LOGD(TAG, " Transition Length: %.1fs", *this->transition_length_ / 1e3f); + } + + // Special case: Transition and effect can be set when turning off + if (this->has_effect_()) { + if (this->publish_) { + ESP_LOGD(TAG, " Effect: 'None'"); + } + this->parent_->stop_effect_(); + } + + this->parent_->start_transition_(v, *this->transition_length_); + + } else if (this->has_effect_()) { + // EFFECT + auto effect = this->effect_; + const char *effect_s; + if (effect == 0) + effect_s = "None"; + else + effect_s = this->parent_->effects_[*this->effect_ - 1]->get_name().c_str(); + + if (this->publish_) { + ESP_LOGD(TAG, " Effect: '%s'", effect_s); + } + + this->parent_->start_effect_(*this->effect_); + + // Also set light color values when starting an effect + // For example to turn off the light + this->parent_->set_immediately_(v, true); + } else { + // INSTANT CHANGE + this->parent_->set_immediately_(v, this->publish_); + } + + if (!this->has_transition_()) { + this->parent_->target_state_reached_callback_.call(); + } + if (this->publish_) { + this->parent_->publish_state(); + } + if (this->save_) { + this->parent_->save_remote_values_(); + } +} + +LightColorValues LightCall::validate_() { + // use remote values for fallback + auto *name = this->parent_->get_name().c_str(); + auto traits = this->parent_->get_traits(); + + // Brightness exists check + if (this->brightness_.has_value() && !traits.get_supports_brightness()) { + ESP_LOGW(TAG, "'%s' - This light does not support setting brightness!", name); + this->brightness_.reset(); + } + + // Transition length possible check + if (this->transition_length_.has_value() && *this->transition_length_ != 0 && !traits.get_supports_brightness()) { + ESP_LOGW(TAG, "'%s' - This light does not support transitions!", name); + this->transition_length_.reset(); + } + + // Color brightness exists check + if (this->color_brightness_.has_value() && !traits.get_supports_rgb()) { + ESP_LOGW(TAG, "'%s' - This light does not support setting RGB brightness!", name); + this->color_brightness_.reset(); + } + + // RGB exists check + if (this->red_.has_value() || this->green_.has_value() || this->blue_.has_value()) { + if (!traits.get_supports_rgb()) { + ESP_LOGW(TAG, "'%s' - This light does not support setting RGB color!", name); + this->red_.reset(); + this->green_.reset(); + this->blue_.reset(); + } + } + + // White value exists check + if (this->white_.has_value() && !traits.get_supports_rgb_white_value()) { + ESP_LOGW(TAG, "'%s' - This light does not support setting white value!", name); + this->white_.reset(); + } + + // Color temperature exists check + if (this->color_temperature_.has_value() && !traits.get_supports_color_temperature()) { + ESP_LOGW(TAG, "'%s' - This light does not support setting color temperature!", name); + this->color_temperature_.reset(); + } + + // Set color brightness to 100% if currently zero and a color is set. This is both for compatibility with older + // clients that don't know about color brightness, and it's intuitive UX anyway: if I set a color, it should show up. + if (this->red_.has_value() || this->green_.has_value() || this->blue_.has_value()) { + if (!this->color_brightness_.has_value() && this->parent_->remote_values.get_color_brightness() == 0.0f) + this->color_brightness_ = optional(1.0f); + } + + // Handle interaction between RGB and white for color interlock + if (traits.get_supports_color_interlock()) { + // Find out which channel (white or color) the user wanted to enable + bool output_white = this->white_.has_value() && *this->white_ > 0.0f; + bool output_color = (this->color_brightness_.has_value() && *this->color_brightness_ > 0.0f) || + this->red_.has_value() || this->green_.has_value() || this->blue_.has_value(); + + // Interpret setting the color to white as setting the white channel. + if (output_color && *this->red_ == 1.0f && *this->green_ == 1.0f && *this->blue_ == 1.0f) { + output_white = true; + output_color = false; + + if (!this->white_.has_value()) + this->white_ = optional(this->color_brightness_.value_or(1.0f)); + } + + // Ensure either the white value or the color brightness is always zero. + if (output_white && output_color) { + ESP_LOGW(TAG, "'%s' - Cannot enable color and white channel simultaneously with interlock!", name); + // For compatibility with historic behaviour, prefer white channel in this case. + this->color_brightness_ = optional(0.0f); + } else if (output_white) { + this->color_brightness_ = optional(0.0f); + } else if (output_color) { + this->white_ = optional(0.0f); + } + } + + // If only a color temperature is specified, change to white light + if (this->color_temperature_.has_value() && !this->white_.has_value() && !this->red_.has_value() && + !this->green_.has_value() && !this->blue_.has_value()) { + // Disable color LEDs explicitly if not already set + if (traits.get_supports_rgb() && !this->color_brightness_.has_value()) + this->color_brightness_ = optional(0.0f); + + this->red_ = optional(1.0f); + this->green_ = optional(1.0f); + this->blue_ = optional(1.0f); + + // if setting color temperature from color (i.e. switching to white light), set White to 100% + auto cv = this->parent_->remote_values; + bool was_color = cv.get_red() != 1.0f || cv.get_blue() != 1.0f || cv.get_green() != 1.0f; + if (traits.get_supports_color_interlock() || was_color) { + this->white_ = optional(1.0f); + } + } + +#define VALIDATE_RANGE_(name_, upper_name) \ + if (name_##_.has_value()) { \ + auto val = *name_##_; \ + if (val < 0.0f || val > 1.0f) { \ + ESP_LOGW(TAG, "'%s' - %s value %.2f is out of range [0.0 - 1.0]!", name, upper_name, val); \ + name_##_ = clamp(val, 0.0f, 1.0f); \ + } \ + } +#define VALIDATE_RANGE(name, upper_name) VALIDATE_RANGE_(name, upper_name) + + // Range checks + VALIDATE_RANGE(brightness, "Brightness") + VALIDATE_RANGE(color_brightness, "Color brightness") + VALIDATE_RANGE(red, "Red") + VALIDATE_RANGE(green, "Green") + VALIDATE_RANGE(blue, "Blue") + VALIDATE_RANGE(white, "White") + + auto v = this->parent_->remote_values; + if (this->state_.has_value()) + v.set_state(*this->state_); + if (this->brightness_.has_value()) + v.set_brightness(*this->brightness_); + + if (this->color_brightness_.has_value()) + v.set_color_brightness(*this->color_brightness_); + if (this->red_.has_value()) + v.set_red(*this->red_); + if (this->green_.has_value()) + v.set_green(*this->green_); + if (this->blue_.has_value()) + v.set_blue(*this->blue_); + if (this->white_.has_value()) + v.set_white(*this->white_); + + if (this->color_temperature_.has_value()) + v.set_color_temperature(*this->color_temperature_); + + v.normalize_color(traits); + + // Flash length check + if (this->has_flash_() && *this->flash_length_ == 0) { + ESP_LOGW(TAG, "'%s' - Flash length must be greater than zero!", name); + this->flash_length_.reset(); + } + + // validate transition length/flash length/effect not used at the same time + bool supports_transition = traits.get_supports_brightness(); + + // If effect is already active, remove effect start + if (this->has_effect_() && *this->effect_ == this->parent_->active_effect_index_) { + this->effect_.reset(); + } + + // validate effect index + if (this->has_effect_() && *this->effect_ > this->parent_->effects_.size()) { + ESP_LOGW(TAG, "'%s' Invalid effect index %u", name, *this->effect_); + this->effect_.reset(); + } + + if (this->has_effect_() && (this->has_transition_() || this->has_flash_())) { + ESP_LOGW(TAG, "'%s' - Effect cannot be used together with transition/flash!", name); + this->transition_length_.reset(); + this->flash_length_.reset(); + } + + if (this->has_flash_() && this->has_transition_()) { + ESP_LOGW(TAG, "'%s' - Flash cannot be used together with transition!", name); + this->transition_length_.reset(); + } + + if (!this->has_transition_() && !this->has_flash_() && (!this->has_effect_() || *this->effect_ == 0) && + supports_transition) { + // nothing specified and light supports transitions, set default transition length + this->transition_length_ = this->parent_->default_transition_length_; + } + + if (this->transition_length_.value_or(0) == 0) { + // 0 transition is interpreted as no transition (instant change) + this->transition_length_.reset(); + } + + if (this->has_transition_() && !supports_transition) { + ESP_LOGW(TAG, "'%s' - Light does not support transitions!", name); + this->transition_length_.reset(); + } + + // If not a flash and turning the light off, then disable the light + // Do not use light color values directly, so that effects can set 0% brightness + // Reason: When user turns off the light in frontend, the effect should also stop + if (!this->has_flash_() && !this->state_.value_or(v.is_on())) { + if (this->has_effect_()) { + ESP_LOGW(TAG, "'%s' - Cannot start an effect when turning off!", name); + this->effect_.reset(); + } else if (this->parent_->active_effect_index_ != 0) { + // Auto turn off effect + this->effect_ = 0; + } + } + + // Disable saving for flashes + if (this->has_flash_()) + this->save_ = false; + + return v; +} +LightCall &LightCall::set_effect(const std::string &effect) { + if (strcasecmp(effect.c_str(), "none") == 0) { + this->set_effect(0); + return *this; + } + + bool found = false; + for (uint32_t i = 0; i < this->parent_->effects_.size(); i++) { + LightEffect *e = this->parent_->effects_[i]; + + if (strcasecmp(effect.c_str(), e->get_name().c_str()) == 0) { + this->set_effect(i + 1); + found = true; + break; + } + } + if (!found) { + ESP_LOGW(TAG, "'%s' - No such effect '%s'", this->parent_->get_name().c_str(), effect.c_str()); + } + return *this; +} +LightCall &LightCall::from_light_color_values(const LightColorValues &values) { + this->set_state(values.is_on()); + this->set_brightness_if_supported(values.get_brightness()); + this->set_color_brightness_if_supported(values.get_color_brightness()); + this->set_red_if_supported(values.get_red()); + this->set_green_if_supported(values.get_green()); + this->set_blue_if_supported(values.get_blue()); + this->set_white_if_supported(values.get_white()); + this->set_color_temperature_if_supported(values.get_color_temperature()); + return *this; +} +LightCall &LightCall::set_transition_length_if_supported(uint32_t transition_length) { + if (this->parent_->get_traits().get_supports_brightness()) + this->set_transition_length(transition_length); + return *this; +} +LightCall &LightCall::set_brightness_if_supported(float brightness) { + if (this->parent_->get_traits().get_supports_brightness()) + this->set_brightness(brightness); + return *this; +} +LightCall &LightCall::set_color_brightness_if_supported(float brightness) { + if (this->parent_->get_traits().get_supports_rgb_white_value()) + this->set_color_brightness(brightness); + return *this; +} +LightCall &LightCall::set_red_if_supported(float red) { + if (this->parent_->get_traits().get_supports_rgb()) + this->set_red(red); + return *this; +} +LightCall &LightCall::set_green_if_supported(float green) { + if (this->parent_->get_traits().get_supports_rgb()) + this->set_green(green); + return *this; +} +LightCall &LightCall::set_blue_if_supported(float blue) { + if (this->parent_->get_traits().get_supports_rgb()) + this->set_blue(blue); + return *this; +} +LightCall &LightCall::set_white_if_supported(float white) { + if (this->parent_->get_traits().get_supports_rgb_white_value()) + this->set_white(white); + return *this; +} +LightCall &LightCall::set_color_temperature_if_supported(float color_temperature) { + if (this->parent_->get_traits().get_supports_color_temperature()) + this->set_color_temperature(color_temperature); + return *this; +} +LightCall &LightCall::set_state(optional state) { + this->state_ = state; + return *this; +} +LightCall &LightCall::set_state(bool state) { + this->state_ = state; + return *this; +} +LightCall &LightCall::set_transition_length(optional transition_length) { + this->transition_length_ = transition_length; + return *this; +} +LightCall &LightCall::set_transition_length(uint32_t transition_length) { + this->transition_length_ = transition_length; + return *this; +} +LightCall &LightCall::set_flash_length(optional flash_length) { + this->flash_length_ = flash_length; + return *this; +} +LightCall &LightCall::set_flash_length(uint32_t flash_length) { + this->flash_length_ = flash_length; + return *this; +} +LightCall &LightCall::set_brightness(optional brightness) { + this->brightness_ = brightness; + return *this; +} +LightCall &LightCall::set_brightness(float brightness) { + this->brightness_ = brightness; + return *this; +} +LightCall &LightCall::set_color_brightness(optional brightness) { + this->color_brightness_ = brightness; + return *this; +} +LightCall &LightCall::set_color_brightness(float brightness) { + this->color_brightness_ = brightness; + return *this; +} +LightCall &LightCall::set_red(optional red) { + this->red_ = red; + return *this; +} +LightCall &LightCall::set_red(float red) { + this->red_ = red; + return *this; +} +LightCall &LightCall::set_green(optional green) { + this->green_ = green; + return *this; +} +LightCall &LightCall::set_green(float green) { + this->green_ = green; + return *this; +} +LightCall &LightCall::set_blue(optional blue) { + this->blue_ = blue; + return *this; +} +LightCall &LightCall::set_blue(float blue) { + this->blue_ = blue; + return *this; +} +LightCall &LightCall::set_white(optional white) { + this->white_ = white; + return *this; +} +LightCall &LightCall::set_white(float white) { + this->white_ = white; + return *this; +} +LightCall &LightCall::set_color_temperature(optional color_temperature) { + this->color_temperature_ = color_temperature; + return *this; +} +LightCall &LightCall::set_color_temperature(float color_temperature) { + this->color_temperature_ = color_temperature; + return *this; +} +LightCall &LightCall::set_effect(optional effect) { + if (effect.has_value()) + this->set_effect(*effect); + return *this; +} +LightCall &LightCall::set_effect(uint32_t effect_number) { + this->effect_ = effect_number; + return *this; +} +LightCall &LightCall::set_effect(optional effect_number) { + this->effect_ = effect_number; + return *this; +} +LightCall &LightCall::set_publish(bool publish) { + this->publish_ = publish; + return *this; +} +LightCall &LightCall::set_save(bool save) { + this->save_ = save; + return *this; +} +LightCall &LightCall::set_rgb(float red, float green, float blue) { + this->set_red(red); + this->set_green(green); + this->set_blue(blue); + return *this; +} +LightCall &LightCall::set_rgbw(float red, float green, float blue, float white) { + this->set_rgb(red, green, blue); + this->set_white(white); + return *this; +} + +} // namespace light +} // namespace esphome diff --git a/esphome/components/light/light_call.h b/esphome/components/light/light_call.h new file mode 100644 index 0000000000..c63b63bc54 --- /dev/null +++ b/esphome/components/light/light_call.h @@ -0,0 +1,167 @@ +#pragma once + +#include "esphome/core/optional.h" +#include "light_color_values.h" + +namespace esphome { +namespace light { + +class LightState; + +/** This class represents a requested change in a light state. + */ +class LightCall { + public: + explicit LightCall(LightState *parent) : parent_(parent) {} + + /// Set the binary ON/OFF state of the light. + LightCall &set_state(optional state); + /// Set the binary ON/OFF state of the light. + LightCall &set_state(bool state); + /** Set the transition length of this call in milliseconds. + * + * This argument is ignored for starting flashes and effects. + * + * Defaults to the default transition length defined in the light configuration. + */ + LightCall &set_transition_length(optional transition_length); + /** Set the transition length of this call in milliseconds. + * + * This argument is ignored for starting flashes and effects. + * + * Defaults to the default transition length defined in the light configuration. + */ + LightCall &set_transition_length(uint32_t transition_length); + /// Set the transition length property if the light supports transitions. + LightCall &set_transition_length_if_supported(uint32_t transition_length); + /// Start and set the flash length of this call in milliseconds. + LightCall &set_flash_length(optional flash_length); + /// Start and set the flash length of this call in milliseconds. + LightCall &set_flash_length(uint32_t flash_length); + /// Set the target brightness of the light from 0.0 (fully off) to 1.0 (fully on) + LightCall &set_brightness(optional brightness); + /// Set the target brightness of the light from 0.0 (fully off) to 1.0 (fully on) + LightCall &set_brightness(float brightness); + /// Set the brightness property if the light supports brightness. + LightCall &set_brightness_if_supported(float brightness); + /// Set the color brightness of the light from 0.0 (no color) to 1.0 (fully on) + LightCall &set_color_brightness(optional brightness); + /// Set the color brightness of the light from 0.0 (no color) to 1.0 (fully on) + LightCall &set_color_brightness(float brightness); + /// Set the color brightness property if the light supports RGBW. + LightCall &set_color_brightness_if_supported(float brightness); + /** Set the red RGB value of the light from 0.0 to 1.0. + * + * Note that this only controls the color of the light, not its brightness. + */ + LightCall &set_red(optional red); + /** Set the red RGB value of the light from 0.0 to 1.0. + * + * Note that this only controls the color of the light, not its brightness. + */ + LightCall &set_red(float red); + /// Set the red property if the light supports RGB. + LightCall &set_red_if_supported(float red); + /** Set the green RGB value of the light from 0.0 to 1.0. + * + * Note that this only controls the color of the light, not its brightness. + */ + LightCall &set_green(optional green); + /** Set the green RGB value of the light from 0.0 to 1.0. + * + * Note that this only controls the color of the light, not its brightness. + */ + LightCall &set_green(float green); + /// Set the green property if the light supports RGB. + LightCall &set_green_if_supported(float green); + /** Set the blue RGB value of the light from 0.0 to 1.0. + * + * Note that this only controls the color of the light, not its brightness. + */ + LightCall &set_blue(optional blue); + /** Set the blue RGB value of the light from 0.0 to 1.0. + * + * Note that this only controls the color of the light, not its brightness. + */ + LightCall &set_blue(float blue); + /// Set the blue property if the light supports RGB. + LightCall &set_blue_if_supported(float blue); + /// Set the white value value of the light from 0.0 to 1.0 for RGBW[W] lights. + LightCall &set_white(optional white); + /// Set the white value value of the light from 0.0 to 1.0 for RGBW[W] lights. + LightCall &set_white(float white); + /// Set the white property if the light supports RGB. + LightCall &set_white_if_supported(float white); + /// Set the color temperature of the light in mireds for CWWW or RGBWW lights. + LightCall &set_color_temperature(optional color_temperature); + /// Set the color temperature of the light in mireds for CWWW or RGBWW lights. + LightCall &set_color_temperature(float color_temperature); + /// Set the color_temperature property if the light supports color temperature. + LightCall &set_color_temperature_if_supported(float color_temperature); + /// Set the effect of the light by its name. + LightCall &set_effect(optional effect); + /// Set the effect of the light by its name. + LightCall &set_effect(const std::string &effect); + /// Set the effect of the light by its internal index number (only for internal use). + LightCall &set_effect(uint32_t effect_number); + LightCall &set_effect(optional effect_number); + /// Set whether this light call should trigger a publish state. + LightCall &set_publish(bool publish); + /// Set whether this light call should trigger a save state to recover them at startup.. + LightCall &set_save(bool save); + + /** Set the RGB color of the light by RGB values. + * + * Please note that this only changes the color of the light, not the brightness. + * + * @param red The red color value from 0.0 to 1.0. + * @param green The green color value from 0.0 to 1.0. + * @param blue The blue color value from 0.0 to 1.0. + * @return The light call for chaining setters. + */ + LightCall &set_rgb(float red, float green, float blue); + /** Set the RGBW color of the light by RGB values. + * + * Please note that this only changes the color of the light, not the brightness. + * + * @param red The red color value from 0.0 to 1.0. + * @param green The green color value from 0.0 to 1.0. + * @param blue The blue color value from 0.0 to 1.0. + * @param white The white color value from 0.0 to 1.0. + * @return The light call for chaining setters. + */ + LightCall &set_rgbw(float red, float green, float blue, float white); +#ifdef USE_JSON + LightCall &parse_color_json(JsonObject &root); + LightCall &parse_json(JsonObject &root); +#endif + LightCall &from_light_color_values(const LightColorValues &values); + + void perform(); + + protected: + /// Validate all properties and return the target light color values. + LightColorValues validate_(); + + bool has_transition_() { return this->transition_length_.has_value(); } + bool has_flash_() { return this->flash_length_.has_value(); } + bool has_effect_() { return this->effect_.has_value(); } + + LightState *parent_; + optional state_; + optional transition_length_; + optional flash_length_; + optional brightness_; + optional color_brightness_; + optional red_; + optional green_; + optional blue_; + optional white_; + optional color_temperature_; + optional effect_; + bool publish_{true}; + bool save_{true}; +}; + +} // namespace light +} // namespace esphome diff --git a/esphome/components/light/light_color_values.h b/esphome/components/light/light_color_values.h index cdd05ae7b7..54dcaea5a3 100644 --- a/esphome/components/light/light_color_values.h +++ b/esphome/components/light/light_color_values.h @@ -13,17 +13,24 @@ namespace light { /** This class represents the color state for a light object. * - * All values in this class are represented using floats in the range from 0.0 (off) to 1.0 (on). - * Not all values have to be populated though, for example a simple monochromatic light only needs - * to access the state and brightness attributes. + * All values in this class (except color temperature) are represented using floats in the range + * from 0.0 (off) to 1.0 (on). Please note that all values are automatically clamped to this range. * - * Please note all float values are automatically clamped. + * This class has the following properties: + * - state: Whether the light should be on/off. Represented as a float for transitions. Used for + * all lights. + * - brightness: The master brightness of the light, applied to all channels. Used for all lights + * with brightness control. + * - color_brightness: The brightness of the color channels of the light. Used for RGB, RGBW and + * RGBWW lights. + * - red, green, blue: The RGB values of the current color. They are normalized, so at least one of + * them is always 1.0. + * - white: The brightness of the white channel of the light. Used for RGBW and RGBWW lights. + * - color_temperature: The color temperature of the white channel in mireds. Used for RGBWW and + * CWWW lights. * - * state - Whether the light should be on/off. Represented as a float for transitions. - * brightness - The brightness of the light. - * red, green, blue - RGB values. - * white - The white value for RGBW lights. - * color_temperature - Temperature of the white value, range from 0.0 (cold) to 1.0 (warm) + * For lights with a color interlock (RGB lights and white light cannot be on at the same time), a + * valid state has always either color_brightness or white (or both) set to zero. */ class LightColorValues { public: @@ -31,16 +38,18 @@ class LightColorValues { LightColorValues() : state_(0.0f), brightness_(1.0f), + color_brightness_(1.0f), red_(1.0f), green_(1.0f), blue_(1.0f), white_(1.0f), color_temperature_{1.0f} {} - LightColorValues(float state, float brightness, float red, float green, float blue, float white, - float color_temperature = 1.0f) { + LightColorValues(float state, float brightness, float color_brightness, float red, float green, float blue, + float white, float color_temperature = 1.0f) { this->set_state(state); this->set_brightness(brightness); + this->set_color_brightness(color_brightness); this->set_red(red); this->set_green(green); this->set_blue(blue); @@ -48,38 +57,46 @@ class LightColorValues { this->set_color_temperature(color_temperature); } - LightColorValues(bool state, float brightness, float red, float green, float blue, float white, - float color_temperature = 1.0f) - : LightColorValues(state ? 1.0f : 0.0f, brightness, red, green, blue, white, color_temperature) {} + LightColorValues(bool state, float brightness, float color_brightness, float red, float green, float blue, + float white, float color_temperature = 1.0f) + : LightColorValues(state ? 1.0f : 0.0f, brightness, color_brightness, red, green, blue, white, + color_temperature) {} /// Create light color values from a binary true/false state. - static LightColorValues from_binary(bool state) { return {state, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}; } + static LightColorValues from_binary(bool state) { return {state, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}; } /// Create light color values from a monochromatic brightness state. static LightColorValues from_monochromatic(float brightness) { if (brightness == 0.0f) - return {0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}; + return {0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}; else - return {1.0f, brightness, 1.0f, 1.0f, 1.0f, 1.0f}; + return {1.0f, brightness, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}; } /// Create light color values from an RGB state. static LightColorValues from_rgb(float r, float g, float b) { float brightness = std::max(r, std::max(g, b)); if (brightness == 0.0f) { - return {0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}; + return {0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}; } else { - return {1.0f, brightness, r / brightness, g / brightness, b / brightness, 1.0f}; + return {1.0f, brightness, 1.0f, r / brightness, g / brightness, b / brightness, 1.0f}; } } /// Create light color values from an RGBW state. static LightColorValues from_rgbw(float r, float g, float b, float w) { - float brightness = std::max(r, std::max(g, std::max(b, w))); - if (brightness == 0.0f) { - return {0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}; + float color_brightness = std::max(r, std::max(g, b)); + float master_brightness = std::max(color_brightness, w); + if (master_brightness == 0.0f) { + return {0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}; } else { - return {1.0f, brightness, r / brightness, g / brightness, b / brightness, w / brightness}; + return {1.0f, + master_brightness, + color_brightness / master_brightness, + r / color_brightness, + g / color_brightness, + b / color_brightness, + w / master_brightness}; } } @@ -97,6 +114,7 @@ class LightColorValues { LightColorValues v; v.set_state(esphome::lerp(completion, start.get_state(), end.get_state())); v.set_brightness(esphome::lerp(completion, start.get_brightness(), end.get_brightness())); + v.set_color_brightness(esphome::lerp(completion, start.get_color_brightness(), end.get_color_brightness())); v.set_red(esphome::lerp(completion, start.get_red(), end.get_red())); v.set_green(esphome::lerp(completion, start.get_green(), end.get_green())); v.set_blue(esphome::lerp(completion, start.get_blue(), end.get_blue())); @@ -117,12 +135,13 @@ class LightColorValues { root["brightness"] = uint8_t(this->get_brightness() * 255); if (traits.get_supports_rgb()) { JsonObject &color = root.createNestedObject("color"); - color["r"] = uint8_t(this->get_red() * 255); - color["g"] = uint8_t(this->get_green() * 255); - color["b"] = uint8_t(this->get_blue() * 255); + color["r"] = uint8_t(this->get_color_brightness() * this->get_red() * 255); + color["g"] = uint8_t(this->get_color_brightness() * this->get_green() * 255); + color["b"] = uint8_t(this->get_color_brightness() * this->get_blue() * 255); } - if (traits.get_supports_rgb_white_value()) + if (traits.get_supports_rgb_white_value()) { root["white_value"] = uint8_t(this->get_white() * 255); + } if (traits.get_supports_color_temperature()) root["color_temp"] = uint32_t(this->get_color_temperature()); } @@ -131,21 +150,15 @@ class LightColorValues { /** Normalize the color (RGB/W) component. * * Divides all color attributes by the maximum attribute, so effectively set at least one attribute to 1. - * For example: r=0.3, g=0.5, b=0.4 => r=0.6, g=1.0, b=0.8 + * For example: r=0.3, g=0.5, b=0.4 => r=0.6, g=1.0, b=0.8. + * + * Note that this does NOT retain the brightness information from the color attributes. * * @param traits Used for determining which attributes to consider. */ void normalize_color(const LightTraits &traits) { if (traits.get_supports_rgb()) { float max_value = fmaxf(this->get_red(), fmaxf(this->get_green(), this->get_blue())); - if (traits.get_supports_rgb_white_value()) { - max_value = fmaxf(max_value, this->get_white()); - if (max_value == 0.0f) { - this->set_white(1.0f); - } else { - this->set_white(this->get_white() / max_value); - } - } if (max_value == 0.0f) { this->set_red(1.0f); this->set_green(1.0f); @@ -158,15 +171,10 @@ class LightColorValues { } if (traits.get_supports_brightness() && this->get_brightness() == 0.0f) { - if (traits.get_supports_rgb_white_value()) { - // 0% brightness for RGBW[W] means no RGB channel, but white channel on. - // do nothing - } else { - // 0% brightness means off - this->set_state(false); - // reset brightness to 100% - this->set_brightness(1.0f); - } + // 0% brightness means off + this->set_state(false); + // reset brightness to 100% + this->set_brightness(1.0f); } } @@ -180,9 +188,9 @@ class LightColorValues { /// Convert these light color values to an RGB representation and write them to red, green, blue. void as_rgb(float *red, float *green, float *blue, float gamma = 0, bool color_interlock = false) const { - float brightness = this->state_ * this->brightness_; - if (color_interlock) { - brightness = brightness * (1.0f - this->white_); + float brightness = this->state_ * this->brightness_ * this->color_brightness_; + if (color_interlock && this->white_ > 0.0f) { + brightness = 0; } *red = gamma_correct(brightness * this->red_, gamma); *green = gamma_correct(brightness * this->green_, gamma); @@ -232,8 +240,9 @@ class LightColorValues { /// Compare this LightColorValues to rhs, return true if and only if all attributes match. bool operator==(const LightColorValues &rhs) const { - return state_ == rhs.state_ && brightness_ == rhs.brightness_ && red_ == rhs.red_ && green_ == rhs.green_ && - blue_ == rhs.blue_ && white_ == rhs.white_ && color_temperature_ == rhs.color_temperature_; + return state_ == rhs.state_ && brightness_ == rhs.brightness_ && color_brightness_ == rhs.color_brightness_ && + red_ == rhs.red_ && green_ == rhs.green_ && blue_ == rhs.blue_ && white_ == rhs.white_ && + color_temperature_ == rhs.color_temperature_; } bool operator!=(const LightColorValues &rhs) const { return !(rhs == *this); } @@ -251,6 +260,11 @@ class LightColorValues { /// Set the brightness property of these light color values. In range 0.0 to 1.0 void set_brightness(float brightness) { this->brightness_ = clamp(brightness, 0.0f, 1.0f); } + /// Get the color brightness property of these light color values. In range 0.0 to 1.0 + float get_color_brightness() const { return this->color_brightness_; } + /// Set the color brightness property of these light color values. In range 0.0 to 1.0 + void set_color_brightness(float brightness) { this->color_brightness_ = clamp(brightness, 0.0f, 1.0f); } + /// Get the red property of these light color values. In range 0.0 to 1.0 float get_red() const { return this->red_; } /// Set the red property of these light color values. In range 0.0 to 1.0 @@ -281,6 +295,7 @@ class LightColorValues { protected: float state_; ///< ON / OFF, float for transition float brightness_; + float color_brightness_; float red_; float green_; float blue_; diff --git a/esphome/components/light/light_effect.h b/esphome/components/light/light_effect.h index bef2562f55..f9903397b4 100644 --- a/esphome/components/light/light_effect.h +++ b/esphome/components/light/light_effect.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "esphome/core/component.h" #include "light_color_values.h" #include "light_state.h" @@ -11,7 +13,7 @@ class LightState; class LightEffect { public: - explicit LightEffect(const std::string &name) : name_(name) {} + explicit LightEffect(std::string name) : name_(std::move(name)) {} /// Initialize this LightEffect. Will be called once after creation. virtual void start() {} diff --git a/esphome/components/light/light_state.cpp b/esphome/components/light/light_state.cpp index 749dea5419..e32b15daf1 100644 --- a/esphome/components/light/light_state.cpp +++ b/esphome/components/light/light_state.cpp @@ -5,86 +5,20 @@ namespace esphome { namespace light { -static const char *TAG = "light"; - -void LightState::start_transition_(const LightColorValues &target, uint32_t length) { - this->transformer_ = make_unique(millis(), length, this->current_values, target); - this->remote_values = this->transformer_->get_remote_values(); -} - -void LightState::start_flash_(const LightColorValues &target, uint32_t length) { - LightColorValues end_colors = this->current_values; - // If starting a flash if one is already happening, set end values to end values of current flash - // Hacky but works - if (this->transformer_ != nullptr) - end_colors = this->transformer_->get_end_values(); - this->transformer_ = make_unique(millis(), length, end_colors, target); - this->remote_values = this->transformer_->get_remote_values(); -} +static const char *const TAG = "light"; LightState::LightState(const std::string &name, LightOutput *output) : Nameable(name), output_(output) {} -void LightState::set_immediately_(const LightColorValues &target, bool set_remote_values) { - this->transformer_ = nullptr; - this->current_values = target; - if (set_remote_values) { - this->remote_values = target; - } - this->next_write_ = true; -} - -LightColorValues LightState::get_current_values() { return this->current_values; } - -void LightState::publish_state() { - this->remote_values_callback_.call(); - this->next_write_ = true; -} - -LightColorValues LightState::get_remote_values() { return this->remote_values; } - -std::string LightState::get_effect_name() { - if (this->active_effect_index_ > 0) - return this->effects_[this->active_effect_index_ - 1]->get_name(); - else - return "None"; -} - -void LightState::start_effect_(uint32_t effect_index) { - this->stop_effect_(); - if (effect_index == 0) - return; - - this->active_effect_index_ = effect_index; - auto *effect = this->get_active_effect_(); - effect->start_internal(); -} - -bool LightState::supports_effects() { return !this->effects_.empty(); } -void LightState::set_transformer_(std::unique_ptr transformer) { - this->transformer_ = std::move(transformer); -} -void LightState::stop_effect_() { - auto *effect = this->get_active_effect_(); - if (effect != nullptr) { - effect->stop(); - } - this->active_effect_index_ = 0; -} - -void LightState::set_default_transition_length(uint32_t default_transition_length) { - this->default_transition_length_ = default_transition_length; -} -#ifdef USE_JSON -void LightState::dump_json(JsonObject &root) { - if (this->supports_effects()) - root["effect"] = this->get_effect_name(); - this->remote_values.dump_json(root, this->output_->get_traits()); -} -#endif +LightTraits LightState::get_traits() { return this->output_->get_traits(); } +LightCall LightState::turn_on() { return this->make_call().set_state(true); } +LightCall LightState::turn_off() { return this->make_call().set_state(false); } +LightCall LightState::toggle() { return this->make_call().set_state(!this->remote_values.is_on()); } +LightCall LightState::make_call() { return LightCall(this); } struct LightStateRTCState { bool state{false}; float brightness{1.0f}; + float color_brightness{1.0f}; float red{1.0f}; float green{1.0f}; float blue{1.0f}; @@ -132,6 +66,7 @@ void LightState::setup() { call.set_state(recovered.state); call.set_brightness_if_supported(recovered.brightness); + call.set_color_brightness_if_supported(recovered.color_brightness); call.set_red_if_supported(recovered.red); call.set_green_if_supported(recovered.green); call.set_blue_if_supported(recovered.blue); @@ -144,6 +79,17 @@ void LightState::setup() { } call.perform(); } +void LightState::dump_config() { + ESP_LOGCONFIG(TAG, "Light '%s'", this->get_name().c_str()); + if (this->get_traits().get_supports_brightness()) { + ESP_LOGCONFIG(TAG, " Default Transition Length: %.1fs", this->default_transition_length_ / 1e3f); + ESP_LOGCONFIG(TAG, " Gamma Correct: %.2f", this->gamma_correct_); + } + if (this->get_traits().get_supports_color_temperature()) { + ESP_LOGCONFIG(TAG, " Min Mireds: %.1f", this->get_traits().get_min_mireds()); + ESP_LOGCONFIG(TAG, " Max Mireds: %.1f", this->get_traits().get_max_mireds()); + } +} void LightState::loop() { // Apply effect (if any) auto *effect = this->get_active_effect_(); @@ -171,559 +117,52 @@ void LightState::loop() { this->next_write_ = false; } } -LightTraits LightState::get_traits() { return this->output_->get_traits(); } + +float LightState::get_setup_priority() const { return setup_priority::HARDWARE - 1.0f; } +uint32_t LightState::hash_base() { return 1114400283; } + +void LightState::publish_state() { + this->remote_values_callback_.call(); + this->next_write_ = true; +} + +LightOutput *LightState::get_output() const { return this->output_; } +std::string LightState::get_effect_name() { + if (this->active_effect_index_ > 0) + return this->effects_[this->active_effect_index_ - 1]->get_name(); + else + return "None"; +} + +void LightState::add_new_remote_values_callback(std::function &&send_callback) { + this->remote_values_callback_.add(std::move(send_callback)); +} +void LightState::add_new_target_state_reached_callback(std::function &&send_callback) { + this->target_state_reached_callback_.add(std::move(send_callback)); +} + +#ifdef USE_JSON +void LightState::dump_json(JsonObject &root) { + if (this->supports_effects()) + root["effect"] = this->get_effect_name(); + this->remote_values.dump_json(root, this->output_->get_traits()); +} +#endif + +void LightState::set_default_transition_length(uint32_t default_transition_length) { + this->default_transition_length_ = default_transition_length; +} +void LightState::set_gamma_correct(float gamma_correct) { this->gamma_correct_ = gamma_correct; } +void LightState::set_restore_mode(LightRestoreMode restore_mode) { this->restore_mode_ = restore_mode; } +bool LightState::supports_effects() { return !this->effects_.empty(); } const std::vector &LightState::get_effects() const { return this->effects_; } -void LightState::add_effects(const std::vector effects) { +void LightState::add_effects(const std::vector &effects) { this->effects_.reserve(this->effects_.size() + effects.size()); for (auto *effect : effects) { this->effects_.push_back(effect); } } -LightCall LightState::turn_on() { return this->make_call().set_state(true); } -LightCall LightState::turn_off() { return this->make_call().set_state(false); } -LightCall LightState::toggle() { return this->make_call().set_state(!this->remote_values.is_on()); } -LightCall LightState::make_call() { return LightCall(this); } -uint32_t LightState::hash_base() { return 1114400283; } -void LightState::dump_config() { - ESP_LOGCONFIG(TAG, "Light '%s'", this->get_name().c_str()); - if (this->get_traits().get_supports_brightness()) { - ESP_LOGCONFIG(TAG, " Default Transition Length: %.1fs", this->default_transition_length_ / 1e3f); - ESP_LOGCONFIG(TAG, " Gamma Correct: %.2f", this->gamma_correct_); - } - if (this->get_traits().get_supports_color_temperature()) { - ESP_LOGCONFIG(TAG, " Min Mireds: %.1f", this->get_traits().get_min_mireds()); - ESP_LOGCONFIG(TAG, " Max Mireds: %.1f", this->get_traits().get_max_mireds()); - } -} -#ifdef USE_MQTT_LIGHT -MQTTJSONLightComponent *LightState::get_mqtt() const { return this->mqtt_; } -void LightState::set_mqtt(MQTTJSONLightComponent *mqtt) { this->mqtt_ = mqtt; } -#endif -#ifdef USE_JSON -LightCall &LightCall::parse_color_json(JsonObject &root) { - if (root.containsKey("state")) { - auto val = parse_on_off(root["state"]); - switch (val) { - case PARSE_ON: - this->set_state(true); - break; - case PARSE_OFF: - this->set_state(false); - break; - case PARSE_TOGGLE: - this->set_state(!this->parent_->remote_values.is_on()); - break; - case PARSE_NONE: - break; - } - } - - if (root.containsKey("brightness")) { - this->set_brightness(float(root["brightness"]) / 255.0f); - } - - if (root.containsKey("color")) { - JsonObject &color = root["color"]; - if (color.containsKey("r")) { - this->set_red(float(color["r"]) / 255.0f); - } - if (color.containsKey("g")) { - this->set_green(float(color["g"]) / 255.0f); - } - if (color.containsKey("b")) { - this->set_blue(float(color["b"]) / 255.0f); - } - } - - if (root.containsKey("white_value")) { - this->set_white(float(root["white_value"]) / 255.0f); - } - - if (root.containsKey("color_temp")) { - this->set_color_temperature(float(root["color_temp"])); - } - - return *this; -} -LightCall &LightCall::parse_json(JsonObject &root) { - this->parse_color_json(root); - - if (root.containsKey("flash")) { - auto length = uint32_t(float(root["flash"]) * 1000); - this->set_flash_length(length); - } - - if (root.containsKey("transition")) { - auto length = uint32_t(float(root["transition"]) * 1000); - this->set_transition_length(length); - } - - if (root.containsKey("effect")) { - const char *effect = root["effect"]; - this->set_effect(effect); - } - - return *this; -} -#endif - -void LightCall::perform() { - // use remote values for fallback - const char *name = this->parent_->get_name().c_str(); - if (this->publish_) { - ESP_LOGD(TAG, "'%s' Setting:", name); - } - - LightColorValues v = this->validate_(); - - if (this->publish_) { - // Only print state when it's being changed - bool current_state = this->parent_->remote_values.is_on(); - if (this->state_.value_or(current_state) != current_state) { - ESP_LOGD(TAG, " State: %s", ONOFF(v.is_on())); - } - - if (this->brightness_.has_value()) { - ESP_LOGD(TAG, " Brightness: %.0f%%", v.get_brightness() * 100.0f); - } - - if (this->color_temperature_.has_value()) { - ESP_LOGD(TAG, " Color Temperature: %.1f mireds", v.get_color_temperature()); - } - - if (this->red_.has_value() || this->green_.has_value() || this->blue_.has_value()) { - ESP_LOGD(TAG, " Red=%.0f%%, Green=%.0f%%, Blue=%.0f%%", v.get_red() * 100.0f, v.get_green() * 100.0f, - v.get_blue() * 100.0f); - } - if (this->white_.has_value()) { - ESP_LOGD(TAG, " White Value: %.0f%%", v.get_white() * 100.0f); - } - } - - if (this->has_flash_()) { - // FLASH - if (this->publish_) { - ESP_LOGD(TAG, " Flash Length: %.1fs", *this->flash_length_ / 1e3f); - } - - this->parent_->start_flash_(v, *this->flash_length_); - } else if (this->has_transition_()) { - // TRANSITION - if (this->publish_) { - ESP_LOGD(TAG, " Transition Length: %.1fs", *this->transition_length_ / 1e3f); - } - - // Special case: Transition and effect can be set when turning off - if (this->has_effect_()) { - if (this->publish_) { - ESP_LOGD(TAG, " Effect: 'None'"); - } - this->parent_->stop_effect_(); - } - - this->parent_->start_transition_(v, *this->transition_length_); - - } else if (this->has_effect_()) { - // EFFECT - auto effect = this->effect_; - const char *effect_s; - if (effect == 0) - effect_s = "None"; - else - effect_s = this->parent_->effects_[*this->effect_ - 1]->get_name().c_str(); - - if (this->publish_) { - ESP_LOGD(TAG, " Effect: '%s'", effect_s); - } - - this->parent_->start_effect_(*this->effect_); - - // Also set light color values when starting an effect - // For example to turn off the light - this->parent_->set_immediately_(v, true); - } else { - // INSTANT CHANGE - this->parent_->set_immediately_(v, this->publish_); - } - - if (!this->has_transition_()) { - this->parent_->target_state_reached_callback_.call(); - } - if (this->publish_) { - this->parent_->publish_state(); - } - - if (this->save_) { - LightStateRTCState saved; - saved.state = v.is_on(); - saved.brightness = v.get_brightness(); - saved.red = v.get_red(); - saved.green = v.get_green(); - saved.blue = v.get_blue(); - saved.white = v.get_white(); - saved.color_temp = v.get_color_temperature(); - saved.effect = this->parent_->active_effect_index_; - this->parent_->rtc_.save(&saved); - } -} - -LightColorValues LightCall::validate_() { - // use remote values for fallback - auto *name = this->parent_->get_name().c_str(); - auto traits = this->parent_->get_traits(); - - // Brightness exists check - if (this->brightness_.has_value() && !traits.get_supports_brightness()) { - ESP_LOGW(TAG, "'%s' - This light does not support setting brightness!", name); - this->brightness_.reset(); - } - - // Transition length possible check - if (this->transition_length_.has_value() && *this->transition_length_ != 0 && !traits.get_supports_brightness()) { - ESP_LOGW(TAG, "'%s' - This light does not support transitions!", name); - this->transition_length_.reset(); - } - - // RGB exists check - if (this->red_.has_value() || this->green_.has_value() || this->blue_.has_value()) { - if (!traits.get_supports_rgb()) { - ESP_LOGW(TAG, "'%s' - This light does not support setting RGB color!", name); - this->red_.reset(); - this->green_.reset(); - this->blue_.reset(); - } - } - - // White value exists check - if (this->white_.has_value() && !traits.get_supports_rgb_white_value()) { - ESP_LOGW(TAG, "'%s' - This light does not support setting white value!", name); - this->white_.reset(); - } - - // Color temperature exists check - if (this->color_temperature_.has_value() && !traits.get_supports_color_temperature()) { - ESP_LOGW(TAG, "'%s' - This light does not support setting color temperature!", name); - this->color_temperature_.reset(); - } - - // If white channel is specified, set RGB to white color (when interlock is enabled) - if (this->white_.has_value()) { - if (traits.get_supports_color_interlock()) { - if (!this->red_.has_value() && !this->green_.has_value() && !this->blue_.has_value()) { - this->red_ = optional(1.0f); - this->green_ = optional(1.0f); - this->blue_ = optional(1.0f); - } - // make white values binary aka 0.0f or 1.0f... this allows brightness to do its job - if (*this->white_ > 0.0f) { - this->white_ = optional(1.0f); - } else { - this->white_ = optional(0.0f); - } - } - } - // If only a color channel is specified, set white channel to 100% for white, otherwise 0% (when interlock is enabled) - else if (this->red_.has_value() || this->green_.has_value() || this->blue_.has_value()) { - if (traits.get_supports_color_interlock()) { - if (*this->red_ == 1.0f && *this->green_ == 1.0f && *this->blue_ == 1.0f) { - this->white_ = optional(1.0f); - } else { - this->white_ = optional(0.0f); - } - } - } - // If only a color temperature is specified, change to white light - else if (this->color_temperature_.has_value()) { - this->red_ = optional(1.0f); - this->green_ = optional(1.0f); - this->blue_ = optional(1.0f); - - // if setting color temperature from color (i.e. switching to white light), set White to 100% - auto cv = this->parent_->remote_values; - bool was_color = cv.get_red() != 1.0f || cv.get_blue() != 1.0f || cv.get_green() != 1.0f; - if (traits.get_supports_color_interlock() || was_color) { - this->white_ = optional(1.0f); - } - } - -#define VALIDATE_RANGE_(name_, upper_name) \ - if (name_##_.has_value()) { \ - auto val = *name_##_; \ - if (val < 0.0f || val > 1.0f) { \ - ESP_LOGW(TAG, "'%s' - %s value %.2f is out of range [0.0 - 1.0]!", name, upper_name, val); \ - name_##_ = clamp(val, 0.0f, 1.0f); \ - } \ - } -#define VALIDATE_RANGE(name, upper_name) VALIDATE_RANGE_(name, upper_name) - - // Range checks - VALIDATE_RANGE(brightness, "Brightness") - VALIDATE_RANGE(red, "Red") - VALIDATE_RANGE(green, "Green") - VALIDATE_RANGE(blue, "Blue") - VALIDATE_RANGE(white, "White") - - auto v = this->parent_->remote_values; - if (this->state_.has_value()) - v.set_state(*this->state_); - if (this->brightness_.has_value()) - v.set_brightness(*this->brightness_); - - if (this->red_.has_value()) - v.set_red(*this->red_); - if (this->green_.has_value()) - v.set_green(*this->green_); - if (this->blue_.has_value()) - v.set_blue(*this->blue_); - if (this->white_.has_value()) - v.set_white(*this->white_); - - if (this->color_temperature_.has_value()) - v.set_color_temperature(*this->color_temperature_); - - v.normalize_color(traits); - - // Flash length check - if (this->has_flash_() && *this->flash_length_ == 0) { - ESP_LOGW(TAG, "'%s' - Flash length must be greater than zero!", name); - this->flash_length_.reset(); - } - - // validate transition length/flash length/effect not used at the same time - bool supports_transition = traits.get_supports_brightness(); - - // If effect is already active, remove effect start - if (this->has_effect_() && *this->effect_ == this->parent_->active_effect_index_) { - this->effect_.reset(); - } - - // validate effect index - if (this->has_effect_() && *this->effect_ > this->parent_->effects_.size()) { - ESP_LOGW(TAG, "'%s' Invalid effect index %u", name, *this->effect_); - this->effect_.reset(); - } - - if (this->has_effect_() && (this->has_transition_() || this->has_flash_())) { - ESP_LOGW(TAG, "'%s' - Effect cannot be used together with transition/flash!", name); - this->transition_length_.reset(); - this->flash_length_.reset(); - } - - if (this->has_flash_() && this->has_transition_()) { - ESP_LOGW(TAG, "'%s' - Flash cannot be used together with transition!", name); - this->transition_length_.reset(); - } - - if (!this->has_transition_() && !this->has_flash_() && (!this->has_effect_() || *this->effect_ == 0) && - supports_transition) { - // nothing specified and light supports transitions, set default transition length - this->transition_length_ = this->parent_->default_transition_length_; - } - - if (this->transition_length_.value_or(0) == 0) { - // 0 transition is interpreted as no transition (instant change) - this->transition_length_.reset(); - } - - if (this->has_transition_() && !supports_transition) { - ESP_LOGW(TAG, "'%s' - Light does not support transitions!", name); - this->transition_length_.reset(); - } - - // If not a flash and turning the light off, then disable the light - // Do not use light color values directly, so that effects can set 0% brightness - // Reason: When user turns off the light in frontend, the effect should also stop - if (!this->has_flash_() && !this->state_.value_or(v.is_on())) { - if (this->has_effect_()) { - ESP_LOGW(TAG, "'%s' - Cannot start an effect when turning off!", name); - this->effect_.reset(); - } else if (this->parent_->active_effect_index_ != 0) { - // Auto turn off effect - this->effect_ = 0; - } - } - - // Disable saving for flashes - if (this->has_flash_()) - this->save_ = false; - - return v; -} -LightCall &LightCall::set_effect(const std::string &effect) { - if (strcasecmp(effect.c_str(), "none") == 0) { - this->set_effect(0); - return *this; - } - - bool found = false; - for (uint32_t i = 0; i < this->parent_->effects_.size(); i++) { - LightEffect *e = this->parent_->effects_[i]; - - if (strcasecmp(effect.c_str(), e->get_name().c_str()) == 0) { - this->set_effect(i + 1); - found = true; - break; - } - } - if (!found) { - ESP_LOGW(TAG, "'%s' - No such effect '%s'", this->parent_->get_name().c_str(), effect.c_str()); - } - return *this; -} -LightCall &LightCall::from_light_color_values(const LightColorValues &values) { - this->set_state(values.is_on()); - this->set_brightness_if_supported(values.get_brightness()); - this->set_red_if_supported(values.get_red()); - this->set_green_if_supported(values.get_green()); - this->set_blue_if_supported(values.get_blue()); - this->set_white_if_supported(values.get_white()); - this->set_color_temperature_if_supported(values.get_color_temperature()); - return *this; -} -LightCall &LightCall::set_transition_length_if_supported(uint32_t transition_length) { - if (this->parent_->get_traits().get_supports_brightness()) - this->set_transition_length(transition_length); - return *this; -} -LightCall &LightCall::set_brightness_if_supported(float brightness) { - if (this->parent_->get_traits().get_supports_brightness()) - this->set_brightness(brightness); - return *this; -} -LightCall &LightCall::set_red_if_supported(float red) { - if (this->parent_->get_traits().get_supports_rgb()) - this->set_red(red); - return *this; -} -LightCall &LightCall::set_green_if_supported(float green) { - if (this->parent_->get_traits().get_supports_rgb()) - this->set_green(green); - return *this; -} -LightCall &LightCall::set_blue_if_supported(float blue) { - if (this->parent_->get_traits().get_supports_rgb()) - this->set_blue(blue); - return *this; -} -LightCall &LightCall::set_white_if_supported(float white) { - if (this->parent_->get_traits().get_supports_rgb_white_value()) - this->set_white(white); - return *this; -} -LightCall &LightCall::set_color_temperature_if_supported(float color_temperature) { - if (this->parent_->get_traits().get_supports_color_temperature()) - this->set_color_temperature(color_temperature); - return *this; -} -LightCall &LightCall::set_state(optional state) { - this->state_ = state; - return *this; -} -LightCall &LightCall::set_state(bool state) { - this->state_ = state; - return *this; -} -LightCall &LightCall::set_transition_length(optional transition_length) { - this->transition_length_ = transition_length; - return *this; -} -LightCall &LightCall::set_transition_length(uint32_t transition_length) { - this->transition_length_ = transition_length; - return *this; -} -LightCall &LightCall::set_flash_length(optional flash_length) { - this->flash_length_ = flash_length; - return *this; -} -LightCall &LightCall::set_flash_length(uint32_t flash_length) { - this->flash_length_ = flash_length; - return *this; -} -LightCall &LightCall::set_brightness(optional brightness) { - this->brightness_ = brightness; - return *this; -} -LightCall &LightCall::set_brightness(float brightness) { - this->brightness_ = brightness; - return *this; -} -LightCall &LightCall::set_red(optional red) { - this->red_ = red; - return *this; -} -LightCall &LightCall::set_red(float red) { - this->red_ = red; - return *this; -} -LightCall &LightCall::set_green(optional green) { - this->green_ = green; - return *this; -} -LightCall &LightCall::set_green(float green) { - this->green_ = green; - return *this; -} -LightCall &LightCall::set_blue(optional blue) { - this->blue_ = blue; - return *this; -} -LightCall &LightCall::set_blue(float blue) { - this->blue_ = blue; - return *this; -} -LightCall &LightCall::set_white(optional white) { - this->white_ = white; - return *this; -} -LightCall &LightCall::set_white(float white) { - this->white_ = white; - return *this; -} -LightCall &LightCall::set_color_temperature(optional color_temperature) { - this->color_temperature_ = color_temperature; - return *this; -} -LightCall &LightCall::set_color_temperature(float color_temperature) { - this->color_temperature_ = color_temperature; - return *this; -} -LightCall &LightCall::set_effect(optional effect) { - if (effect.has_value()) - this->set_effect(*effect); - return *this; -} -LightCall &LightCall::set_effect(uint32_t effect_number) { - this->effect_ = effect_number; - return *this; -} -LightCall &LightCall::set_effect(optional effect_number) { - this->effect_ = effect_number; - return *this; -} -LightCall &LightCall::set_publish(bool publish) { - this->publish_ = publish; - return *this; -} -LightCall &LightCall::set_save(bool save) { - this->save_ = save; - return *this; -} -LightCall &LightCall::set_rgb(float red, float green, float blue) { - this->set_red(red); - this->set_green(green); - this->set_blue(blue); - return *this; -} -LightCall &LightCall::set_rgbw(float red, float green, float blue, float white) { - this->set_rgb(red, green, blue); - this->set_white(white); - return *this; -} - -float LightState::get_setup_priority() const { return setup_priority::HARDWARE - 1.0f; } -LightOutput *LightState::get_output() const { return this->output_; } -void LightState::set_gamma_correct(float gamma_correct) { this->gamma_correct_ = gamma_correct; } void LightState::current_values_as_binary(bool *binary) { this->current_values.as_binary(binary); } void LightState::current_values_as_brightness(float *brightness) { this->current_values.as_brightness(brightness, this->gamma_correct_); @@ -748,19 +187,71 @@ void LightState::current_values_as_cwww(float *cold_white, float *warm_white, bo this->current_values.as_cwww(traits.get_min_mireds(), traits.get_max_mireds(), cold_white, warm_white, this->gamma_correct_, constant_brightness); } -void LightState::add_new_remote_values_callback(std::function &&send_callback) { - this->remote_values_callback_.add(std::move(send_callback)); -} -void LightState::add_new_target_state_reached_callback(std::function &&send_callback) { - this->target_state_reached_callback_.add(std::move(send_callback)); -} +void LightState::start_effect_(uint32_t effect_index) { + this->stop_effect_(); + if (effect_index == 0) + return; + + this->active_effect_index_ = effect_index; + auto *effect = this->get_active_effect_(); + effect->start_internal(); +} LightEffect *LightState::get_active_effect_() { if (this->active_effect_index_ == 0) return nullptr; else return this->effects_[this->active_effect_index_ - 1]; } +void LightState::stop_effect_() { + auto *effect = this->get_active_effect_(); + if (effect != nullptr) { + effect->stop(); + } + this->active_effect_index_ = 0; +} + +void LightState::start_transition_(const LightColorValues &target, uint32_t length) { + this->transformer_ = make_unique(millis(), length, this->current_values, target); + this->remote_values = this->transformer_->get_remote_values(); +} + +void LightState::start_flash_(const LightColorValues &target, uint32_t length) { + LightColorValues end_colors = this->current_values; + // If starting a flash if one is already happening, set end values to end values of current flash + // Hacky but works + if (this->transformer_ != nullptr) + end_colors = this->transformer_->get_end_values(); + this->transformer_ = make_unique(millis(), length, end_colors, target); + this->remote_values = this->transformer_->get_remote_values(); +} + +void LightState::set_immediately_(const LightColorValues &target, bool set_remote_values) { + this->transformer_ = nullptr; + this->current_values = target; + if (set_remote_values) { + this->remote_values = target; + } + this->next_write_ = true; +} + +void LightState::set_transformer_(std::unique_ptr transformer) { + this->transformer_ = std::move(transformer); +} + +void LightState::save_remote_values_() { + LightStateRTCState saved; + saved.state = this->remote_values.is_on(); + saved.brightness = this->remote_values.get_brightness(); + saved.color_brightness = this->remote_values.get_color_brightness(); + saved.red = this->remote_values.get_red(); + saved.green = this->remote_values.get_green(); + saved.blue = this->remote_values.get_blue(); + saved.white = this->remote_values.get_white(); + saved.color_temp = this->remote_values.get_color_temperature(); + saved.effect = this->active_effect_index_; + this->rtc_.save(&saved); +} } // namespace light } // namespace esphome diff --git a/esphome/components/light/light_state.h b/esphome/components/light/light_state.h index aff63bf5db..cd5e6ad1cb 100644 --- a/esphome/components/light/light_state.h +++ b/esphome/components/light/light_state.h @@ -5,161 +5,15 @@ #include "esphome/core/preferences.h" #include "light_effect.h" #include "light_color_values.h" +#include "light_call.h" #include "light_traits.h" #include "light_transformer.h" namespace esphome { namespace light { -class LightState; class LightOutput; -class LightCall { - public: - explicit LightCall(LightState *parent) : parent_(parent) {} - - /// Set the binary ON/OFF state of the light. - LightCall &set_state(optional state); - /// Set the binary ON/OFF state of the light. - LightCall &set_state(bool state); - /** Set the transition length of this call in milliseconds. - * - * This argument is ignored for starting flashes and effects. - * - * Defaults to the default transition length defined in the light configuration. - */ - LightCall &set_transition_length(optional transition_length); - /** Set the transition length of this call in milliseconds. - * - * This argument is ignored for starting flashes and effects. - * - * Defaults to the default transition length defined in the light configuration. - */ - LightCall &set_transition_length(uint32_t transition_length); - /// Set the transition length property if the light supports transitions. - LightCall &set_transition_length_if_supported(uint32_t transition_length); - /// Start and set the flash length of this call in milliseconds. - LightCall &set_flash_length(optional flash_length); - /// Start and set the flash length of this call in milliseconds. - LightCall &set_flash_length(uint32_t flash_length); - /// Set the target brightness of the light from 0.0 (fully off) to 1.0 (fully on) - LightCall &set_brightness(optional brightness); - /// Set the target brightness of the light from 0.0 (fully off) to 1.0 (fully on) - LightCall &set_brightness(float brightness); - /// Set the brightness property if the light supports brightness. - LightCall &set_brightness_if_supported(float brightness); - /** Set the red RGB value of the light from 0.0 to 1.0. - * - * Note that this only controls the color of the light, not its brightness. - */ - LightCall &set_red(optional red); - /** Set the red RGB value of the light from 0.0 to 1.0. - * - * Note that this only controls the color of the light, not its brightness. - */ - LightCall &set_red(float red); - /// Set the red property if the light supports RGB. - LightCall &set_red_if_supported(float red); - /** Set the green RGB value of the light from 0.0 to 1.0. - * - * Note that this only controls the color of the light, not its brightness. - */ - LightCall &set_green(optional green); - /** Set the green RGB value of the light from 0.0 to 1.0. - * - * Note that this only controls the color of the light, not its brightness. - */ - LightCall &set_green(float green); - /// Set the green property if the light supports RGB. - LightCall &set_green_if_supported(float green); - /** Set the blue RGB value of the light from 0.0 to 1.0. - * - * Note that this only controls the color of the light, not its brightness. - */ - LightCall &set_blue(optional blue); - /** Set the blue RGB value of the light from 0.0 to 1.0. - * - * Note that this only controls the color of the light, not its brightness. - */ - LightCall &set_blue(float blue); - /// Set the blue property if the light supports RGB. - LightCall &set_blue_if_supported(float blue); - /// Set the white value value of the light from 0.0 to 1.0 for RGBW[W] lights. - LightCall &set_white(optional white); - /// Set the white value value of the light from 0.0 to 1.0 for RGBW[W] lights. - LightCall &set_white(float white); - /// Set the white property if the light supports RGB. - LightCall &set_white_if_supported(float white); - /// Set the color temperature of the light in mireds for CWWW or RGBWW lights. - LightCall &set_color_temperature(optional color_temperature); - /// Set the color temperature of the light in mireds for CWWW or RGBWW lights. - LightCall &set_color_temperature(float color_temperature); - /// Set the color_temperature property if the light supports color temperature. - LightCall &set_color_temperature_if_supported(float color_temperature); - /// Set the effect of the light by its name. - LightCall &set_effect(optional effect); - /// Set the effect of the light by its name. - LightCall &set_effect(const std::string &effect); - /// Set the effect of the light by its internal index number (only for internal use). - LightCall &set_effect(uint32_t effect_number); - LightCall &set_effect(optional effect_number); - /// Set whether this light call should trigger a publish state. - LightCall &set_publish(bool publish); - /// Set whether this light call should trigger a save state to recover them at startup.. - LightCall &set_save(bool save); - - /** Set the RGB color of the light by RGB values. - * - * Please note that this only changes the color of the light, not the brightness. - * - * @param red The red color value from 0.0 to 1.0. - * @param green The green color value from 0.0 to 1.0. - * @param blue The blue color value from 0.0 to 1.0. - * @return The light call for chaining setters. - */ - LightCall &set_rgb(float red, float green, float blue); - /** Set the RGBW color of the light by RGB values. - * - * Please note that this only changes the color of the light, not the brightness. - * - * @param red The red color value from 0.0 to 1.0. - * @param green The green color value from 0.0 to 1.0. - * @param blue The blue color value from 0.0 to 1.0. - * @param white The white color value from 0.0 to 1.0. - * @return The light call for chaining setters. - */ - LightCall &set_rgbw(float red, float green, float blue, float white); -#ifdef USE_JSON - LightCall &parse_color_json(JsonObject &root); - LightCall &parse_json(JsonObject &root); -#endif - LightCall &from_light_color_values(const LightColorValues &values); - - void perform(); - - protected: - /// Validate all properties and return the target light color values. - LightColorValues validate_(); - - bool has_transition_() { return this->transition_length_.has_value(); } - bool has_flash_() { return this->flash_length_.has_value(); } - bool has_effect_() { return this->effect_.has_value(); } - - LightState *parent_; - optional state_; - optional transition_length_; - optional flash_length_; - optional brightness_; - optional red_; - optional green_; - optional blue_; - optional white_; - optional color_temperature_; - optional effect_; - bool publish_{true}; - bool save_{true}; -}; - enum LightRestoreMode { LIGHT_RESTORE_DEFAULT_OFF, LIGHT_RESTORE_DEFAULT_ON, @@ -204,14 +58,6 @@ class LightState : public Nameable, public Component { */ LightColorValues current_values; - /// Deprecated method to access current_values. - ESPDEPRECATED("get_current_values() is deprecated, please use .current_values instead.") - LightColorValues get_current_values(); - - /// Deprecated method to access remote_values. - ESPDEPRECATED("get_remote_values() is deprecated, please use .remote_values instead.") - LightColorValues get_remote_values(); - /** The remote color values reported to the frontend. * * These are different from the "current" values: For example transitions will @@ -231,29 +77,22 @@ class LightState : public Nameable, public Component { /// Return the name of the current effect, or if no effect is active "None". std::string get_effect_name(); - /** This lets front-end components subscribe to light change events. - * - * This is different from add_new_current_values_callback in that it only sends events for start - * and end values. For example, with transitions it will only send a single callback whereas - * the callback passed in add_new_current_values_callback will be called every loop() cycle when - * a transition is active - * - * Note the callback should get the output values through get_remote_values(). + /** + * This lets front-end components subscribe to light change events. This callback is called once + * when the remote color values are changed. * * @param send_callback The callback. */ void add_new_remote_values_callback(std::function &&send_callback); /** - * The callback is called once the state of current_values and remote_values are equal + * The callback is called once the state of current_values and remote_values are equal (when the + * transition is finished). * * @param send_callback */ void add_new_target_state_reached_callback(std::function &&send_callback); - /// Return whether the light has any effects that meet the trait requirements. - bool supports_effects(); - #ifdef USE_JSON /// Dump the state of this light as JSON. void dump_json(JsonObject &root); @@ -265,11 +104,18 @@ class LightState : public Nameable, public Component { /// Set the gamma correction factor void set_gamma_correct(float gamma_correct); float get_gamma_correct() const { return this->gamma_correct_; } - void set_restore_mode(LightRestoreMode restore_mode) { restore_mode_ = restore_mode; } + /// Set the restore mode of this light + void set_restore_mode(LightRestoreMode restore_mode); + + /// Return whether the light has any effects that meet the trait requirements. + bool supports_effects(); + + /// Get all effects for this light state. const std::vector &get_effects() const; - void add_effects(std::vector effects); + /// Add effects for this light state. + void add_effects(const std::vector &effects); void current_values_as_binary(bool *binary); @@ -293,6 +139,8 @@ class LightState : public Nameable, public Component { /// Internal method to start an effect with the given index void start_effect_(uint32_t effect_index); + /// Internal method to get the currently active effect + LightEffect *get_active_effect_(); /// Internal method to stop the current effect (if one is active). void stop_effect_(); /// Internal method to start a transition to the target color with the given length. @@ -307,18 +155,21 @@ class LightState : public Nameable, public Component { /// Internal method to start a transformer. void set_transformer_(std::unique_ptr transformer); - LightEffect *get_active_effect_(); + /// Internal method to save the current remote_values to the preferences + void save_remote_values_(); - /// Object used to store the persisted values of the light. - ESPPreferenceObject rtc_; - /// Restore mode of the light. - LightRestoreMode restore_mode_; - /// Default transition length for all transitions in ms. - uint32_t default_transition_length_{}; + /// Store the output to allow effects to have more access. + LightOutput *output_; /// Value for storing the index of the currently active effect. 0 if no effect is active uint32_t active_effect_index_{}; /// The currently active transformer for this light (transition/flash). std::unique_ptr transformer_{nullptr}; + /// Whether the light value should be written in the next cycle. + bool next_write_{true}; + + /// Object used to store the persisted values of the light. + ESPPreferenceObject rtc_; + /** Callback to call when new values for the frontend are available. * * "Remote values" are light color values that are reported to the frontend and have a lower @@ -333,11 +184,12 @@ class LightState : public Nameable, public Component { */ CallbackManager target_state_reached_callback_{}; - LightOutput *output_; ///< Store the output to allow effects to have more access. - /// Whether the light value should be written in the next cycle. - bool next_write_{true}; + /// Default transition length for all transitions in ms. + uint32_t default_transition_length_{}; /// Gamma correction factor for the light. float gamma_correct_{}; + /// Restore mode of the light. + LightRestoreMode restore_mode_; /// List of effects for this light. std::vector effects_; }; diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index 2b571b817b..8d79c96f63 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -205,8 +205,7 @@ def maybe_simple_message(schema): def validate_printf(value): # https://stackoverflow.com/questions/30011379/how-can-i-parse-a-c-format-string-in-python - # pylint: disable=anomalous-backslash-in-string - cfmt = """\ + cfmt = r""" ( # start of capture group 1 % # literal "%" (?:[-+0 #]{0,5}) # optional flags diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index 9c65f494f0..ce82a51b94 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -8,9 +8,9 @@ namespace esphome { namespace logger { -static const char *TAG = "logger"; +static const char *const TAG = "logger"; -static const char *LOG_LEVEL_COLORS[] = { +static const char *const LOG_LEVEL_COLORS[] = { "", // NONE ESPHOME_LOG_BOLD(ESPHOME_LOG_COLOR_RED), // ERROR ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_YELLOW), // WARNING @@ -20,7 +20,7 @@ static const char *LOG_LEVEL_COLORS[] = { ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_GRAY), // VERBOSE ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_WHITE), // VERY_VERBOSE }; -static const char *LOG_LEVEL_LETTERS[] = { +static const char *const LOG_LEVEL_LETTERS[] = { "", // NONE "E", // ERROR "W", // WARNING @@ -178,12 +178,12 @@ void Logger::add_on_log_callback(std::functionlog_callback_.add(std::move(callback)); } float Logger::get_setup_priority() const { return setup_priority::HARDWARE - 1.0f; } -const char *LOG_LEVELS[] = {"NONE", "ERROR", "WARN", "INFO", "CONFIG", "DEBUG", "VERBOSE", "VERY_VERBOSE"}; +const char *const LOG_LEVELS[] = {"NONE", "ERROR", "WARN", "INFO", "CONFIG", "DEBUG", "VERBOSE", "VERY_VERBOSE"}; #ifdef ARDUINO_ARCH_ESP32 -const char *UART_SELECTIONS[] = {"UART0", "UART1", "UART2"}; +const char *const UART_SELECTIONS[] = {"UART0", "UART1", "UART2"}; #endif #ifdef ARDUINO_ARCH_ESP8266 -const char *UART_SELECTIONS[] = {"UART0", "UART1", "UART0_SWAP"}; +const char *const UART_SELECTIONS[] = {"UART0", "UART1", "UART0_SWAP"}; #endif void Logger::dump_config() { ESP_LOGCONFIG(TAG, "Logger:"); @@ -196,7 +196,7 @@ void Logger::dump_config() { } void Logger::write_footer_() { this->write_to_buffer_(ESPHOME_LOG_RESET_COLOR, strlen(ESPHOME_LOG_RESET_COLOR)); } -Logger *global_logger = nullptr; +Logger *global_logger = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace logger } // namespace esphome diff --git a/esphome/components/logger/logger.h b/esphome/components/logger/logger.h index 039ad78c63..1724875229 100644 --- a/esphome/components/logger/logger.h +++ b/esphome/components/logger/logger.h @@ -115,7 +115,7 @@ class Logger : public Component { CallbackManager log_callback_{}; }; -extern Logger *global_logger; +extern Logger *global_logger; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) class LoggerMessageTrigger : public Trigger { public: diff --git a/esphome/components/max31855/max31855.cpp b/esphome/components/max31855/max31855.cpp index 868cd4f16a..2578c4742d 100644 --- a/esphome/components/max31855/max31855.cpp +++ b/esphome/components/max31855/max31855.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace max31855 { -static const char* TAG = "max31855"; +static const char *const TAG = "max31855"; void MAX31855Sensor::update() { this->enable(); diff --git a/esphome/components/max31856/max31856.cpp b/esphome/components/max31856/max31856.cpp index b3e5b9277a..9b627e21a2 100644 --- a/esphome/components/max31856/max31856.cpp +++ b/esphome/components/max31856/max31856.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace max31856 { -static const char *TAG = "max31856"; +static const char *const TAG = "max31856"; // Based on Adafruit's library: https://github.com/adafruit/Adafruit_MAX31856 diff --git a/esphome/components/max31865/max31865.cpp b/esphome/components/max31865/max31865.cpp index d6334a3c34..daadc26cdc 100644 --- a/esphome/components/max31865/max31865.cpp +++ b/esphome/components/max31865/max31865.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace max31865 { -static const char* TAG = "max31865"; +static const char *const TAG = "max31865"; void MAX31865Sensor::update() { // Check new faults since last measurement @@ -176,7 +176,7 @@ const uint16_t MAX31865Sensor::read_register_16_(uint8_t reg) { return value; } -float MAX31865Sensor::calc_temperature_(const float& rtd_ratio) { +float MAX31865Sensor::calc_temperature_(const float &rtd_ratio) { // Based loosely on Adafruit's library: https://github.com/adafruit/Adafruit_MAX31865 // Mainly based on formulas provided by Analog: // http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf diff --git a/esphome/components/max31865/max31865.h b/esphome/components/max31865/max31865.h index ee2f155f74..393dd4b434 100644 --- a/esphome/components/max31865/max31865.h +++ b/esphome/components/max31865/max31865.h @@ -51,7 +51,7 @@ class MAX31865Sensor : public sensor::Sensor, void write_register_(uint8_t reg, uint8_t value); const uint8_t read_register_(uint8_t reg); const uint16_t read_register_16_(uint8_t reg); - float calc_temperature_(const float& rtd_ratio); + float calc_temperature_(const float &rtd_ratio); }; } // namespace max31865 diff --git a/esphome/components/max6675/max6675.cpp b/esphome/components/max6675/max6675.cpp index 53442b9cb1..1ec1d5ee53 100644 --- a/esphome/components/max6675/max6675.cpp +++ b/esphome/components/max6675/max6675.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace max6675 { -static const char *TAG = "max6675"; +static const char *const TAG = "max6675"; void MAX6675Sensor::update() { this->enable(); diff --git a/esphome/components/max7219/max7219.cpp b/esphome/components/max7219/max7219.cpp index 9c5c729487..91bea22e46 100644 --- a/esphome/components/max7219/max7219.cpp +++ b/esphome/components/max7219/max7219.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace max7219 { -static const char *TAG = "max7219"; +static const char *const TAG = "max7219"; static const uint8_t MAX7219_REGISTER_NOOP = 0x00; static const uint8_t MAX7219_REGISTER_DECODE_MODE = 0x09; diff --git a/esphome/components/max7219digit/max7219digit.cpp b/esphome/components/max7219digit/max7219digit.cpp index 01bc5e0531..b130823c12 100644 --- a/esphome/components/max7219digit/max7219digit.cpp +++ b/esphome/components/max7219digit/max7219digit.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace max7219digit { -static const char *TAG = "max7219DIGIT"; +static const char *const TAG = "max7219DIGIT"; static const uint8_t MAX7219_REGISTER_NOOP = 0x00; static const uint8_t MAX7219_REGISTER_DECODE_MODE = 0x09; @@ -55,7 +55,7 @@ void MAX7219Component::dump_config() { } void MAX7219Component::loop() { - unsigned long now = millis(); + uint32_t now = millis(); // check if the buffer has shrunk past the current position since last update if ((this->max_displaybuffer_.size() >= this->old_buffer_size_ + 3) || diff --git a/esphome/components/max7219digit/max7219digit.h b/esphome/components/max7219digit/max7219digit.h index ddefe976a2..83f45e3a00 100644 --- a/esphome/components/max7219digit/max7219digit.h +++ b/esphome/components/max7219digit/max7219digit.h @@ -99,7 +99,7 @@ class MAX7219Component : public PollingComponent, uint8_t orientation_; uint8_t bckgrnd_ = 0x0; std::vector max_displaybuffer_; - unsigned long last_scroll_ = 0; + uint32_t last_scroll_ = 0; uint16_t stepsleft_; size_t get_buffer_length_(); optional writer_local_{}; diff --git a/esphome/components/mcp23008/mcp23008.cpp b/esphome/components/mcp23008/mcp23008.cpp index eb66bfc7d7..351360fe1c 100644 --- a/esphome/components/mcp23008/mcp23008.cpp +++ b/esphome/components/mcp23008/mcp23008.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace mcp23008 { -static const char *TAG = "mcp23008"; +static const char *const TAG = "mcp23008"; void MCP23008::setup() { ESP_LOGCONFIG(TAG, "Setting up MCP23008..."); diff --git a/esphome/components/mcp23016/mcp23016.cpp b/esphome/components/mcp23016/mcp23016.cpp index bd04486965..f2b55fe2e2 100644 --- a/esphome/components/mcp23016/mcp23016.cpp +++ b/esphome/components/mcp23016/mcp23016.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace mcp23016 { -static const char *TAG = "mcp23016"; +static const char *const TAG = "mcp23016"; void MCP23016::setup() { ESP_LOGCONFIG(TAG, "Setting up MCP23016..."); diff --git a/esphome/components/mcp23017/mcp23017.cpp b/esphome/components/mcp23017/mcp23017.cpp index 0523b14d5a..7344f482e0 100644 --- a/esphome/components/mcp23017/mcp23017.cpp +++ b/esphome/components/mcp23017/mcp23017.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace mcp23017 { -static const char *TAG = "mcp23017"; +static const char *const TAG = "mcp23017"; void MCP23017::setup() { ESP_LOGCONFIG(TAG, "Setting up MCP23017..."); diff --git a/esphome/components/mcp23s08/mcp23s08.cpp b/esphome/components/mcp23s08/mcp23s08.cpp index 3bb03cb225..b7adeb94d2 100644 --- a/esphome/components/mcp23s08/mcp23s08.cpp +++ b/esphome/components/mcp23s08/mcp23s08.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace mcp23s08 { -static const char *TAG = "mcp23s08"; +static const char *const TAG = "mcp23s08"; void MCP23S08::set_device_address(uint8_t device_addr) { if (device_addr != 0) { diff --git a/esphome/components/mcp23s17/mcp23s17.cpp b/esphome/components/mcp23s17/mcp23s17.cpp index 7c2cfcd526..8e3d5213f8 100644 --- a/esphome/components/mcp23s17/mcp23s17.cpp +++ b/esphome/components/mcp23s17/mcp23s17.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace mcp23s17 { -static const char *TAG = "mcp23s17"; +static const char *const TAG = "mcp23s17"; void MCP23S17::set_device_address(uint8_t device_addr) { if (device_addr != 0) { diff --git a/esphome/components/mcp23x08_base/mcp23x08_base.cpp b/esphome/components/mcp23x08_base/mcp23x08_base.cpp index c14e0020dd..2b047fa288 100644 --- a/esphome/components/mcp23x08_base/mcp23x08_base.cpp +++ b/esphome/components/mcp23x08_base/mcp23x08_base.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace mcp23x08_base { -static const char *TAG = "mcp23x08_base"; +static const char *const TAG = "mcp23x08_base"; bool MCP23X08Base::digital_read(uint8_t pin) { uint8_t bit = pin % 8; diff --git a/esphome/components/mcp23x17_base/mcp23x17_base.cpp b/esphome/components/mcp23x17_base/mcp23x17_base.cpp index 18f3ba7c6d..72dec2d457 100644 --- a/esphome/components/mcp23x17_base/mcp23x17_base.cpp +++ b/esphome/components/mcp23x17_base/mcp23x17_base.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace mcp23x17_base { -static const char *TAG = "mcp23x17_base"; +static const char *const TAG = "mcp23x17_base"; bool MCP23X17Base::digital_read(uint8_t pin) { uint8_t bit = pin % 8; diff --git a/esphome/components/mcp2515/mcp2515.cpp b/esphome/components/mcp2515/mcp2515.cpp index 228a951702..ce451cbb33 100644 --- a/esphome/components/mcp2515/mcp2515.cpp +++ b/esphome/components/mcp2515/mcp2515.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace mcp2515 { -static const char *TAG = "mcp2515"; +static const char *const TAG = "mcp2515"; const struct MCP2515::TxBnRegs MCP2515::TXB[N_TXBUFFERS] = {{MCP_TXB0CTRL, MCP_TXB0SIDH, MCP_TXB0DATA}, {MCP_TXB1CTRL, MCP_TXB1SIDH, MCP_TXB1DATA}, @@ -113,7 +113,7 @@ uint8_t MCP2515::get_status_() { canbus::Error MCP2515::set_mode_(const CanctrlReqopMode mode) { modify_register_(MCP_CANCTRL, CANCTRL_REQOP, mode); - unsigned long end_time = millis() + 10; + uint32_t end_time = millis() + 10; bool mode_match = false; while (millis() < end_time) { uint8_t new_mode = read_register_(MCP_CANSTAT); @@ -600,9 +600,9 @@ canbus::Error MCP2515::set_bitrate_(canbus::CanSpeed can_speed, CanClock can_clo } if (set) { - set_register_(MCP_CNF1, cfg1); - set_register_(MCP_CNF2, cfg2); - set_register_(MCP_CNF3, cfg3); + set_register_(MCP_CNF1, cfg1); // NOLINT + set_register_(MCP_CNF2, cfg2); // NOLINT + set_register_(MCP_CNF3, cfg3); // NOLINT return canbus::ERROR_OK; } else { return canbus::ERROR_FAIL; diff --git a/esphome/components/mcp3008/mcp3008.cpp b/esphome/components/mcp3008/mcp3008.cpp index d09c2e4e92..909a6f4708 100644 --- a/esphome/components/mcp3008/mcp3008.cpp +++ b/esphome/components/mcp3008/mcp3008.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace mcp3008 { -static const char *TAG = "mcp3008"; +static const char *const TAG = "mcp3008"; float MCP3008::get_setup_priority() const { return setup_priority::HARDWARE; } @@ -37,7 +37,7 @@ float MCP3008::read_data(uint8_t pin) { return data / 1023.0f; } -MCP3008Sensor::MCP3008Sensor(MCP3008 *parent, std::string name, uint8_t pin, float reference_voltage) +MCP3008Sensor::MCP3008Sensor(MCP3008 *parent, const std::string &name, uint8_t pin, float reference_voltage) : PollingComponent(1000), parent_(parent), pin_(pin) { this->set_name(name); this->reference_voltage_ = reference_voltage; diff --git a/esphome/components/mcp3008/mcp3008.h b/esphome/components/mcp3008/mcp3008.h index 129f299a14..16f1c14fcb 100644 --- a/esphome/components/mcp3008/mcp3008.h +++ b/esphome/components/mcp3008/mcp3008.h @@ -26,7 +26,7 @@ class MCP3008 : public Component, class MCP3008Sensor : public PollingComponent, public sensor::Sensor, public voltage_sampler::VoltageSampler { public: - MCP3008Sensor(MCP3008 *parent, std::string name, uint8_t pin, float reference_voltage); + MCP3008Sensor(MCP3008 *parent, const std::string &name, uint8_t pin, float reference_voltage); void set_reference_voltage(float reference_voltage) { reference_voltage_ = reference_voltage; } void setup() override; diff --git a/esphome/components/mcp4725/mcp4725.cpp b/esphome/components/mcp4725/mcp4725.cpp index 37ae7ac2b8..a8b130208e 100644 --- a/esphome/components/mcp4725/mcp4725.cpp +++ b/esphome/components/mcp4725/mcp4725.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace mcp4725 { -static const char *TAG = "mcp4725"; +static const char *const TAG = "mcp4725"; void MCP4725::setup() { ESP_LOGCONFIG(TAG, "Setting up MCP4725 (0x%02X)...", this->address_); diff --git a/esphome/components/mcp9808/mcp9808.cpp b/esphome/components/mcp9808/mcp9808.cpp index 38870b3b00..8f60df1d88 100644 --- a/esphome/components/mcp9808/mcp9808.cpp +++ b/esphome/components/mcp9808/mcp9808.cpp @@ -15,7 +15,7 @@ static const uint8_t MCP9808_AMBIENT_CLEAR_FLAGS = 0x1F; static const uint8_t MCP9808_AMBIENT_CLEAR_SIGN = 0x0F; static const uint8_t MCP9808_AMBIENT_TEMP_NEGATIVE = 0x10; -static const char *TAG = "mcp9808"; +static const char *const TAG = "mcp9808"; void MCP9808Sensor::setup() { ESP_LOGCONFIG(TAG, "Setting up %s...", this->name_.c_str()); diff --git a/esphome/components/mhz19/mhz19.cpp b/esphome/components/mhz19/mhz19.cpp index 8e28d04dea..db3ad50851 100644 --- a/esphome/components/mhz19/mhz19.cpp +++ b/esphome/components/mhz19/mhz19.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace mhz19 { -static const char *TAG = "mhz19"; +static const char *const TAG = "mhz19"; static const uint8_t MHZ19_REQUEST_LENGTH = 8; static const uint8_t MHZ19_RESPONSE_LENGTH = 9; static const uint8_t MHZ19_COMMAND_GET_PPM[] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00}; diff --git a/esphome/components/midea_ac/midea_climate.cpp b/esphome/components/midea_ac/midea_climate.cpp index b3b196aad2..9fe5df7de3 100644 --- a/esphome/components/midea_ac/midea_climate.cpp +++ b/esphome/components/midea_ac/midea_climate.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace midea_ac { -static const char *TAG = "midea_ac"; +static const char *const TAG = "midea_ac"; static void set_sensor(sensor::Sensor *sensor, float value) { if (sensor != nullptr && (!sensor->has_state() || sensor->get_raw_state() != value)) diff --git a/esphome/components/midea_ac/midea_climate.h b/esphome/components/midea_ac/midea_climate.h index d5f29529df..62bd4c339e 100644 --- a/esphome/components/midea_ac/midea_climate.h +++ b/esphome/components/midea_ac/midea_climate.h @@ -1,5 +1,10 @@ #pragma once +#include + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/midea_dongle/midea_dongle.h" #include "esphome/components/climate/climate.h" #include "esphome/components/midea_dongle/midea_dongle.h" #include "esphome/components/sensor/sensor.h" diff --git a/esphome/components/midea_ac/midea_frame.cpp b/esphome/components/midea_ac/midea_frame.cpp index a624400411..5f09f4314f 100644 --- a/esphome/components/midea_ac/midea_frame.cpp +++ b/esphome/components/midea_ac/midea_frame.cpp @@ -3,7 +3,7 @@ namespace esphome { namespace midea_ac { -static const char *TAG = "midea_ac"; +static const char *const TAG = "midea_ac"; const std::string MIDEA_SILENT_FAN_MODE = "silent"; const std::string MIDEA_TURBO_FAN_MODE = "turbo"; const std::string MIDEA_FREEZE_PROTECTION_PRESET = "freeze protection"; diff --git a/esphome/components/midea_ac/midea_frame.h b/esphome/components/midea_ac/midea_frame.h index a84161b4af..3777f6fd77 100644 --- a/esphome/components/midea_ac/midea_frame.h +++ b/esphome/components/midea_ac/midea_frame.h @@ -102,8 +102,11 @@ class PropertiesFrame : public midea_dongle::BaseFrame { void set_sleep_mode(bool state) { this->set_bytemask_(20, 0x01, state); } /* TURBO MODE */ - bool get_turbo_mode() const { return this->pbuf_[18] & 0x20; } - void set_turbo_mode(bool state) { this->set_bytemask_(18, 0x20, state); } + bool get_turbo_mode() const { return this->pbuf_[18] & 0x20 || this->pbuf_[20] & 0x02; } + void set_turbo_mode(bool state) { + this->set_bytemask_(18, 0x20, state); + this->set_bytemask_(20, 0x02, state); + } /* FREEZE PROTECTION */ bool get_freeze_protection_mode() const { return this->pbuf_[31] & 0x80; } diff --git a/esphome/components/midea_dongle/midea_dongle.cpp b/esphome/components/midea_dongle/midea_dongle.cpp index 8ddaba1cb6..7e3683a964 100644 --- a/esphome/components/midea_dongle/midea_dongle.cpp +++ b/esphome/components/midea_dongle/midea_dongle.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace midea_dongle { -static const char *TAG = "midea_dongle"; +static const char *const TAG = "midea_dongle"; void MideaDongle::loop() { while (this->available()) { @@ -52,7 +52,7 @@ void MideaDongle::update() { wifi_strength = this->notify_.get_signal_strength(); } else { this->rssi_timer_ = 60; - const long dbm = WiFi.RSSI(); + const int32_t dbm = WiFi.RSSI(); if (dbm > -63) wifi_strength = 4; else if (dbm > -75) diff --git a/esphome/components/mitsubishi/mitsubishi.cpp b/esphome/components/mitsubishi/mitsubishi.cpp index c9d0ce842e..43397770d1 100644 --- a/esphome/components/mitsubishi/mitsubishi.cpp +++ b/esphome/components/mitsubishi/mitsubishi.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace mitsubishi { -static const char *TAG = "mitsubishi.climate"; +static const char *const TAG = "mitsubishi.climate"; const uint32_t MITSUBISHI_OFF = 0x00; @@ -42,8 +42,8 @@ void MitsubishiClimate::transmit_state() { break; } - remote_state[7] = - (uint8_t) roundf(clamp(this->target_temperature, MITSUBISHI_TEMP_MIN, MITSUBISHI_TEMP_MAX) - MITSUBISHI_TEMP_MIN); + remote_state[7] = (uint8_t) roundf(clamp(this->target_temperature, MITSUBISHI_TEMP_MIN, MITSUBISHI_TEMP_MAX) - + MITSUBISHI_TEMP_MIN); ESP_LOGV(TAG, "Sending Mitsubishi target temp: %.1f state: %02X mode: %02X temp: %02X", this->target_temperature, remote_state[5], remote_state[6], remote_state[7]); diff --git a/esphome/components/modbus/modbus.cpp b/esphome/components/modbus/modbus.cpp index 7820e14bf9..2d714e72a2 100644 --- a/esphome/components/modbus/modbus.cpp +++ b/esphome/components/modbus/modbus.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace modbus { -static const char *TAG = "modbus"; +static const char *const TAG = "modbus"; void Modbus::setup() { if (this->flow_control_pin_ != nullptr) { diff --git a/esphome/components/mpr121/mpr121.cpp b/esphome/components/mpr121/mpr121.cpp index 2025bc5b3f..274ed6dfec 100644 --- a/esphome/components/mpr121/mpr121.cpp +++ b/esphome/components/mpr121/mpr121.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace mpr121 { -static const char *TAG = "mpr121"; +static const char *const TAG = "mpr121"; void MPR121Component::setup() { ESP_LOGCONFIG(TAG, "Setting up MPR121..."); diff --git a/esphome/components/mpu6050/mpu6050.cpp b/esphome/components/mpu6050/mpu6050.cpp index 06f8951bf5..8a0756e63c 100644 --- a/esphome/components/mpu6050/mpu6050.cpp +++ b/esphome/components/mpu6050/mpu6050.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace mpu6050 { -static const char *TAG = "mpu6050"; +static const char *const TAG = "mpu6050"; const uint8_t MPU6050_REGISTER_WHO_AM_I = 0x75; const uint8_t MPU6050_REGISTER_POWER_MANAGEMENT_1 = 0x6B; diff --git a/esphome/components/mqtt/__init__.py b/esphome/components/mqtt/__init__.py index 906c570b17..3559fce046 100644 --- a/esphome/components/mqtt/__init__.py +++ b/esphome/components/mqtt/__init__.py @@ -91,6 +91,7 @@ MQTTJSONLightComponent = mqtt_ns.class_("MQTTJSONLightComponent", MQTTComponent) MQTTSensorComponent = mqtt_ns.class_("MQTTSensorComponent", MQTTComponent) MQTTSwitchComponent = mqtt_ns.class_("MQTTSwitchComponent", MQTTComponent) MQTTTextSensor = mqtt_ns.class_("MQTTTextSensor", MQTTComponent) +MQTTNumberComponent = mqtt_ns.class_("MQTTNumberComponent", MQTTComponent) def validate_config(value): diff --git a/esphome/components/mqtt/custom_mqtt_device.cpp b/esphome/components/mqtt/custom_mqtt_device.cpp index 8b17c5f17f..9dec9498ad 100644 --- a/esphome/components/mqtt/custom_mqtt_device.cpp +++ b/esphome/components/mqtt/custom_mqtt_device.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace mqtt { -static const char *TAG = "mqtt.custom"; +static const char *const TAG = "mqtt.custom"; bool CustomMQTTDevice::publish(const std::string &topic, const std::string &payload, uint8_t qos, bool retain) { return global_mqtt_client->publish(topic, payload, qos, retain); diff --git a/esphome/components/mqtt/mqtt_binary_sensor.cpp b/esphome/components/mqtt/mqtt_binary_sensor.cpp index edabcb398c..53a49c6844 100644 --- a/esphome/components/mqtt/mqtt_binary_sensor.cpp +++ b/esphome/components/mqtt/mqtt_binary_sensor.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace mqtt { -static const char *TAG = "mqtt.binary_sensor"; +static const char *const TAG = "mqtt.binary_sensor"; std::string MQTTBinarySensorComponent::component_type() const { return "binary_sensor"; } diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index 3330956b52..afb67d36ed 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -1,8 +1,10 @@ #include "mqtt_client.h" -#include "esphome/core/log.h" + #include "esphome/core/application.h" #include "esphome/core/helpers.h" +#include "esphome/core/log.h" #include "esphome/core/util.h" +#include #ifdef USE_LOGGER #include "esphome/components/logger/logger.h" #endif @@ -13,7 +15,7 @@ namespace esphome { namespace mqtt { -static const char *TAG = "mqtt"; +static const char *const TAG = "mqtt"; MQTTClientComponent::MQTTClientComponent() { global_mqtt_client = this; @@ -340,7 +342,7 @@ void MQTTClientComponent::subscribe(const std::string &topic, mqtt_callback_t ca this->subscriptions_.push_back(subscription); } -void MQTTClientComponent::subscribe_json(const std::string &topic, mqtt_json_callback_t callback, uint8_t qos) { +void MQTTClientComponent::subscribe_json(const std::string &topic, const mqtt_json_callback_t &callback, uint8_t qos) { auto f = [callback](const std::string &topic, const std::string &payload) { json::parse_json(payload, [topic, callback](JsonObject &root) { callback(topic, root); }); }; @@ -558,10 +560,10 @@ void MQTTClientComponent::add_ssl_fingerprint(const std::arrayqos_ = qos; } void MQTTMessageTrigger::set_payload(const std::string &payload) { this->payload_ = payload; } void MQTTMessageTrigger::setup() { diff --git a/esphome/components/mqtt/mqtt_client.h b/esphome/components/mqtt/mqtt_client.h index e4f7c479b2..119e61db2b 100644 --- a/esphome/components/mqtt/mqtt_client.h +++ b/esphome/components/mqtt/mqtt_client.h @@ -157,7 +157,7 @@ class MQTTClientComponent : public Component { * received. * @param qos The QoS of this subscription. */ - void subscribe_json(const std::string &topic, mqtt_json_callback_t callback, uint8_t qos = 0); + void subscribe_json(const std::string &topic, const mqtt_json_callback_t &callback, uint8_t qos = 0); /** Unsubscribe from an MQTT topic. * @@ -275,11 +275,11 @@ class MQTTClientComponent : public Component { optional disconnect_reason_{}; }; -extern MQTTClientComponent *global_mqtt_client; +extern MQTTClientComponent *global_mqtt_client; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) class MQTTMessageTrigger : public Trigger, public Component { public: - explicit MQTTMessageTrigger(const std::string &topic); + explicit MQTTMessageTrigger(std::string topic); void set_qos(uint8_t qos); void set_payload(const std::string &payload); diff --git a/esphome/components/mqtt/mqtt_climate.cpp b/esphome/components/mqtt/mqtt_climate.cpp index ed3193ae97..ab8354e66c 100644 --- a/esphome/components/mqtt/mqtt_climate.cpp +++ b/esphome/components/mqtt/mqtt_climate.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace mqtt { -static const char *TAG = "mqtt.climate"; +static const char *const TAG = "mqtt.climate"; using namespace esphome::climate; @@ -97,6 +97,8 @@ void MQTTClimateComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryC fan_modes.add("focus"); if (traits.supports_fan_mode(CLIMATE_FAN_DIFFUSE)) fan_modes.add("diffuse"); + for (const auto &fan_mode : traits.get_supported_custom_fan_modes()) + fan_modes.add(fan_mode); } if (traits.get_supports_swing_modes()) { @@ -291,36 +293,39 @@ bool MQTTClimateComponent::publish_state_() { } if (traits.get_supports_fan_modes()) { - const char *payload = ""; - switch (this->device_->fan_mode.value()) { - case CLIMATE_FAN_ON: - payload = "on"; - break; - case CLIMATE_FAN_OFF: - payload = "off"; - break; - case CLIMATE_FAN_AUTO: - payload = "auto"; - break; - case CLIMATE_FAN_LOW: - payload = "low"; - break; - case CLIMATE_FAN_MEDIUM: - payload = "medium"; - break; - case CLIMATE_FAN_HIGH: - payload = "high"; - break; - case CLIMATE_FAN_MIDDLE: - payload = "middle"; - break; - case CLIMATE_FAN_FOCUS: - payload = "focus"; - break; - case CLIMATE_FAN_DIFFUSE: - payload = "diffuse"; - break; - } + std::string payload; + if (this->device_->fan_mode.has_value()) + switch (this->device_->fan_mode.value()) { + case CLIMATE_FAN_ON: + payload = "on"; + break; + case CLIMATE_FAN_OFF: + payload = "off"; + break; + case CLIMATE_FAN_AUTO: + payload = "auto"; + break; + case CLIMATE_FAN_LOW: + payload = "low"; + break; + case CLIMATE_FAN_MEDIUM: + payload = "medium"; + break; + case CLIMATE_FAN_HIGH: + payload = "high"; + break; + case CLIMATE_FAN_MIDDLE: + payload = "middle"; + break; + case CLIMATE_FAN_FOCUS: + payload = "focus"; + break; + case CLIMATE_FAN_DIFFUSE: + payload = "diffuse"; + break; + } + if (this->device_->custom_fan_mode.has_value()) + payload = this->device_->custom_fan_mode.value(); if (!this->publish(this->get_fan_mode_state_topic(), payload)) success = false; } diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index 9b4255060f..13acdcacd8 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -7,7 +7,7 @@ namespace esphome { namespace mqtt { -static const char *TAG = "mqtt.component"; +static const char *const TAG = "mqtt.component"; void MQTTComponent::set_retain(bool retain) { this->retain_ = retain; } @@ -124,8 +124,8 @@ void MQTTComponent::subscribe(const std::string &topic, mqtt_callback_t callback global_mqtt_client->subscribe(topic, std::move(callback), qos); } -void MQTTComponent::subscribe_json(const std::string &topic, mqtt_json_callback_t callback, uint8_t qos) { - global_mqtt_client->subscribe_json(topic, std::move(callback), qos); +void MQTTComponent::subscribe_json(const std::string &topic, const mqtt_json_callback_t &callback, uint8_t qos) { + global_mqtt_client->subscribe_json(topic, callback, qos); } MQTTComponent::MQTTComponent() = default; diff --git a/esphome/components/mqtt/mqtt_component.h b/esphome/components/mqtt/mqtt_component.h index 43465af498..4d7a522d5f 100644 --- a/esphome/components/mqtt/mqtt_component.h +++ b/esphome/components/mqtt/mqtt_component.h @@ -127,7 +127,7 @@ class MQTTComponent : public Component { * received. * @param qos The MQTT quality of service. Defaults to 0. */ - void subscribe_json(const std::string &topic, mqtt_json_callback_t callback, uint8_t qos = 0); + void subscribe_json(const std::string &topic, const mqtt_json_callback_t &callback, uint8_t qos = 0); protected: /// Helper method to get the discovery topic for this component. diff --git a/esphome/components/mqtt/mqtt_cover.cpp b/esphome/components/mqtt/mqtt_cover.cpp index a414c261f0..25ac430abb 100644 --- a/esphome/components/mqtt/mqtt_cover.cpp +++ b/esphome/components/mqtt/mqtt_cover.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace mqtt { -static const char *TAG = "mqtt.cover"; +static const char *const TAG = "mqtt.cover"; using namespace esphome::cover; diff --git a/esphome/components/mqtt/mqtt_fan.cpp b/esphome/components/mqtt/mqtt_fan.cpp index c020d73105..4171dae04c 100644 --- a/esphome/components/mqtt/mqtt_fan.cpp +++ b/esphome/components/mqtt/mqtt_fan.cpp @@ -7,7 +7,7 @@ namespace esphome { namespace mqtt { -static const char *TAG = "mqtt.fan"; +static const char *const TAG = "mqtt.fan"; using namespace esphome::fan; diff --git a/esphome/components/mqtt/mqtt_light.cpp b/esphome/components/mqtt/mqtt_light.cpp index d392169435..4bd0882b8c 100644 --- a/esphome/components/mqtt/mqtt_light.cpp +++ b/esphome/components/mqtt/mqtt_light.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace mqtt { -static const char *TAG = "mqtt.light"; +static const char *const TAG = "mqtt.light"; using namespace esphome::light; diff --git a/esphome/components/mqtt/mqtt_number.cpp b/esphome/components/mqtt/mqtt_number.cpp new file mode 100644 index 0000000000..0311526340 --- /dev/null +++ b/esphome/components/mqtt/mqtt_number.cpp @@ -0,0 +1,65 @@ +#include "mqtt_number.h" +#include "esphome/core/log.h" + +#ifdef USE_NUMBER + +namespace esphome { +namespace mqtt { + +static const char *const TAG = "mqtt.number"; + +using namespace esphome::number; + +MQTTNumberComponent::MQTTNumberComponent(Number *number) : MQTTComponent(), number_(number) {} + +void MQTTNumberComponent::setup() { + this->subscribe(this->get_command_topic_(), [this](const std::string &topic, const std::string &state) { + auto val = parse_float(state); + if (!val.has_value()) { + ESP_LOGW(TAG, "Can't convert '%s' to number!", state.c_str()); + return; + } + auto call = this->number_->make_call(); + call.set_value(*val); + call.perform(); + }); + this->number_->add_on_state_callback([this](float state) { this->publish_state(state); }); +} + +void MQTTNumberComponent::dump_config() { + ESP_LOGCONFIG(TAG, "MQTT Number '%s':", this->number_->get_name().c_str()); + LOG_MQTT_COMPONENT(true, false) +} + +std::string MQTTNumberComponent::component_type() const { return "number"; } + +std::string MQTTNumberComponent::friendly_name() const { return this->number_->get_name(); } +void MQTTNumberComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) { + const auto &traits = number_->traits; + // https://www.home-assistant.io/integrations/number.mqtt/ + if (!traits.get_icon().empty()) + root["icon"] = traits.get_icon(); + root["min_value"] = traits.get_min_value(); + root["max_value"] = traits.get_max_value(); + root["step"] = traits.get_step(); + + config.command_topic = true; +} +bool MQTTNumberComponent::send_initial_state() { + if (this->number_->has_state()) { + return this->publish_state(this->number_->state); + } else { + return true; + } +} +bool MQTTNumberComponent::is_internal() { return this->number_->is_internal(); } +bool MQTTNumberComponent::publish_state(float value) { + char buffer[64]; + snprintf(buffer, sizeof(buffer), "%f", value); + return this->publish(this->get_state_topic_(), buffer); +} + +} // namespace mqtt +} // namespace esphome + +#endif diff --git a/esphome/components/mqtt/mqtt_number.h b/esphome/components/mqtt/mqtt_number.h new file mode 100644 index 0000000000..f44de91435 --- /dev/null +++ b/esphome/components/mqtt/mqtt_number.h @@ -0,0 +1,46 @@ +#pragma once + +#include "esphome/core/defines.h" + +#ifdef USE_NUMBER + +#include "esphome/components/number/number.h" +#include "mqtt_component.h" + +namespace esphome { +namespace mqtt { + +class MQTTNumberComponent : public mqtt::MQTTComponent { + public: + /** Construct this MQTTNumberComponent instance with the provided friendly_name and number + * + * @param number The number. + */ + explicit MQTTNumberComponent(number::Number *number); + + // ========== INTERNAL METHODS ========== + // (In most use cases you won't need these) + /// Override setup. + void setup() override; + void dump_config() override; + + void send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) override; + + bool send_initial_state() override; + bool is_internal() override; + + bool publish_state(float value); + + protected: + /// Override for MQTTComponent, returns "number". + std::string component_type() const override; + + std::string friendly_name() const override; + + number::Number *number_; +}; + +} // namespace mqtt +} // namespace esphome + +#endif diff --git a/esphome/components/mqtt/mqtt_sensor.cpp b/esphome/components/mqtt/mqtt_sensor.cpp index f87e7651b9..c106a95902 100644 --- a/esphome/components/mqtt/mqtt_sensor.cpp +++ b/esphome/components/mqtt/mqtt_sensor.cpp @@ -10,7 +10,7 @@ namespace esphome { namespace mqtt { -static const char *TAG = "mqtt.sensor"; +static const char *const TAG = "mqtt.sensor"; using namespace esphome::sensor; @@ -46,6 +46,9 @@ void MQTTSensorComponent::set_expire_after(uint32_t expire_after) { this->expire void MQTTSensorComponent::disable_expire_after() { this->expire_after_ = 0; } std::string MQTTSensorComponent::friendly_name() const { return this->sensor_->get_name(); } void MQTTSensorComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) { + if (!this->sensor_->get_device_class().empty()) + root["device_class"] = this->sensor_->get_device_class(); + if (!this->sensor_->get_unit_of_measurement().empty()) root["unit_of_measurement"] = this->sensor_->get_unit_of_measurement(); diff --git a/esphome/components/mqtt/mqtt_switch.cpp b/esphome/components/mqtt/mqtt_switch.cpp index a01d6a6c6e..b73e1ab8dc 100644 --- a/esphome/components/mqtt/mqtt_switch.cpp +++ b/esphome/components/mqtt/mqtt_switch.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace mqtt { -static const char *TAG = "mqtt.switch"; +static const char *const TAG = "mqtt.switch"; using namespace esphome::switch_; diff --git a/esphome/components/mqtt/mqtt_text_sensor.cpp b/esphome/components/mqtt/mqtt_text_sensor.cpp index 37d475d25d..8bc11d954c 100644 --- a/esphome/components/mqtt/mqtt_text_sensor.cpp +++ b/esphome/components/mqtt/mqtt_text_sensor.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace mqtt { -static const char *TAG = "mqtt.text_sensor"; +static const char *const TAG = "mqtt.text_sensor"; using namespace esphome::text_sensor; diff --git a/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.cpp b/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.cpp index f6ecb0df36..06251bd7c8 100644 --- a/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.cpp +++ b/esphome/components/mqtt_subscribe/sensor/mqtt_subscribe_sensor.cpp @@ -4,12 +4,12 @@ namespace esphome { namespace mqtt_subscribe { -static const char *TAG = "mqtt_subscribe.sensor"; +static const char *const TAG = "mqtt_subscribe.sensor"; void MQTTSubscribeSensor::setup() { mqtt::global_mqtt_client->subscribe( this->topic_, - [this](const std::string &topic, std::string payload) { + [this](const std::string &topic, const std::string &payload) { auto val = parse_float(payload); if (!val.has_value()) { ESP_LOGW(TAG, "Can't convert '%s' to number!", payload.c_str()); diff --git a/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.cpp b/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.cpp index e7373c4b95..2b0908979c 100644 --- a/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.cpp +++ b/esphome/components/mqtt_subscribe/text_sensor/mqtt_subscribe_text_sensor.cpp @@ -1,14 +1,16 @@ #include "mqtt_subscribe_text_sensor.h" + #include "esphome/core/log.h" +#include namespace esphome { namespace mqtt_subscribe { -static const char *TAG = "mqtt_subscribe.text_sensor"; +static const char *const TAG = "mqtt_subscribe.text_sensor"; void MQTTSubscribeTextSensor::setup() { this->parent_->subscribe( - this->topic_, [this](const std::string &topic, std::string payload) { this->publish_state(payload); }, + this->topic_, [this](const std::string &topic, const std::string &payload) { this->publish_state(payload); }, this->qos_); } float MQTTSubscribeTextSensor::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; } diff --git a/esphome/components/ms5611/ms5611.cpp b/esphome/components/ms5611/ms5611.cpp index 39bce9f32c..51dc569240 100644 --- a/esphome/components/ms5611/ms5611.cpp +++ b/esphome/components/ms5611/ms5611.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace ms5611 { -static const char *TAG = "ms5611"; +static const char *const TAG = "ms5611"; static const uint8_t MS5611_ADDRESS = 0x77; static const uint8_t MS5611_CMD_ADC_READ = 0x00; diff --git a/esphome/components/my9231/my9231.cpp b/esphome/components/my9231/my9231.cpp index 4b4603d56b..a97587b7be 100644 --- a/esphome/components/my9231/my9231.cpp +++ b/esphome/components/my9231/my9231.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace my9231 { -static const char *TAG = "my9231.output"; +static const char *const TAG = "my9231.output"; // One-shot select (frame cycle repeat mode / frame cycle One-shot mode) static const uint8_t MY9231_CMD_ONE_SHOT_DISABLE = 0x0 << 6; diff --git a/esphome/components/nextion/__init__.py b/esphome/components/nextion/__init__.py index 67a49df9fa..924d58198d 100644 --- a/esphome/components/nextion/__init__.py +++ b/esphome/components/nextion/__init__.py @@ -1,3 +1,8 @@ import esphome.codegen as cg +from esphome.components import uart nextion_ns = cg.esphome_ns.namespace("nextion") +Nextion = nextion_ns.class_("Nextion", cg.PollingComponent, uart.UARTDevice) +nextion_ref = Nextion.operator("ref") + +CONF_NEXTION_ID = "nextion_id" diff --git a/esphome/components/nextion/automation.h b/esphome/components/nextion/automation.h new file mode 100644 index 0000000000..5f4219acb1 --- /dev/null +++ b/esphome/components/nextion/automation.h @@ -0,0 +1,30 @@ +#pragma once +#include "esphome/core/automation.h" +#include "nextion.h" + +namespace esphome { +namespace nextion { + +class SetupTrigger : public Trigger<> { + public: + explicit SetupTrigger(Nextion *nextion) { + nextion->add_setup_state_callback([this]() { this->trigger(); }); + } +}; + +class SleepTrigger : public Trigger<> { + public: + explicit SleepTrigger(Nextion *nextion) { + nextion->add_sleep_state_callback([this]() { this->trigger(); }); + } +}; + +class WakeTrigger : public Trigger<> { + public: + explicit WakeTrigger(Nextion *nextion) { + nextion->add_wake_state_callback([this]() { this->trigger(); }); + } +}; + +} // namespace nextion +} // namespace esphome diff --git a/esphome/components/nextion/base_component.py b/esphome/components/nextion/base_component.py new file mode 100644 index 0000000000..3bf828aafa --- /dev/null +++ b/esphome/components/nextion/base_component.py @@ -0,0 +1,126 @@ +from string import ascii_letters, digits +import esphome.config_validation as cv +import esphome.codegen as cg +from esphome.components import color + +from . import CONF_NEXTION_ID +from . import Nextion + +CONF_VARIABLE_NAME = "variable_name" +CONF_COMPONENT_NAME = "component_name" +CONF_WAVE_CHANNEL_ID = "wave_channel_id" +CONF_WAVE_MAX_VALUE = "wave_max_value" +CONF_PRECISION = "precision" +CONF_WAVEFORM_SEND_LAST_VALUE = "waveform_send_last_value" +CONF_TFT_URL = "tft_url" +CONF_ON_SLEEP = "on_sleep" +CONF_ON_WAKE = "on_wake" +CONF_ON_SETUP = "on_setup" +CONF_TOUCH_SLEEP_TIMEOUT = "touch_sleep_timeout" +CONF_WAKE_UP_PAGE = "wake_up_page" +CONF_AUTO_WAKE_ON_TOUCH = "auto_wake_on_touch" +CONF_WAVE_MAX_LENGTH = "wave_max_length" +CONF_BACKGROUND_COLOR = "background_color" +CONF_BACKGROUND_PRESSED_COLOR = "background_pressed_color" +CONF_FOREGROUND_COLOR = "foreground_color" +CONF_FOREGROUND_PRESSED_COLOR = "foreground_pressed_color" +CONF_FONT_ID = "font_id" +CONF_VISIBLE = "visible" + + +def NextionName(value): + valid_chars = ascii_letters + digits + "." + if not isinstance(value, str) or len(value) > 29: + raise cv.Invalid("Must be a string less than 29 characters") + + for char in value: + if char not in valid_chars: + raise cv.Invalid( + "Must only consist of upper/lowercase characters, numbers and the period '.'. The character '{}' cannot be used.".format( + char + ) + ) + + return value + + +CONFIG_BASE_COMPONENT_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_NEXTION_ID): cv.use_id(Nextion), + cv.Optional(CONF_BACKGROUND_COLOR): cv.use_id(color), + cv.Optional(CONF_FOREGROUND_COLOR): cv.use_id(color), + cv.Optional(CONF_VISIBLE, default=True): cv.boolean, + } +) + + +CONFIG_TEXT_COMPONENT_SCHEMA = CONFIG_BASE_COMPONENT_SCHEMA.extend( + cv.Schema( + { + cv.Required(CONF_COMPONENT_NAME): NextionName, + cv.Optional(CONF_FONT_ID): cv.int_range(min=0, max=255), + } + ) +) + +CONFIG_BINARY_SENSOR_SCHEMA = CONFIG_BASE_COMPONENT_SCHEMA.extend( + cv.Schema( + { + cv.Optional(CONF_COMPONENT_NAME): NextionName, + cv.Optional(CONF_VARIABLE_NAME): NextionName, + } + ) +) + +CONFIG_SENSOR_COMPONENT_SCHEMA = CONFIG_BINARY_SENSOR_SCHEMA.extend( + cv.Schema( + { + cv.Optional(CONF_FONT_ID): cv.int_range(min=0, max=255), + } + ) +) + + +CONFIG_SWITCH_COMPONENT_SCHEMA = CONFIG_SENSOR_COMPONENT_SCHEMA.extend( + cv.Schema( + { + cv.Optional(CONF_FOREGROUND_PRESSED_COLOR): cv.use_id(color), + cv.Optional(CONF_BACKGROUND_PRESSED_COLOR): cv.use_id(color), + } + ) +) + + +async def setup_component_core_(var, config, arg): + + if CONF_VARIABLE_NAME in config: + cg.add(var.set_variable_name(config[CONF_VARIABLE_NAME])) + elif CONF_COMPONENT_NAME in config: + cg.add( + var.set_variable_name( + config[CONF_COMPONENT_NAME], + config[CONF_COMPONENT_NAME] + arg, + ) + ) + + if CONF_BACKGROUND_COLOR in config: + color_component = await cg.get_variable(config[CONF_BACKGROUND_COLOR]) + cg.add(var.set_background_color(color_component)) + + if CONF_BACKGROUND_PRESSED_COLOR in config: + color_component = await cg.get_variable(config[CONF_BACKGROUND_PRESSED_COLOR]) + cg.add(var.set_background_pressed_color(color_component)) + + if CONF_FOREGROUND_COLOR in config: + color_component = await cg.get_variable(config[CONF_FOREGROUND_COLOR]) + cg.add(var.set_foreground_color(color_component)) + + if CONF_FOREGROUND_PRESSED_COLOR in config: + color_component = await cg.get_variable(config[CONF_FOREGROUND_PRESSED_COLOR]) + cg.add(var.set_foreground_pressed_color(color_component)) + + if CONF_FONT_ID in config: + cg.add(var.set_font_id(config[CONF_FONT_ID])) + + if CONF_VISIBLE in config: + cg.add(var.set_visible(config[CONF_VISIBLE])) diff --git a/esphome/components/nextion/binary_sensor.py b/esphome/components/nextion/binary_sensor.py deleted file mode 100644 index ed4e8d832a..0000000000 --- a/esphome/components/nextion/binary_sensor.py +++ /dev/null @@ -1,34 +0,0 @@ -import esphome.codegen as cg -import esphome.config_validation as cv -from esphome.components import binary_sensor -from esphome.const import CONF_COMPONENT_ID, CONF_PAGE_ID, CONF_ID -from . import nextion_ns -from .display import Nextion - -DEPENDENCIES = ["display"] - -CONF_NEXTION_ID = "nextion_id" - -NextionTouchComponent = nextion_ns.class_( - "NextionTouchComponent", binary_sensor.BinarySensor -) - -CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(NextionTouchComponent), - cv.GenerateID(CONF_NEXTION_ID): cv.use_id(Nextion), - cv.Required(CONF_PAGE_ID): cv.uint8_t, - cv.Required(CONF_COMPONENT_ID): cv.uint8_t, - } -) - - -async def to_code(config): - var = cg.new_Pvariable(config[CONF_ID]) - await binary_sensor.register_binary_sensor(var, config) - - hub = await cg.get_variable(config[CONF_NEXTION_ID]) - cg.add(hub.register_touch_component(var)) - - cg.add(var.set_component_id(config[CONF_COMPONENT_ID])) - cg.add(var.set_page_id(config[CONF_PAGE_ID])) diff --git a/esphome/components/nextion/binary_sensor/__init__.py b/esphome/components/nextion/binary_sensor/__init__.py new file mode 100644 index 0000000000..090fae3429 --- /dev/null +++ b/esphome/components/nextion/binary_sensor/__init__.py @@ -0,0 +1,54 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import binary_sensor + +from esphome.const import CONF_COMPONENT_ID, CONF_PAGE_ID, CONF_ID +from .. import nextion_ns, CONF_NEXTION_ID + + +from ..base_component import ( + setup_component_core_, + CONFIG_BINARY_SENSOR_SCHEMA, + CONF_VARIABLE_NAME, + CONF_COMPONENT_NAME, +) + +CODEOWNERS = ["@senexcrenshaw"] + +NextionBinarySensor = nextion_ns.class_( + "NextionBinarySensor", binary_sensor.BinarySensor, cg.PollingComponent +) + +CONFIG_SCHEMA = cv.All( + binary_sensor.BINARY_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(NextionBinarySensor), + cv.Optional(CONF_PAGE_ID): cv.uint8_t, + cv.Optional(CONF_COMPONENT_ID): cv.uint8_t, + } + ) + .extend(CONFIG_BINARY_SENSOR_SCHEMA) + .extend(cv.polling_component_schema("never")), + cv.has_at_least_one_key( + CONF_PAGE_ID, + CONF_COMPONENT_ID, + CONF_COMPONENT_NAME, + CONF_VARIABLE_NAME, + ), +) + + +async def to_code(config): + hub = await cg.get_variable(config[CONF_NEXTION_ID]) + var = cg.new_Pvariable(config[CONF_ID], hub) + await binary_sensor.register_binary_sensor(var, config) + await cg.register_component(var, config) + + if config.keys() >= {CONF_PAGE_ID, CONF_COMPONENT_ID}: + cg.add(hub.register_touch_component(var)) + cg.add(var.set_component_id(config[CONF_COMPONENT_ID])) + cg.add(var.set_page_id(config[CONF_PAGE_ID])) + + if CONF_COMPONENT_NAME in config or CONF_VARIABLE_NAME in config: + await setup_component_core_(var, config, ".val") + cg.add(hub.register_binarysensor_component(var)) diff --git a/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp b/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp new file mode 100644 index 0000000000..bf6e74cb38 --- /dev/null +++ b/esphome/components/nextion/binary_sensor/nextion_binarysensor.cpp @@ -0,0 +1,69 @@ +#include "nextion_binarysensor.h" +#include "esphome/core/util.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace nextion { + +static const char *const TAG = "nextion_binarysensor"; + +void NextionBinarySensor::process_bool(const std::string &variable_name, bool state) { + if (!this->nextion_->is_setup()) + return; + + if (this->variable_name_.empty()) // This is a touch component + return; + + if (this->variable_name_ == variable_name) { + this->publish_state(state); + ESP_LOGD(TAG, "Processed binarysensor \"%s\" state %s", variable_name.c_str(), state ? "ON" : "OFF"); + } +} + +void NextionBinarySensor::process_touch(uint8_t page_id, uint8_t component_id, bool state) { + if (this->page_id_ == page_id && this->component_id_ == component_id) { + this->publish_state(state); + } +} + +void NextionBinarySensor::update() { + if (!this->nextion_->is_setup()) + return; + + if (this->variable_name_.empty()) // This is a touch component + return; + + this->nextion_->add_to_get_queue(this); +} + +void NextionBinarySensor::set_state(bool state, bool publish, bool send_to_nextion) { + if (!this->nextion_->is_setup()) + return; + + if (this->component_id_ == 0) // This is a legacy touch component + return; + + if (send_to_nextion) { + if (this->nextion_->is_sleeping() || !this->visible_) { + this->needs_to_send_update_ = true; + } else { + this->needs_to_send_update_ = false; + this->nextion_->add_no_result_to_queue_with_set(this, (int) state); + } + } + + if (publish) { + this->publish_state(state); + } else { + this->state = state; + this->has_state_ = true; + } + + this->update_component_settings(); + + ESP_LOGN(TAG, "Wrote state for sensor \"%s\" state %s", this->variable_name_.c_str(), + ONOFF(this->variable_name_.c_str())); +} + +} // namespace nextion +} // namespace esphome diff --git a/esphome/components/nextion/binary_sensor/nextion_binarysensor.h b/esphome/components/nextion/binary_sensor/nextion_binarysensor.h new file mode 100644 index 0000000000..b6b23ada85 --- /dev/null +++ b/esphome/components/nextion/binary_sensor/nextion_binarysensor.h @@ -0,0 +1,42 @@ +#pragma once +#include "esphome/core/component.h" +#include "esphome/components/binary_sensor/binary_sensor.h" +#include "../nextion_component.h" +#include "../nextion_base.h" + +namespace esphome { +namespace nextion { +class NextionBinarySensor; + +class NextionBinarySensor : public NextionComponent, + public binary_sensor::BinarySensorInitiallyOff, + public PollingComponent { + public: + NextionBinarySensor(NextionBase *nextion) { this->nextion_ = nextion; } + + void update_component() override { this->update(); } + void update() override; + void send_state_to_nextion() override { this->set_state(this->state, false); }; + void process_bool(const std::string &variable_name, bool state) override; + void process_touch(uint8_t page_id, uint8_t component_id, bool state) override; + + // Set the components page id for Nextion Touch Component + void set_page_id(uint8_t page_id) { page_id_ = page_id; } + // Set the components component id for Nextion Touch Component + void set_component_id(uint8_t component_id) { component_id_ = component_id; } + + void set_state(bool state) override { this->set_state(state, true, true); } + void set_state(bool state, bool publish) override { this->set_state(state, publish, true); } + void set_state(bool state, bool publish, bool send_to_nextion) override; + + NextionQueueType get_queue_type() override { return NextionQueueType::BINARY_SENSOR; } + void set_state_from_string(const std::string &state_value, bool publish, bool send_to_nextion) override {} + void set_state_from_int(int state_value, bool publish, bool send_to_nextion) override { + this->set_state(state_value != 0, publish, send_to_nextion); + } + + protected: + uint8_t page_id_; +}; +} // namespace nextion +} // namespace esphome diff --git a/esphome/components/nextion/display.py b/esphome/components/nextion/display.py index 7d7018a4c4..e693b2f1ec 100644 --- a/esphome/components/nextion/display.py +++ b/esphome/components/nextion/display.py @@ -1,20 +1,58 @@ import esphome.codegen as cg import esphome.config_validation as cv +from esphome import automation from esphome.components import display, uart -from esphome.const import CONF_ID, CONF_LAMBDA, CONF_BRIGHTNESS -from . import nextion_ns +from esphome.const import ( + CONF_ID, + CONF_LAMBDA, + CONF_BRIGHTNESS, + CONF_TRIGGER_ID, +) + +from . import Nextion, nextion_ns, nextion_ref +from .base_component import ( + CONF_ON_SLEEP, + CONF_ON_WAKE, + CONF_ON_SETUP, + CONF_TFT_URL, + CONF_TOUCH_SLEEP_TIMEOUT, + CONF_WAKE_UP_PAGE, + CONF_AUTO_WAKE_ON_TOUCH, +) + +CODEOWNERS = ["@senexcrenshaw"] DEPENDENCIES = ["uart"] -AUTO_LOAD = ["binary_sensor"] +AUTO_LOAD = ["binary_sensor", "switch", "sensor", "text_sensor"] -Nextion = nextion_ns.class_("Nextion", cg.PollingComponent, uart.UARTDevice) -NextionRef = Nextion.operator("ref") +SetupTrigger = nextion_ns.class_("SetupTrigger", automation.Trigger.template()) +SleepTrigger = nextion_ns.class_("SleepTrigger", automation.Trigger.template()) +WakeTrigger = nextion_ns.class_("WakeTrigger", automation.Trigger.template()) CONFIG_SCHEMA = ( display.BASIC_DISPLAY_SCHEMA.extend( { cv.GenerateID(): cv.declare_id(Nextion), + cv.Optional(CONF_TFT_URL): cv.string, cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage, + cv.Optional(CONF_ON_SETUP): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SetupTrigger), + } + ), + cv.Optional(CONF_ON_SLEEP): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SleepTrigger), + } + ), + cv.Optional(CONF_ON_WAKE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(WakeTrigger), + } + ), + cv.Optional(CONF_TOUCH_SLEEP_TIMEOUT): cv.int_range(min=3, max=65535), + cv.Optional(CONF_WAKE_UP_PAGE): cv.positive_int, + cv.Optional(CONF_AUTO_WAKE_ON_TOUCH, default=True): cv.boolean, } ) .extend(cv.polling_component_schema("5s")) @@ -31,8 +69,33 @@ async def to_code(config): cg.add(var.set_brightness(config[CONF_BRIGHTNESS])) if CONF_LAMBDA in config: lambda_ = await cg.process_lambda( - config[CONF_LAMBDA], [(NextionRef, "it")], return_type=cg.void + config[CONF_LAMBDA], [(nextion_ref, "it")], return_type=cg.void ) cg.add(var.set_writer(lambda_)) + if CONF_TFT_URL in config: + cg.add_define("USE_TFT_UPLOAD") + cg.add(var.set_tft_url(config[CONF_TFT_URL])) + + if CONF_TOUCH_SLEEP_TIMEOUT in config: + cg.add(var.set_touch_sleep_timeout_internal(config[CONF_TOUCH_SLEEP_TIMEOUT])) + + if CONF_WAKE_UP_PAGE in config: + cg.add(var.set_wake_up_page_internal(config[CONF_WAKE_UP_PAGE])) + + if CONF_AUTO_WAKE_ON_TOUCH in config: + cg.add(var.set_auto_wake_on_touch_internal(config[CONF_AUTO_WAKE_ON_TOUCH])) + await display.register_display(var, config) + + for conf in config.get(CONF_ON_SETUP, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + + for conf in config.get(CONF_ON_SLEEP, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + + for conf in config.get(CONF_ON_WAKE, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) diff --git a/esphome/components/nextion/nextion.cpp b/esphome/components/nextion/nextion.cpp index d18d9f33d8..9a5424917f 100644 --- a/esphome/components/nextion/nextion.cpp +++ b/esphome/components/nextion/nextion.cpp @@ -1,75 +1,179 @@ #include "nextion.h" +#include "esphome/core/util.h" #include "esphome/core/log.h" +#include "esphome/core/application.h" namespace esphome { namespace nextion { -static const char *TAG = "nextion"; +static const char *const TAG = "nextion"; void Nextion::setup() { - this->send_command_no_ack(""); - this->send_command_printf("bkcmd=3"); - this->set_backlight_brightness(static_cast(brightness_ * 100)); - this->goto_page("0"); + this->is_setup_ = false; + this->ignore_is_setup_ = true; + + // Wake up the nextion + this->send_command_("bkcmd=0"); + this->send_command_("sleep=0"); + + this->send_command_("bkcmd=0"); + this->send_command_("sleep=0"); + + // Reboot it + this->send_command_("rest"); + + this->ignore_is_setup_ = false; } -float Nextion::get_setup_priority() const { return setup_priority::PROCESSOR; } + +bool Nextion::send_command_(const std::string &command) { + if (!this->ignore_is_setup_ && !this->is_setup()) { + return false; + } + + ESP_LOGN(TAG, "send_command %s", command.c_str()); + + this->write_str(command.c_str()); + const uint8_t to_send[3] = {0xFF, 0xFF, 0xFF}; + this->write_array(to_send, sizeof(to_send)); + return true; +} + +bool Nextion::check_connect_() { + if (this->get_is_connected_()) + return true; + + if (this->comok_sent_ == 0) { + this->reset_(false); + + this->ignore_is_setup_ = true; + this->send_command_("boguscommand=0"); // bogus command. needed sometimes after updating + this->send_command_("connect"); + + this->comok_sent_ = millis(); + this->ignore_is_setup_ = false; + + return false; + } + + if (millis() - this->comok_sent_ <= 500) // Wait 500 ms + return false; + + std::string response; + + this->recv_ret_string_(response, 0, false); + if (response.empty() || response.find("comok") == std::string::npos) { +#ifdef NEXTION_PROTOCOL_LOG + ESP_LOGN(TAG, "Bad connect request %s", response.c_str()); + for (int i = 0; i < response.length(); i++) { + ESP_LOGN(TAG, "response %s %d %d %c", response.c_str(), i, response[i], response[i]); + } +#endif + + ESP_LOGW(TAG, "Nextion is not connected! "); + comok_sent_ = 0; + return false; + } + + this->ignore_is_setup_ = true; + ESP_LOGI(TAG, "Nextion is connected"); + this->is_connected_ = true; + + ESP_LOGN(TAG, "connect request %s", response.c_str()); + + size_t start; + size_t end = 0; + std::vector connect_info; + while ((start = response.find_first_not_of(',', end)) != std::string::npos) { + end = response.find(',', start); + connect_info.push_back(response.substr(start, end - start)); + } + + if (connect_info.size() == 7) { + ESP_LOGN(TAG, "Received connect_info %zu", connect_info.size()); + + this->device_model_ = connect_info[2]; + this->firmware_version_ = connect_info[3]; + this->serial_number_ = connect_info[5]; + this->flash_size_ = connect_info[6]; + } else { + ESP_LOGE(TAG, "Nextion returned bad connect value \"%s\"", response.c_str()); + } + + this->ignore_is_setup_ = false; + this->dump_config(); + return true; +} + +void Nextion::reset_(bool reset_nextion) { + uint8_t d; + + while (this->available()) { // Clear receive buffer + this->read_byte(&d); + }; + this->nextion_queue_.clear(); +} + +void Nextion::dump_config() { + ESP_LOGCONFIG(TAG, "Nextion:"); + ESP_LOGCONFIG(TAG, " Device Model: %s", this->device_model_.c_str()); + ESP_LOGCONFIG(TAG, " Firmware Version: %s", this->firmware_version_.c_str()); + ESP_LOGCONFIG(TAG, " Serial Number: %s", this->serial_number_.c_str()); + ESP_LOGCONFIG(TAG, " Flash Size: %s", this->flash_size_.c_str()); + ESP_LOGCONFIG(TAG, " Wake On Touch: %s", this->auto_wake_on_touch_ ? "True" : "False"); + + if (this->touch_sleep_timeout_ != 0) { + ESP_LOGCONFIG(TAG, " Touch Timeout: %d", this->touch_sleep_timeout_); + } + + if (this->wake_up_page_ != -1) { + ESP_LOGCONFIG(TAG, " Wake Up Page : %d", this->wake_up_page_); + } +} + +float Nextion::get_setup_priority() const { return setup_priority::DATA; } void Nextion::update() { + if (!this->is_setup()) { + return; + } if (this->writer_.has_value()) { (*this->writer_)(*this); } } -void Nextion::send_command_no_ack(const char *command) { - // Flush RX... - this->loop(); - this->write_str(command); - const uint8_t data[3] = {0xFF, 0xFF, 0xFF}; - this->write_array(data, sizeof(data)); +void Nextion::add_sleep_state_callback(std::function &&callback) { + this->sleep_callback_.add(std::move(callback)); } -bool Nextion::ack_() { - if (!this->wait_for_ack_) - return true; +void Nextion::add_wake_state_callback(std::function &&callback) { + this->wake_callback_.add(std::move(callback)); +} - uint32_t start = millis(); - while (!this->read_until_ack_()) { - if (millis() - start > 100) { - ESP_LOGW(TAG, "Waiting for ACK timed out!"); - return false; - } +void Nextion::add_setup_state_callback(std::function &&callback) { + this->setup_callback_.add(std::move(callback)); +} + +void Nextion::update_all_components() { + if ((!this->is_setup() && !this->ignore_is_setup_) || this->is_sleeping()) + return; + + for (auto *binarysensortype : this->binarysensortype_) { + binarysensortype->update_component(); + } + for (auto *sensortype : this->sensortype_) { + sensortype->update_component(); + } + for (auto *switchtype : this->switchtype_) { + switchtype->update_component(); + } + for (auto *textsensortype : this->textsensortype_) { + textsensortype->update_component(); } - return true; } -void Nextion::set_component_text(const char *component, const char *text) { - this->send_command_printf("%s.txt=\"%s\"", component, text); -} -void Nextion::set_component_value(const char *component, int value) { - this->send_command_printf("%s.val=%d", component, value); -} -void Nextion::display_picture(int picture_id, int x_start, int y_start) { - this->send_command_printf("pic %d %d %d", x_start, y_start, picture_id); -} -void Nextion::set_component_background_color(const char *component, const char *color) { - this->send_command_printf("%s.bco=\"%s\"", component, color); -} -void Nextion::set_component_pressed_background_color(const char *component, const char *color) { - this->send_command_printf("%s.bco2=\"%s\"", component, color); -} -void Nextion::set_component_font_color(const char *component, const char *color) { - this->send_command_printf("%s.pco=\"%s\"", component, color); -} -void Nextion::set_component_pressed_font_color(const char *component, const char *color) { - this->send_command_printf("%s.pco2=\"%s\"", component, color); -} -void Nextion::set_component_coordinates(const char *component, int x, int y) { - this->send_command_printf("%s.xcen=%d", component, x); - this->send_command_printf("%s.ycen=%d", component, y); -} -void Nextion::set_component_font(const char *component, uint8_t font_id) { - this->send_command_printf("%s.font=%d", component, font_id); -} -void Nextion::goto_page(const char *page) { this->send_command_printf("page %s", page); } + bool Nextion::send_command_printf(const char *format, ...) { + if ((!this->is_setup() && !this->ignore_is_setup_) || this->is_sleeping()) + return false; + char buffer[256]; va_list arg; va_start(arg, format); @@ -79,208 +183,911 @@ bool Nextion::send_command_printf(const char *format, ...) { ESP_LOGW(TAG, "Building command for format '%s' failed!", format); return false; } - this->send_command_no_ack(buffer); - if (!this->ack_()) { - ESP_LOGW(TAG, "Sending command '%s' failed because no ACK was received", buffer); + + if (this->send_command_(buffer)) { + this->add_no_result_to_queue_("send_command_printf"); + return true; + } + return false; +} + +#ifdef NEXTION_PROTOCOL_LOG +void Nextion::print_queue_members_() { + ESP_LOGN(TAG, "print_queue_members_ (top 10) size %zu", this->nextion_queue_.size()); + ESP_LOGN(TAG, "*******************************************"); + int count = 0; + for (auto *i : this->nextion_queue_) { + if (count++ == 10) + break; + + if (i == nullptr) { + ESP_LOGN(TAG, "Nextion queue is null"); + } else { + ESP_LOGN(TAG, "Nextion queue type: %d:%s , name: %s", i->component->get_queue_type(), + i->component->get_queue_type_string().c_str(), i->component->get_variable_name().c_str()); + } + } + ESP_LOGN(TAG, "*******************************************"); +} +#endif + +void Nextion::loop() { + if (!this->check_connect_() || this->is_updating_) + return; + + if (this->nextion_reports_is_setup_ && !this->sent_setup_commands_) { + this->ignore_is_setup_ = true; + this->sent_setup_commands_ = true; + this->send_command_("bkcmd=3"); // Always, returns 0x00 to 0x23 result of serial command. + + this->set_backlight_brightness(this->brightness_); + this->goto_page("0"); + + this->set_auto_wake_on_touch(this->auto_wake_on_touch_); + + if (this->touch_sleep_timeout_ != 0) { + this->set_touch_sleep_timeout(this->touch_sleep_timeout_); + } + + if (this->wake_up_page_ != -1) { + this->set_wake_up_page(this->wake_up_page_); + } + + this->ignore_is_setup_ = false; + } + + this->process_serial_(); // Receive serial data + this->process_nextion_commands_(); // Process nextion return commands + + if (!this->nextion_reports_is_setup_) { + if (this->started_ms_ == 0) + this->started_ms_ = millis(); + + if (this->started_ms_ + this->startup_override_ms_ < millis()) { + ESP_LOGD(TAG, "Manually set nextion report ready"); + this->nextion_reports_is_setup_ = true; + } + } +} + +bool Nextion::remove_from_q_(bool report_empty) { + if (this->nextion_queue_.empty()) { + if (report_empty) + ESP_LOGE(TAG, "Nextion queue is empty!"); return false; } + NextionQueue *nb = this->nextion_queue_.front(); + NextionComponentBase *component = nb->component; + + ESP_LOGN(TAG, "Removing %s from the queue", component->get_variable_name().c_str()); + + if (component->get_queue_type() == NextionQueueType::NO_RESULT) { + if (component->get_variable_name() == "sleep_wake") { + this->is_sleeping_ = false; + } + delete component; + } + delete nb; + this->nextion_queue_.pop_front(); return true; } -void Nextion::hide_component(const char *component) { this->send_command_printf("vis %s,0", component); } -void Nextion::show_component(const char *component) { this->send_command_printf("vis %s,1", component); } -void Nextion::enable_component_touch(const char *component) { this->send_command_printf("tsw %s,1", component); } -void Nextion::disable_component_touch(const char *component) { this->send_command_printf("tsw %s,0", component); } -void Nextion::add_waveform_data(int component_id, uint8_t channel_number, uint8_t value) { - this->send_command_printf("add %d,%u,%u", component_id, channel_number, value); -} -void Nextion::fill_area(int x1, int y1, int width, int height, const char *color) { - this->send_command_printf("fill %d,%d,%d,%d,%s", x1, y1, width, height, color); -} -void Nextion::line(int x1, int y1, int x2, int y2, const char *color) { - this->send_command_printf("line %d,%d,%d,%d,%s", x1, y1, x2, y2, color); -} -void Nextion::rectangle(int x1, int y1, int width, int height, const char *color) { - this->send_command_printf("draw %d,%d,%d,%d,%s", x1, y1, x1 + width, y1 + height, color); -} -void Nextion::circle(int center_x, int center_y, int radius, const char *color) { - this->send_command_printf("cir %d,%d,%d,%s", center_x, center_y, radius, color); -} -void Nextion::filled_circle(int center_x, int center_y, int radius, const char *color) { - this->send_command_printf("cirs %d,%d,%d,%s", center_x, center_y, radius, color); -} -bool Nextion::read_until_ack_() { - while (this->available() >= 4) { - // flush preceding filler bytes - uint8_t temp; - while (this->available() && this->peek_byte(&temp) && temp == 0xFF) - this->read_byte(&temp); - if (!this->available()) - break; +void Nextion::process_serial_() { + uint8_t d; - uint8_t event; - // event type - this->read_byte(&event); + while (this->available()) { + read_byte(&d); + this->command_data_ += d; + } +} +// nextion.tech/instruction-set/ +void Nextion::process_nextion_commands_() { + if (this->command_data_.length() == 0) { + return; + } - uint8_t data[255]; - // total length of data (including end bytes) - uint8_t data_length = 0; - // message is terminated by three consecutive 0xFF - // this variable keeps track of ohow many of those have - // been received - uint8_t end_length = 0; - while (this->available() && end_length < 3 && data_length < sizeof(data)) { - uint8_t byte; - this->read_byte(&byte); - if (byte == 0xFF) { - end_length++; - } else { - end_length = 0; - } - data[data_length++] = byte; + size_t to_process_length = 0; + std::string to_process; + + ESP_LOGN(TAG, "this->command_data_ %s length %d", this->command_data_.c_str(), this->command_data_.length()); +#ifdef NEXTION_PROTOCOL_LOG + this->print_queue_members_(); +#endif + while ((to_process_length = this->command_data_.find(COMMAND_DELIMITER)) != std::string::npos) { + ESP_LOGN(TAG, "print_queue_members_ size %zu", this->nextion_queue_.size()); + while (to_process_length + COMMAND_DELIMITER.length() < this->command_data_.length() && + static_cast(this->command_data_[to_process_length + COMMAND_DELIMITER.length()]) == 0xFF) { + ++to_process_length; + ESP_LOGN(TAG, "Add extra 0xFF to process"); } - if (end_length != 3) { - ESP_LOGW(TAG, "Received unknown filler end bytes from Nextion!"); - continue; - } + this->nextion_event_ = this->command_data_[0]; - data_length -= 3; // remove filler bytes + to_process_length -= 1; + to_process = this->command_data_.substr(1, to_process_length); - bool invalid_data_length = false; - switch (event) { - case 0x01: // successful execution of instruction (ACK) - return true; - case 0x00: // invalid instruction + switch (this->nextion_event_) { + case 0x00: // instruction sent by user has failed ESP_LOGW(TAG, "Nextion reported invalid instruction!"); + this->remove_from_q_(); + break; - case 0x02: // component ID invalid - ESP_LOGW(TAG, "Nextion reported component ID invalid!"); + case 0x01: // instruction sent by user was successful + + ESP_LOGVV(TAG, "instruction sent by user was successful"); + ESP_LOGN(TAG, "this->nextion_queue_.empty() %s", this->nextion_queue_.empty() ? "True" : "False"); + + this->remove_from_q_(); + if (!this->is_setup_) { + if (this->nextion_queue_.empty()) { + ESP_LOGD(TAG, "Nextion is setup"); + this->is_setup_ = true; + this->setup_callback_.call(); + } + } + break; - case 0x03: // page ID invalid + case 0x02: // invalid Component ID or name was used + this->remove_from_q_(); + break; + case 0x03: // invalid Page ID or name was used ESP_LOGW(TAG, "Nextion reported page ID invalid!"); + this->remove_from_q_(); break; - case 0x04: // picture ID invalid + case 0x04: // invalid Picture ID was used ESP_LOGW(TAG, "Nextion reported picture ID invalid!"); + this->remove_from_q_(); break; - case 0x05: // font ID invalid + case 0x05: // invalid Font ID was used ESP_LOGW(TAG, "Nextion reported font ID invalid!"); + this->remove_from_q_(); break; - case 0x11: // baud rate setting invalid + case 0x06: // File operation fails + ESP_LOGW(TAG, "Nextion File operation fail!"); + break; + case 0x09: // Instructions with CRC validation fails their CRC check + ESP_LOGW(TAG, "Nextion Instructions with CRC validation fails their CRC check!"); + break; + case 0x11: // invalid Baud rate was used ESP_LOGW(TAG, "Nextion reported baud rate invalid!"); break; - case 0x12: // curve control ID number or channel number is invalid - ESP_LOGW(TAG, "Nextion reported control/channel ID invalid!"); + case 0x12: // invalid Waveform ID or Channel # was used + + if (!this->nextion_queue_.empty()) { + int index = 0; + int found = -1; + for (auto &nb : this->nextion_queue_) { + NextionComponentBase *component = nb->component; + + if (component->get_queue_type() == NextionQueueType::WAVEFORM_SENSOR) { + ESP_LOGW(TAG, "Nextion reported invalid Waveform ID %d or Channel # %d was used!", + component->get_component_id(), component->get_wave_channel_id()); + + ESP_LOGN(TAG, "Removing waveform from queue with component id %d and waveform id %d", + component->get_component_id(), component->get_wave_channel_id()); + + found = index; + + delete component; + delete nb; + + break; + } + ++index; + } + + if (found != -1) { + this->nextion_queue_.erase(this->nextion_queue_.begin() + found); + } else { + ESP_LOGW( + TAG, + "Nextion reported invalid Waveform ID or Channel # was used but no waveform sensor in queue found!"); + } + } break; case 0x1A: // variable name invalid - ESP_LOGW(TAG, "Nextion reported variable name invalid!"); + this->remove_from_q_(); + break; case 0x1B: // variable operation invalid ESP_LOGW(TAG, "Nextion reported variable operation invalid!"); + this->remove_from_q_(); break; case 0x1C: // failed to assign ESP_LOGW(TAG, "Nextion reported failed to assign variable!"); + this->remove_from_q_(); break; case 0x1D: // operate EEPROM failed ESP_LOGW(TAG, "Nextion reported operating EEPROM failed!"); break; case 0x1E: // parameter quantity invalid ESP_LOGW(TAG, "Nextion reported parameter quantity invalid!"); + this->remove_from_q_(); break; case 0x1F: // IO operation failed ESP_LOGW(TAG, "Nextion reported component I/O operation invalid!"); break; case 0x20: // undefined escape characters ESP_LOGW(TAG, "Nextion reported undefined escape characters!"); + this->remove_from_q_(); break; case 0x23: // too long variable name ESP_LOGW(TAG, "Nextion reported too long variable name!"); + this->remove_from_q_(); + + break; + case 0x24: // Serial Buffer overflow occurs + ESP_LOGW(TAG, "Nextion reported Serial Buffer overflow!"); break; case 0x65: { // touch event return data - if (data_length != 3) { - invalid_data_length = true; + if (to_process_length != 3) { + ESP_LOGW(TAG, "Touch event data is expecting 3, received %zu", to_process_length); + break; } - uint8_t page_id = data[0]; - uint8_t component_id = data[1]; - uint8_t touch_event = data[2]; // 0 -> release, 1 -> press + uint8_t page_id = to_process[0]; + uint8_t component_id = to_process[1]; + uint8_t touch_event = to_process[2]; // 0 -> release, 1 -> press ESP_LOGD(TAG, "Got touch page=%u component=%u type=%s", page_id, component_id, touch_event ? "PRESS" : "RELEASE"); for (auto *touch : this->touch_) { - touch->process(page_id, component_id, touch_event); + touch->process_touch(page_id, component_id, touch_event != 0); } break; } - case 0x67: - case 0x68: { // touch coordinate data - if (data_length != 5) { - invalid_data_length = true; + case 0x67: { // Touch Coordinate (awake) + break; + } + case 0x68: { // touch coordinate data (sleep) + + if (to_process_length != 5) { + ESP_LOGW(TAG, "Touch coordinate data is expecting 5, received %zu", to_process_length); + ESP_LOGW(TAG, "%s", to_process.c_str()); break; } - uint16_t x = (uint16_t(data[0]) << 8) | data[1]; - uint16_t y = (uint16_t(data[2]) << 8) | data[3]; - uint8_t touch_event = data[4]; // 0 -> release, 1 -> press + + uint16_t x = (uint16_t(to_process[0]) << 8) | to_process[1]; + uint16_t y = (uint16_t(to_process[2]) << 8) | to_process[3]; + uint8_t touch_event = to_process[4]; // 0 -> release, 1 -> press ESP_LOGD(TAG, "Got touch at x=%u y=%u type=%s", x, y, touch_event ? "PRESS" : "RELEASE"); break; } - case 0x66: // sendme page id + case 0x66: { + break; + } // sendme page id + + // 0x70 0x61 0x62 0x31 0x32 0x33 0xFF 0xFF 0xFF + // Returned when using get command for a string. + // Each byte is converted to char. + // data: ab123 case 0x70: // string variable data return + { + if (this->nextion_queue_.empty()) { + ESP_LOGW(TAG, "ERROR: Received string return but the queue is empty"); + break; + } + + NextionQueue *nb = this->nextion_queue_.front(); + NextionComponentBase *component = nb->component; + + if (component->get_queue_type() != NextionQueueType::TEXT_SENSOR) { + ESP_LOGE(TAG, "ERROR: Received string return but next in queue \"%s\" is not a text sensor", + component->get_variable_name().c_str()); + } else { + ESP_LOGN(TAG, "Received get_string response: \"%s\" for component id: %s, type: %s", to_process.c_str(), + component->get_variable_name().c_str(), component->get_queue_type_string().c_str()); + component->set_state_from_string(to_process, true, false); + } + + delete nb; + this->nextion_queue_.pop_front(); + + break; + } + // 0x71 0x01 0x02 0x03 0x04 0xFF 0xFF 0xFF + // Returned when get command to return a number + // 4 byte 32-bit value in little endian order. + // (0x01+0x02*256+0x03*65536+0x04*16777216) + // data: 67305985 case 0x71: // numeric variable data return - case 0x86: // device automatically enters into sleep mode + { + if (this->nextion_queue_.empty()) { + ESP_LOGE(TAG, "ERROR: Received numeric return but the queue is empty"); + break; + } + + if (to_process_length == 0) { + ESP_LOGE(TAG, "ERROR: Received numeric return but no data!"); + break; + } + + int dataindex = 0; + + int value = 0; + + for (int i = 0; i < 4; ++i) { + value += to_process[i] << (8 * i); + ++dataindex; + } + + NextionQueue *nb = this->nextion_queue_.front(); + NextionComponentBase *component = nb->component; + + if (component->get_queue_type() != NextionQueueType::SENSOR && + component->get_queue_type() != NextionQueueType::BINARY_SENSOR && + component->get_queue_type() != NextionQueueType::SWITCH) { + ESP_LOGE(TAG, "ERROR: Received numeric return but next in queue \"%s\" is not a valid sensor type %d", + component->get_variable_name().c_str(), component->get_queue_type()); + } else { + ESP_LOGN(TAG, "Received numeric return for variable %s, queue type %d:%s, value %d", + component->get_variable_name().c_str(), component->get_queue_type(), + component->get_queue_type_string().c_str(), value); + component->set_state_from_int(value, true, false); + } + + delete nb; + this->nextion_queue_.pop_front(); + + break; + } + + case 0x86: { // device automatically enters into sleep mode + ESP_LOGVV(TAG, "Received Nextion entering sleep automatically"); + this->is_sleeping_ = true; + this->sleep_callback_.call(); + break; + } case 0x87: // device automatically wakes up + { + ESP_LOGVV(TAG, "Received Nextion leaves sleep automatically"); + this->is_sleeping_ = false; + this->wake_callback_.call(); + break; + } case 0x88: // system successful start up - case 0x89: // start SD card upgrade - case 0xFD: // data transparent transmit finished - case 0xFE: // data transparent transmit ready + { + ESP_LOGD(TAG, "system successful start up %zu", to_process_length); + this->nextion_reports_is_setup_ = true; break; + } + case 0x89: { // start SD card upgrade + break; + } + // Data from nextion is + // 0x90 - Start + // variable length of 0x70 return formatted data (bytes) that contain the variable name: prints "temp1",0 + // 00 - NULL + // 00/01 - Single byte for on/off + // FF FF FF - End + case 0x90: { // Switched component + std::string variable_name; + uint8_t index = 0; + + // Get variable name + index = to_process.find('\0'); + if (static_cast(index) == std::string::npos || (to_process_length - index - 1) < 1) { + ESP_LOGE(TAG, "Bad switch component data received for 0x90 event!"); + ESP_LOGN(TAG, "to_process %s %zu %d", to_process.c_str(), to_process_length, index); + break; + } + + variable_name = to_process.substr(0, index); + ++index; + + ESP_LOGN(TAG, "Got Switch variable_name=%s value=%d", variable_name.c_str(), to_process[0] != 0); + + for (auto *switchtype : this->switchtype_) { + switchtype->process_bool(variable_name, to_process[index] != 0); + } + break; + } + // Data from nextion is + // 0x91 - Start + // variable length of 0x70 return formatted data (bytes) that contain the variable name: prints "temp1",0 + // 00 - NULL + // variable length of 0x71 return data: prints temp1.val,0 + // FF FF FF - End + case 0x91: { // Sensor component + std::string variable_name; + uint8_t index = 0; + + index = to_process.find('\0'); + if (static_cast(index) == std::string::npos || (to_process_length - index - 1) != 4) { + ESP_LOGE(TAG, "Bad sensor component data received for 0x91 event!"); + ESP_LOGN(TAG, "to_process %s %zu %d", to_process.c_str(), to_process_length, index); + break; + } + + index = to_process.find('\0'); + variable_name = to_process.substr(0, index); + // // Get variable name + int value = 0; + for (int i = 0; i < 4; ++i) { + value += to_process[i + index + 1] << (8 * i); + } + + ESP_LOGN(TAG, "Got sensor variable_name=%s value=%d", variable_name.c_str(), value); + + for (auto *sensor : this->sensortype_) { + sensor->process_sensor(variable_name, value); + } + break; + } + + // Data from nextion is + // 0x92 - Start + // variable length of 0x70 return formatted data (bytes) that contain the variable name: prints "temp1",0 + // 00 - NULL + // variable length of 0x70 return formatted data (bytes) that contain the text prints temp1.txt,0 + // 00 - NULL + // FF FF FF - End + case 0x92: { // Text Sensor Component + std::string variable_name; + std::string text_value; + uint8_t index = 0; + + // Get variable name + index = to_process.find('\0'); + if (static_cast(index) == std::string::npos || (to_process_length - index - 1) < 1) { + ESP_LOGE(TAG, "Bad text sensor component data received for 0x92 event!"); + ESP_LOGN(TAG, "to_process %s %zu %d", to_process.c_str(), to_process_length, index); + break; + } + + variable_name = to_process.substr(0, index); + ++index; + + text_value = to_process.substr(index); + + ESP_LOGN(TAG, "Got Text Sensor variable_name=%s value=%s", variable_name.c_str(), text_value.c_str()); + + // NextionTextSensorResponseQueue *nq = new NextionTextSensorResponseQueue; + // nq->variable_name = variable_name; + // nq->state = text_value; + // this->textsensorq_.push_back(nq); + for (auto *textsensortype : this->textsensortype_) { + textsensortype->process_text(variable_name, text_value); + } + break; + } + // Data from nextion is + // 0x93 - Start + // variable length of 0x70 return formatted data (bytes) that contain the variable name: prints "temp1",0 + // 00 - NULL + // 00/01 - Single byte for on/off + // FF FF FF - End + case 0x93: { // Binary Sensor component + std::string variable_name; + uint8_t index = 0; + + // Get variable name + index = to_process.find('\0'); + if (static_cast(index) == std::string::npos || (to_process_length - index - 1) < 1) { + ESP_LOGE(TAG, "Bad binary sensor component data received for 0x92 event!"); + ESP_LOGN(TAG, "to_process %s %zu %d", to_process.c_str(), to_process_length, index); + break; + } + + variable_name = to_process.substr(0, index); + ++index; + + ESP_LOGN(TAG, "Got Binary Sensor variable_name=%s value=%d", variable_name.c_str(), to_process[index] != 0); + + for (auto *binarysensortype : this->binarysensortype_) { + binarysensortype->process_bool(&variable_name[0], to_process[index] != 0); + } + break; + } + case 0xFD: { // data transparent transmit finished + ESP_LOGVV(TAG, "Nextion reported data transmit finished!"); + break; + } + case 0xFE: { // data transparent transmit ready + ESP_LOGVV(TAG, "Nextion reported ready for transmit!"); + + int index = 0; + int found = -1; + for (auto &nb : this->nextion_queue_) { + auto component = nb->component; + if (component->get_queue_type() == NextionQueueType::WAVEFORM_SENSOR) { + size_t buffer_to_send = component->get_wave_buffer().size() < 255 ? component->get_wave_buffer().size() + : 255; // ADDT command can only send 255 + + this->write_array(component->get_wave_buffer().data(), static_cast(buffer_to_send)); + + ESP_LOGN(TAG, "Nextion sending waveform data for component id %d and waveform id %d, size %zu", + component->get_component_id(), component->get_wave_channel_id(), buffer_to_send); + + if (component->get_wave_buffer().size() <= 255) { + component->get_wave_buffer().clear(); + } else { + component->get_wave_buffer().erase(component->get_wave_buffer().begin(), + component->get_wave_buffer().begin() + buffer_to_send); + } + found = index; + delete component; + delete nb; + break; + } + ++index; + } + + if (found == -1) { + ESP_LOGE(TAG, "No waveforms in queue to send data!"); + break; + } else { + this->nextion_queue_.erase(this->nextion_queue_.begin() + found); + } + break; + } default: - ESP_LOGW(TAG, "Received unknown event from nextion: 0x%02X", event); + ESP_LOGW(TAG, "Received unknown event from nextion: 0x%02X", this->nextion_event_); break; } - if (invalid_data_length) { - ESP_LOGW(TAG, "Invalid data length from nextion!"); + + // ESP_LOGN(TAG, "nextion_event_ deleting from 0 to %d", to_process_length + COMMAND_DELIMITER.length() + 1); + this->command_data_.erase(0, to_process_length + COMMAND_DELIMITER.length() + 1); + // App.feed_wdt(); Remove before master merge + this->process_serial_(); + } + + uint32_t ms = millis(); + + if (!this->nextion_queue_.empty() && this->nextion_queue_.front()->queue_time + this->max_q_age_ms_ < ms) { + for (int i = 0; i < this->nextion_queue_.size(); i++) { + NextionComponentBase *component = this->nextion_queue_[i]->component; + if (this->nextion_queue_[i]->queue_time + this->max_q_age_ms_ < ms) { + if (this->nextion_queue_[i]->queue_time == 0) + ESP_LOGD(TAG, "Removing old queue type \"%s\" name \"%s\" queue_time 0", + component->get_queue_type_string().c_str(), component->get_variable_name().c_str()); + + if (component->get_variable_name() == "sleep_wake") { + this->is_sleeping_ = false; + } + + ESP_LOGD(TAG, "Removing old queue type \"%s\" name \"%s\"", component->get_queue_type_string().c_str(), + component->get_variable_name().c_str()); + + if (component->get_queue_type() == NextionQueueType::NO_RESULT) { + if (component->get_variable_name() == "sleep_wake") { + this->is_sleeping_ = false; + } + delete component; + } + + delete this->nextion_queue_[i]; + + this->nextion_queue_.erase(this->nextion_queue_.begin() + i); + + } else { + break; + } + } + } + ESP_LOGN(TAG, "Loop End"); + // App.feed_wdt(); Remove before master merge + this->process_serial_(); +} // namespace nextion + +void Nextion::set_nextion_sensor_state(int queue_type, const std::string &name, float state) { + this->set_nextion_sensor_state(static_cast(queue_type), name, state); +} + +void Nextion::set_nextion_sensor_state(NextionQueueType queue_type, const std::string &name, float state) { + ESP_LOGN(TAG, "Received state for variable %s, state %lf for queue type %d", name.c_str(), state, queue_type); + + switch (queue_type) { + case NextionQueueType::SENSOR: { + for (auto *sensor : this->sensortype_) { + if (name == sensor->get_variable_name()) { + sensor->set_state(state, true, true); + break; + } + } + break; + } + case NextionQueueType::BINARY_SENSOR: { + for (auto *sensor : this->binarysensortype_) { + if (name == sensor->get_variable_name()) { + sensor->set_state(state != 0, true, true); + break; + } + } + break; + } + case NextionQueueType::SWITCH: { + for (auto *sensor : this->switchtype_) { + if (name == sensor->get_variable_name()) { + sensor->set_state(state != 0, true, true); + break; + } + } + break; + } + default: { + ESP_LOGW(TAG, "set_nextion_sensor_state does not support a queue type %d", queue_type); + } + } +} + +void Nextion::set_nextion_text_state(const std::string &name, const std::string &state) { + ESP_LOGD(TAG, "Received state for variable %s, state %s", name.c_str(), state.c_str()); + + for (auto *sensor : this->textsensortype_) { + if (name == sensor->get_variable_name()) { + sensor->set_state(state, true, true); + break; + } + } +} + +void Nextion::all_components_send_state_(bool force_update) { + ESP_LOGD(TAG, "all_components_send_state_ "); + for (auto *binarysensortype : this->binarysensortype_) { + if (force_update || binarysensortype->get_needs_to_send_update()) + binarysensortype->send_state_to_nextion(); + } + for (auto *sensortype : this->sensortype_) { + if ((force_update || sensortype->get_needs_to_send_update()) && sensortype->get_wave_chan_id() == 0) + sensortype->send_state_to_nextion(); + } + for (auto *switchtype : this->switchtype_) { + if (force_update || switchtype->get_needs_to_send_update()) + switchtype->send_state_to_nextion(); + } + for (auto *textsensortype : this->textsensortype_) { + if (force_update || textsensortype->get_needs_to_send_update()) + textsensortype->send_state_to_nextion(); + } +} + +void Nextion::update_components_by_prefix(const std::string &prefix) { + for (auto *binarysensortype : this->binarysensortype_) { + if (binarysensortype->get_variable_name().find(prefix, 0) != std::string::npos) + binarysensortype->update_component_settings(true); + } + for (auto *sensortype : this->sensortype_) { + if (sensortype->get_variable_name().find(prefix, 0) != std::string::npos) + sensortype->update_component_settings(true); + } + for (auto *switchtype : this->switchtype_) { + if (switchtype->get_variable_name().find(prefix, 0) != std::string::npos) + switchtype->update_component_settings(true); + } + for (auto *textsensortype : this->textsensortype_) { + if (textsensortype->get_variable_name().find(prefix, 0) != std::string::npos) + textsensortype->update_component_settings(true); + } +} + +uint16_t Nextion::recv_ret_string_(std::string &response, uint32_t timeout, bool recv_flag) { + uint16_t ret = 0; + uint8_t c = 0; + uint8_t nr_of_ff_bytes = 0; + uint64_t start; + bool exit_flag = false; + bool ff_flag = false; + + start = millis(); + + while ((timeout == 0 && this->available()) || millis() - start <= timeout) { + this->read_byte(&c); + if (c == 0xFF) + nr_of_ff_bytes++; + else { + nr_of_ff_bytes = 0; + ff_flag = false; + } + + if (nr_of_ff_bytes >= 3) + ff_flag = true; + + response += (char) c; + if (recv_flag) { + if (response.find(0x05) != std::string::npos) { + exit_flag = true; + } + } + App.feed_wdt(); + delay(1); + + if (exit_flag || ff_flag) { + break; } } - return false; + if (ff_flag) + response = response.substr(0, response.length() - 3); // Remove last 3 0xFF + + ret = response.length(); + return ret; } -void Nextion::loop() { - while (this->available() >= 4) { - this->read_until_ack_(); + +/** + * @brief + * + * @param variable_name Name for the queue + */ +void Nextion::add_no_result_to_queue_(const std::string &variable_name) { + nextion::NextionQueue *nextion_queue = new nextion::NextionQueue; + + nextion_queue->component = new nextion::NextionComponentBase; + nextion_queue->component->set_variable_name(variable_name); + + nextion_queue->queue_time = millis(); + + this->nextion_queue_.push_back(nextion_queue); + + ESP_LOGN(TAG, "Add to queue type: NORESULT component %s", nextion_queue->component->get_variable_name().c_str()); +} + +/** + * @brief + * + * @param variable_name Variable name for the queue + * @param command + */ +void Nextion::add_no_result_to_queue_with_command_(const std::string &variable_name, const std::string &command) { + if ((!this->is_setup() && !this->ignore_is_setup_) || command.empty()) + return; + + if (this->send_command_(command)) { + this->add_no_result_to_queue_(variable_name); } } -#ifdef USE_TIME -void Nextion::set_nextion_rtc_time(time::ESPTime time) { - this->send_command_printf("rtc0=%u", time.year); - this->send_command_printf("rtc1=%u", time.month); - this->send_command_printf("rtc2=%u", time.day_of_month); - this->send_command_printf("rtc3=%u", time.hour); - this->send_command_printf("rtc4=%u", time.minute); - this->send_command_printf("rtc5=%u", time.second); -} -#endif -void Nextion::set_backlight_brightness(uint8_t brightness) { this->send_command_printf("dim=%u", brightness); } -void Nextion::set_touch_sleep_timeout(uint16_t timeout) { this->send_command_printf("thsp=%u", timeout); } +bool Nextion::add_no_result_to_queue_with_ignore_sleep_printf_(const std::string &variable_name, const char *format, + ...) { + if ((!this->is_setup() && !this->ignore_is_setup_)) + return false; -void Nextion::set_writer(const nextion_writer_t &writer) { this->writer_ = writer; } -void Nextion::set_component_text_printf(const char *component, const char *format, ...) { + char buffer[256]; va_list arg; va_start(arg, format); - char buffer[256]; int ret = vsnprintf(buffer, sizeof(buffer), format, arg); va_end(arg); - if (ret > 0) - this->set_component_text(component, buffer); -} -void Nextion::set_wait_for_ack(bool wait_for_ack) { this->wait_for_ack_ = wait_for_ack; } + if (ret <= 0) { + ESP_LOGW(TAG, "Building command for format '%s' failed!", format); + return false; + } -void NextionTouchComponent::process(uint8_t page_id, uint8_t component_id, bool on) { - if (this->page_id_ == page_id && this->component_id_ == component_id) { - this->publish_state(on); + this->add_no_result_to_queue_with_command_(variable_name, buffer); + return true; +} + +/** + * @brief Sends a formatted command to the nextion + * + * @param variable_name Variable name for the queue + * @param format The printf-style command format, like "vis %s,0" + * @param ... The format arguments + */ +bool Nextion::add_no_result_to_queue_with_printf_(const std::string &variable_name, const char *format, ...) { + if ((!this->is_setup() && !this->ignore_is_setup_) || this->is_sleeping()) + return false; + + char buffer[256]; + va_list arg; + va_start(arg, format); + int ret = vsnprintf(buffer, sizeof(buffer), format, arg); + va_end(arg); + if (ret <= 0) { + ESP_LOGW(TAG, "Building command for format '%s' failed!", format); + return false; + } + + this->add_no_result_to_queue_with_command_(variable_name, buffer); + return true; +} + +/** + * @brief + * + * @param variable_name Variable name for the queue + * @param variable_name_to_send Variable name for the left of the command + * @param state_value Value to set + * @param is_sleep_safe The command is safe to send when the Nextion is sleeping + */ + +void Nextion::add_no_result_to_queue_with_set(NextionComponentBase *component, int state_value) { + this->add_no_result_to_queue_with_set(component->get_variable_name(), component->get_variable_name_to_send(), + state_value); +} + +void Nextion::add_no_result_to_queue_with_set(const std::string &variable_name, + const std::string &variable_name_to_send, int state_value) { + this->add_no_result_to_queue_with_set_internal_(variable_name, variable_name_to_send, state_value); +} + +void Nextion::add_no_result_to_queue_with_set_internal_(const std::string &variable_name, + const std::string &variable_name_to_send, int state_value, + bool is_sleep_safe) { + if ((!this->is_setup() && !this->ignore_is_setup_) || (!is_sleep_safe && this->is_sleeping())) + return; + + this->add_no_result_to_queue_with_ignore_sleep_printf_(variable_name, "%s=%d", variable_name_to_send.c_str(), + state_value); +} + +/** + * @brief + * + * @param variable_name Variable name for the queue + * @param variable_name_to_send Variable name for the left of the command + * @param state_value Sting value to set + * @param is_sleep_safe The command is safe to send when the Nextion is sleeping + */ +void Nextion::add_no_result_to_queue_with_set(NextionComponentBase *component, const std::string &state_value) { + this->add_no_result_to_queue_with_set(component->get_variable_name(), component->get_variable_name_to_send(), + state_value); +} +void Nextion::add_no_result_to_queue_with_set(const std::string &variable_name, + const std::string &variable_name_to_send, + const std::string &state_value) { + this->add_no_result_to_queue_with_set_internal_(variable_name, variable_name_to_send, state_value); +} + +void Nextion::add_no_result_to_queue_with_set_internal_(const std::string &variable_name, + const std::string &variable_name_to_send, + const std::string &state_value, bool is_sleep_safe) { + if ((!this->is_setup() && !this->ignore_is_setup_) || (!is_sleep_safe && this->is_sleeping())) + return; + + this->add_no_result_to_queue_with_printf_(variable_name, "%s=\"%s\"", variable_name_to_send.c_str(), + state_value.c_str()); +} + +void Nextion::add_to_get_queue(NextionComponentBase *component) { + if ((!this->is_setup() && !this->ignore_is_setup_)) + return; + + nextion::NextionQueue *nextion_queue = new nextion::NextionQueue; + + nextion_queue->component = component; + nextion_queue->queue_time = millis(); + + ESP_LOGN(TAG, "Add to queue type: %s component %s", component->get_queue_type_string().c_str(), + component->get_variable_name().c_str()); + + std::string command = "get " + component->get_variable_name_to_send(); + + if (this->send_command_(command)) { + this->nextion_queue_.push_back(nextion_queue); } } +/** + * @brief Add addt command to the queue + * + * @param component_id The waveform component id + * @param wave_chan_id The waveform channel to send it to + * @param buffer_to_send The buffer size + * @param buffer_size The buffer data + */ +void Nextion::add_addt_command_to_queue(NextionComponentBase *component) { + if ((!this->is_setup() && !this->ignore_is_setup_) || this->is_sleeping()) + return; + + nextion::NextionQueue *nextion_queue = new nextion::NextionQueue; + + nextion_queue->component = new nextion::NextionComponentBase; + nextion_queue->queue_time = millis(); + + size_t buffer_to_send = component->get_wave_buffer_size() < 255 ? component->get_wave_buffer_size() + : 255; // ADDT command can only send 255 + + std::string command = "addt " + to_string(component->get_component_id()) + "," + + to_string(component->get_wave_channel_id()) + "," + to_string(buffer_to_send); + if (this->send_command_(command)) { + this->nextion_queue_.push_back(nextion_queue); + } +} + +void Nextion::set_writer(const nextion_writer_t &writer) { this->writer_ = writer; } + +ESPDEPRECATED("set_wait_for_ack(bool) is deprecated and has no effect") +void Nextion::set_wait_for_ack(bool wait_for_ack) { ESP_LOGE(TAG, "This command is depreciated"); } + } // namespace nextion } // namespace esphome diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index a55ff747ee..2389cc6235 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -1,9 +1,21 @@ #pragma once -#include "esphome/core/component.h" +#include #include "esphome/core/defines.h" #include "esphome/components/uart/uart.h" -#include "esphome/components/binary_sensor/binary_sensor.h" +#include "nextion_base.h" +#include "nextion_component.h" +#include "esphome/components/display/display_color_utils.h" + +#if defined(USE_ETHERNET) || defined(USE_WIFI) +#ifdef ARDUINO_ARCH_ESP32 +#include +#endif +#ifdef ARDUINO_ARCH_ESP8266 +#include +#include +#endif +#endif #ifdef USE_TIME #include "esphome/components/time/real_time_clock.h" @@ -12,12 +24,14 @@ namespace esphome { namespace nextion { -class NextionTouchComponent; class Nextion; +class NextionComponentBase; using nextion_writer_t = std::function; -class Nextion : public PollingComponent, public uart::UARTDevice { +static const std::string COMMAND_DELIMITER{static_cast(255), static_cast(255), static_cast(255)}; + +class Nextion : public NextionBase, public PollingComponent, public uart::UARTDevice { public: /** * Set the text of a component to a static string. @@ -73,9 +87,20 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * * This will change the image of the component `pic` to the image with ID `4`. */ - void set_component_picture(const char *component, const char *picture) { - this->send_command_printf("%s.val=%s", component, picture); - } + void set_component_picture(const char *component, const char *picture); + /** + * Set the background color of a component. + * @param component The component name. + * @param color The color (as a uint32_t). + * + * Example: + * ```cpp + * it.set_component_background_color("button", 0xFF0000); + * ``` + * + * This will change the background color of the component `button` to red. + */ + void set_component_background_color(const char *component, uint32_t color); /** * Set the background color of a component. * @param component The component name. @@ -83,7 +108,7 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * * Example: * ```cpp - * it.set_component_background_color("button", "17013"); + * it.set_component_background_color("button", "RED"); * ``` * * This will change the background color of the component `button` to blue. @@ -91,6 +116,33 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * Nextion HMI colors. */ void set_component_background_color(const char *component, const char *color); + /** + * Set the background color of a component. + * @param component The component name. + * @param color The color (as Color). + * + * Example: + * ```cpp + * it.set_component_background_color("button", color); + * ``` + * + * This will change the background color of the component `button` to what color contains. + */ + void set_component_background_color(const char *component, Color color) override; + /** + * Set the pressed background color of a component. + * @param component The component name. + * @param color The color (as a int). + * + * Example: + * ```cpp + * it.set_component_pressed_background_color("button", 0xFF0000 ); + * ``` + * + * This will change the pressed background color of the component `button` to red. This is the background color that + * is shown when the component is pressed. + */ + void set_component_pressed_background_color(const char *component, uint32_t color); /** * Set the pressed background color of a component. * @param component The component name. @@ -98,7 +150,7 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * * Example: * ```cpp - * it.set_component_pressed_background_color("button", "17013"); + * it.set_component_pressed_background_color("button", "RED"); * ``` * * This will change the pressed background color of the component `button` to blue. This is the background color that @@ -107,6 +159,63 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * colors. */ void set_component_pressed_background_color(const char *component, const char *color); + /** + * Set the pressed background color of a component. + * @param component The component name. + * @param color The color (as Color). + * + * Example: + * ```cpp + * it.set_component_pressed_background_color("button", color); + * ``` + * + * This will change the pressed background color of the component `button` to blue. This is the background color that + * is shown when the component is pressed. Use this [color + * picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI + * colors. + */ + void set_component_pressed_background_color(const char *component, Color color) override; + + /** + * Set the picture id of a component. + * @param component The component name. + * @param pic_id The picture ID. + * + * Example: + * ```cpp + * it.set_component_pic("textview", 1); + * ``` + * + * This will change the picture id of the component `textview`. + */ + void set_component_pic(const char *component, uint8_t pic_id); + /** + * Set the background picture id of component. + * @param component The component name. + * @param pic_id The picture ID. + * + * Example: + * ```cpp + * it.set_component_picc("textview", 1); + * ``` + * + * This will change the background picture id of the component `textview`. + */ + void set_component_picc(const char *component, uint8_t pic_id); + + /** + * Set the font color of a component. + * @param component The component name. + * @param color The color (as a uint32_t ). + * + * Example: + * ```cpp + * it.set_component_font_color("textview", 0xFF0000); + * ``` + * + * This will change the font color of the component `textview` to a red color. + */ + void set_component_font_color(const char *component, uint32_t color); /** * Set the font color of a component. * @param component The component name. @@ -114,7 +223,7 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * * Example: * ```cpp - * it.set_component_font_color("textview", "17013"); + * it.set_component_font_color("textview", "RED"); * ``` * * This will change the font color of the component `textview` to a blue color. @@ -122,6 +231,34 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * Nextion HMI colors. */ void set_component_font_color(const char *component, const char *color); + /** + * Set the font color of a component. + * @param component The component name. + * @param color The color (as Color). + * + * Example: + * ```cpp + * it.set_component_font_color("textview", color); + * ``` + * + * This will change the font color of the component `textview` to a blue color. + * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to + * Nextion HMI colors. + */ + void set_component_font_color(const char *component, Color color) override; + /** + * Set the pressed font color of a component. + * @param component The component name. + * @param color The color (as a uint32_t). + * + * Example: + * ```cpp + * it.set_component_pressed_font_color("button", 0xFF0000); + * ``` + * + * This will change the pressed font color of the component `button` to a red. + */ + void set_component_pressed_font_color(const char *component, uint32_t color); /** * Set the pressed font color of a component. * @param component The component name. @@ -129,7 +266,7 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * * Example: * ```cpp - * it.set_component_pressed_font_color("button", "17013"); + * it.set_component_pressed_font_color("button", "RED"); * ``` * * This will change the pressed font color of the component `button` to a blue color. @@ -137,6 +274,21 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * Nextion HMI colors. */ void set_component_pressed_font_color(const char *component, const char *color); + /** + * Set the pressed font color of a component. + * @param component The component name. + * @param color The color (as Color). + * + * Example: + * ```cpp + * it.set_component_pressed_font_color("button", color); + * ``` + * + * This will change the pressed font color of the component `button` to a blue color. + * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to + * Nextion HMI colors. + */ + void set_component_pressed_font_color(const char *component, Color color) override; /** * Set the coordinates of a component on screen. * @param component The component name. @@ -163,7 +315,7 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * * Changes the font of the component named `textveiw`. Font IDs are set in the Nextion Editor. */ - void set_component_font(const char *component, uint8_t font_id); + void set_component_font(const char *component, uint8_t font_id) override; #ifdef USE_TIME /** * Send the current time to the nextion display. @@ -195,7 +347,7 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * * Hides the component named `button`. */ - void hide_component(const char *component); + void hide_component(const char *component) override; /** * Show a component. * @param component The component name. @@ -207,7 +359,7 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * * Shows the component named `button`. */ - void show_component(const char *component); + void show_component(const char *component) override; /** * Enable touch for a component. * @param component The component name. @@ -239,6 +391,7 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * @param value The value to write. */ void add_waveform_data(int component_id, uint8_t channel_number, uint8_t value); + void open_waveform_channel(int component_id, uint8_t channel_number, uint8_t value); /** * Display a picture at coordinates. * @param picture_id The picture id. @@ -263,7 +416,7 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * * Example: * ```cpp - * fill_area(50, 50, 100, 100, "17013"); + * fill_area(50, 50, 100, 100, "RED"); * ``` * * Fills an area that starts at x coordiante `50` and y coordinate `50` with a height of `100` and width of `100` with @@ -271,6 +424,24 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * convert color codes to Nextion HMI colors */ void fill_area(int x1, int y1, int width, int height, const char *color); + /** + * Fill a rectangle with a color. + * @param x1 The starting x coordinate. + * @param y1 The starting y coordinate. + * @param width The width to draw. + * @param height The height to draw. + * @param color The color to draw with (as Color). + * + * Example: + * ```cpp + * fill_area(50, 50, 100, 100, color); + * ``` + * + * Fills an area that starts at x coordiante `50` and y coordinate `50` with a height of `100` and width of `100` with + * the color of blue. Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to + * convert color codes to Nextion HMI colors + */ + void fill_area(int x1, int y1, int width, int height, Color color); /** * Draw a line on the screen. * @param x1 The starting x coordinate. @@ -290,6 +461,25 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * colors. */ void line(int x1, int y1, int x2, int y2, const char *color); + /** + * Draw a line on the screen. + * @param x1 The starting x coordinate. + * @param y1 The starting y coordinate. + * @param x2 The ending x coordinate. + * @param y2 The ending y coordinate. + * @param color The color to draw with (as Color). + * + * Example: + * ```cpp + * it.line(50, 50, 75, 75, "17013"); + * ``` + * + * Makes a line that starts at x coordinate `50` and y coordinate `50` and ends at x coordinate `75` and y coordinate + * `75` with the color of blue. Use this [color + * picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI + * colors. + */ + void line(int x1, int y1, int x2, int y2, Color color); /** * Draw a rectangle outline. * @param x1 The starting x coordinate. @@ -309,6 +499,25 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * colors. */ void rectangle(int x1, int y1, int width, int height, const char *color); + /** + * Draw a rectangle outline. + * @param x1 The starting x coordinate. + * @param y1 The starting y coordinate. + * @param width The width of the rectangle. + * @param height The height of the rectangle. + * @param color The color to draw with (as Color). + * + * Example: + * ```cpp + * it.rectangle(25, 35, 40, 50, "17013"); + * ``` + * + * Makes a outline of a rectangle that starts at x coordinate `25` and y coordinate `35` and has a width of `40` and a + * length of `50` with color of blue. Use this [color + * picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI + * colors. + */ + void rectangle(int x1, int y1, int width, int height, Color color); /** * Draw a circle outline * @param center_x The center x coordinate. @@ -317,6 +526,14 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * @param color The color to draw with (as a string). */ void circle(int center_x, int center_y, int radius, const char *color); + /** + * Draw a circle outline + * @param center_x The center x coordinate. + * @param center_y The center y coordinate. + * @param radius The circle radius. + * @param color The color to draw with (as Color). + */ + void circle(int center_x, int center_y, int radius, Color color); /** * Draw a filled circled. * @param center_x The center x coordinate. @@ -334,19 +551,36 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * Nextion HMI colors. */ void filled_circle(int center_x, int center_y, int radius, const char *color); - - /** Set the brightness of the backlight. - * - * @param brightness The brightness, from 0 to 100. + /** + * Draw a filled circled. + * @param center_x The center x coordinate. + * @param center_y The center y coordinate. + * @param radius The circle radius. + * @param color The color to draw with (as Color). * * Example: * ```cpp - * it.set_backlight_brightness(30); + * it.filled_cricle(25, 25, 10, color); + * ``` + * + * Makes a filled circle at the x cordinates `25` and y coordinate `25` with a radius of `10` with a color of blue. + * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to + * Nextion HMI colors. + */ + void filled_circle(int center_x, int center_y, int radius, Color color); + + /** Set the brightness of the backlight. + * + * @param brightness The brightness percentage from 0 to 1.0. + * + * Example: + * ```cpp + * it.set_backlight_brightness(.3); * ``` * * Changes the brightness of the display to 30%. */ - void set_backlight_brightness(uint8_t brightness); + void set_backlight_brightness(float brightness); /** * Set the touch sleep timeout of the display. * @param timeout Timeout in seconds. @@ -360,10 +594,46 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * `thup`. */ void set_touch_sleep_timeout(uint16_t timeout); + /** + * Sets which page Nextion loads when exiting sleep mode. Note this can be set even when Nextion is in sleep mode. + * @param page_id The page id, from 0 to the lage page in Nextion. Set 255 (not set to any existing page) to + * wakes up to current page. + * + * Example: + * ```cpp + * it.set_wake_up_page(2); + * ``` + * + * The display will wake up to page 2. + */ + void set_wake_up_page(uint8_t page_id = 255); + /** + * Sets if Nextion should auto-wake from sleep when touch press occurs. + * @param auto_wake True or false. When auto_wake is true and Nextion is in sleep mode, + * the first touch will only trigger the auto wake mode and not trigger a Touch Event. + * + * Example: + * ```cpp + * it.set_auto_wake_on_touch(true); + * ``` + * + * The display will wake up by touch. + */ + void set_auto_wake_on_touch(bool auto_wake); + /** + * Sets Nextion mode between sleep and awake + * @param True or false. Sleep=true to enter sleep mode or sleep=false to exit sleep mode. + */ + void sleep(bool sleep); // ========== INTERNAL METHODS ========== // (In most use cases you won't need these) - void register_touch_component(NextionTouchComponent *obj) { this->touch_.push_back(obj); } + void register_touch_component(NextionComponentBase *obj) { this->touch_.push_back(obj); } + void register_switch_component(NextionComponentBase *obj) { this->switchtype_.push_back(obj); } + void register_binarysensor_component(NextionComponentBase *obj) { this->binarysensortype_.push_back(obj); } + void register_sensor_component(NextionComponentBase *obj) { this->sensortype_.push_back(obj); } + void register_textsensor_component(NextionComponentBase *obj) { this->textsensortype_.push_back(obj); } + void setup() override; void set_brightness(float brightness) { this->brightness_ = brightness; } float get_setup_priority() const override; @@ -371,11 +641,9 @@ class Nextion : public PollingComponent, public uart::UARTDevice { void loop() override; void set_writer(const nextion_writer_t &writer); - /** - * Manually send a raw command to the display and don't wait for an acknowledgement packet. - * @param command The command to write, for example "vis b0,0". - */ - void send_command_no_ack(const char *command); + // This function has been deprecated + void set_wait_for_ack(bool wait_for_ack); + /** * Manually send a raw formatted command to the display. * @param format The printf-style command format, like "vis %s,0" @@ -384,28 +652,199 @@ class Nextion : public PollingComponent, public uart::UARTDevice { */ bool send_command_printf(const char *format, ...) __attribute__((format(printf, 2, 3))); - void set_wait_for_ack(bool wait_for_ack); +#ifdef USE_TFT_UPLOAD + /** + * Set the tft file URL. https seems problamtic with arduino.. + */ + void set_tft_url(const std::string &tft_url) { this->tft_url_ = tft_url; } + +#endif + + /** + * Upload the tft file and softreset the Nextion + */ + void upload_tft(); + void dump_config() override; + + /** + * Softreset the Nextion + */ + void soft_reset(); + + /** Add a callback to be notified of sleep state changes. + * + * @param callback The void() callback. + */ + void add_sleep_state_callback(std::function &&callback); + + /** Add a callback to be notified of wake state changes. + * + * @param callback The void() callback. + */ + void add_wake_state_callback(std::function &&callback); + + /** Add a callback to be notified when the nextion completes its initialize setup. + * + * @param callback The void() callback. + */ + void add_setup_state_callback(std::function &&callback); + + void update_all_components(); + + /** + * @brief Set the nextion sensor state object. + * + * @param[in] queue_type + * Index of NextionQueueType. + * + * @param[in] name + * Component/variable name. + * + * @param[in] state + * State to set. + */ + void set_nextion_sensor_state(int queue_type, const std::string &name, float state); + void set_nextion_sensor_state(NextionQueueType queue_type, const std::string &name, float state); + void set_nextion_text_state(const std::string &name, const std::string &state); + + void add_no_result_to_queue_with_set(NextionComponentBase *component, int state_value) override; + void add_no_result_to_queue_with_set(const std::string &variable_name, const std::string &variable_name_to_send, + int state_value) override; + + void add_no_result_to_queue_with_set(NextionComponentBase *component, const std::string &state_value) override; + void add_no_result_to_queue_with_set(const std::string &variable_name, const std::string &variable_name_to_send, + const std::string &state_value) override; + + void add_to_get_queue(NextionComponentBase *component) override; + + void add_addt_command_to_queue(NextionComponentBase *component) override; + + void update_components_by_prefix(const std::string &prefix); + + void set_touch_sleep_timeout_internal(uint32_t touch_sleep_timeout) { + this->touch_sleep_timeout_ = touch_sleep_timeout; + } + void set_wake_up_page_internal(uint8_t wake_up_page) { this->wake_up_page_ = wake_up_page; } + void set_auto_wake_on_touch_internal(bool auto_wake_on_touch) { this->auto_wake_on_touch_ = auto_wake_on_touch; } protected: - bool ack_(); - bool read_until_ack_(); + std::deque nextion_queue_; + uint16_t recv_ret_string_(std::string &response, uint32_t timeout, bool recv_flag); + void all_components_send_state_(bool force_update = false); + uint64_t comok_sent_ = 0; + bool remove_from_q_(bool report_empty = true); + /** + * @brief + * Sends commands ignoring of the Nextion has been setup. + */ + bool ignore_is_setup_ = false; + bool nextion_reports_is_setup_ = false; + uint8_t nextion_event_; + + void process_nextion_commands_(); + void process_serial_(); + bool is_updating_ = false; + uint32_t touch_sleep_timeout_ = 0; + int wake_up_page_ = -1; + bool auto_wake_on_touch_ = true; + + /** + * Manually send a raw command to the display and don't wait for an acknowledgement packet. + * @param command The command to write, for example "vis b0,0". + */ + bool send_command_(const std::string &command); + void add_no_result_to_queue_(const std::string &variable_name); + bool add_no_result_to_queue_with_ignore_sleep_printf_(const std::string &variable_name, const char *format, ...) + __attribute__((format(printf, 3, 4))); + void add_no_result_to_queue_with_command_(const std::string &variable_name, const std::string &command); + + bool add_no_result_to_queue_with_printf_(const std::string &variable_name, const char *format, ...) + __attribute__((format(printf, 3, 4))); + + void add_no_result_to_queue_with_set_internal_(const std::string &variable_name, + const std::string &variable_name_to_send, int state_value, + bool is_sleep_safe = false); + + void add_no_result_to_queue_with_set_internal_(const std::string &variable_name, + const std::string &variable_name_to_send, + const std::string &state_value, bool is_sleep_safe = false); + +#ifdef USE_TFT_UPLOAD +#if defined(USE_ETHERNET) || defined(USE_WIFI) +#ifdef ARDUINO_ARCH_ESP8266 + WiFiClient *wifi_client_{nullptr}; + BearSSL::WiFiClientSecure *wifi_client_secure_{nullptr}; + WiFiClient *get_wifi_client_(); +#endif + + /** + * will request chunk_size chunks from the web server + * and send each to the nextion + * @param int contentLength Total size of the file + * @param uint32_t chunk_size + * @return true if success, false for failure. + */ + int content_length_ = 0; + int tft_size_ = 0; + int upload_by_chunks_(HTTPClient *http, int range_start); + + bool upload_with_range_(uint32_t range_start, uint32_t range_end); + + /** + * start update tft file to nextion. + * + * @param const uint8_t *file_buf + * @param size_t buf_size + * @return true if success, false for failure. + */ + bool upload_from_buffer_(const uint8_t *file_buf, size_t buf_size); + void upload_end_(); + +#endif + +#endif + + bool get_is_connected_() { return this->is_connected_; } + + bool check_connect_(); + + std::vector touch_; + std::vector switchtype_; + std::vector sensortype_; + std::vector textsensortype_; + std::vector binarysensortype_; + CallbackManager setup_callback_{}; + CallbackManager sleep_callback_{}; + CallbackManager wake_callback_{}; - std::vector touch_; optional writer_; - bool wait_for_ack_{true}; float brightness_{1.0}; + + std::string device_model_; + std::string firmware_version_; + std::string serial_number_; + std::string flash_size_; + + void remove_front_no_sensors_(); + +#ifdef USE_TFT_UPLOAD + std::string tft_url_; + uint8_t *transfer_buffer_{nullptr}; + size_t transfer_buffer_size_; + bool upload_first_chunk_sent_ = false; +#endif + +#ifdef NEXTION_PROTOCOL_LOG + void print_queue_members_(); +#endif + void reset_(bool reset_nextion = true); + + std::string command_data_; + bool is_connected_ = false; + uint32_t startup_override_ms_ = 8000; + uint32_t max_q_age_ms_ = 8000; + uint32_t started_ms_ = 0; + bool sent_setup_commands_ = false; }; - -class NextionTouchComponent : public binary_sensor::BinarySensorInitiallyOff { - public: - void set_page_id(uint8_t page_id) { page_id_ = page_id; } - void set_component_id(uint8_t component_id) { component_id_ = component_id; } - void process(uint8_t page_id, uint8_t component_id, bool on); - - protected: - uint8_t page_id_; - uint8_t component_id_; -}; - } // namespace nextion } // namespace esphome diff --git a/esphome/components/nextion/nextion_base.h b/esphome/components/nextion/nextion_base.h new file mode 100644 index 0000000000..a24fd74060 --- /dev/null +++ b/esphome/components/nextion/nextion_base.h @@ -0,0 +1,58 @@ +#pragma once +#include "esphome/core/defines.h" +#include "esphome/core/color.h" +#include "nextion_component_base.h" +namespace esphome { +namespace nextion { + +#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE +#define NEXTION_PROTOCOL_LOG +#endif + +#ifdef NEXTION_PROTOCOL_LOG +#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE +#define ESP_LOGN(tag, ...) ESP_LOGVV(tag, __VA_ARGS__) +#else +#define ESP_LOGN(tag, ...) ESP_LOGD(tag, __VA_ARGS__) +#endif +#else +#define ESP_LOGN(tag, ...) \ + {} +#endif + +class NextionBase; + +class NextionBase { + public: + virtual void add_no_result_to_queue_with_set(NextionComponentBase *component, int state_value) = 0; + virtual void add_no_result_to_queue_with_set(const std::string &variable_name, + const std::string &variable_name_to_send, int state_value) = 0; + + virtual void add_no_result_to_queue_with_set(NextionComponentBase *component, const std::string &state_value) = 0; + virtual void add_no_result_to_queue_with_set(const std::string &variable_name, + const std::string &variable_name_to_send, + const std::string &state_value) = 0; + + virtual void add_addt_command_to_queue(NextionComponentBase *component) = 0; + + virtual void add_to_get_queue(NextionComponentBase *component) = 0; + + virtual void set_component_background_color(const char *component, Color color) = 0; + virtual void set_component_pressed_background_color(const char *component, Color color) = 0; + virtual void set_component_font_color(const char *component, Color color) = 0; + virtual void set_component_pressed_font_color(const char *component, Color color) = 0; + virtual void set_component_font(const char *component, uint8_t font_id) = 0; + + virtual void show_component(const char *component) = 0; + virtual void hide_component(const char *component) = 0; + + bool is_sleeping() { return this->is_sleeping_; } + bool is_setup() { return this->is_setup_; } + + protected: + bool is_setup_ = false; + bool is_sleeping_ = false; +}; + +} // namespace nextion +} // namespace esphome diff --git a/esphome/components/nextion/nextion_commands.cpp b/esphome/components/nextion/nextion_commands.cpp new file mode 100644 index 0000000000..931b934ba2 --- /dev/null +++ b/esphome/components/nextion/nextion_commands.cpp @@ -0,0 +1,234 @@ +#include "nextion.h" +#include "esphome/core/util.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace nextion { +static const char *const TAG = "nextion"; + +// Sleep safe commands +void Nextion::soft_reset() { this->send_command_("rest"); } + +void Nextion::set_wake_up_page(uint8_t page_id) { + if (page_id > 255) { + ESP_LOGD(TAG, "Wake up page of bounds, range 0-255"); + return; + } + this->add_no_result_to_queue_with_set_internal_("wake_up_page", "wup", page_id, true); +} + +void Nextion::set_touch_sleep_timeout(uint16_t timeout) { + if (timeout < 3 || timeout > 65535) { + ESP_LOGD(TAG, "Sleep timeout out of bounds, range 3-65535"); + return; + } + + this->add_no_result_to_queue_with_set_internal_("touch_sleep_timeout", "thsp", timeout, true); +} + +void Nextion::sleep(bool sleep) { + if (sleep) { // Set sleep + this->is_sleeping_ = true; + this->add_no_result_to_queue_with_set_internal_("sleep", "sleep", 1, true); + } else { // Turn off sleep. Wait for a sleep_wake return before setting sleep off + this->add_no_result_to_queue_with_set_internal_("sleep_wake", "sleep", 0, true); + } +} +// End sleep safe commands + +// Set Colors +void Nextion::set_component_background_color(const char *component, uint32_t color) { + this->add_no_result_to_queue_with_printf_("set_component_background_color", "%s.bco=%d", component, color); +} + +void Nextion::set_component_background_color(const char *component, const char *color) { + this->add_no_result_to_queue_with_printf_("set_component_background_color", "%s.bco=%s", component, color); +} + +void Nextion::set_component_background_color(const char *component, Color color) { + this->add_no_result_to_queue_with_printf_("set_component_background_color", "%s.bco=%d", component, + display::ColorUtil::color_to_565(color)); +} + +void Nextion::set_component_pressed_background_color(const char *component, uint32_t color) { + this->add_no_result_to_queue_with_printf_("set_component_pressed_background_color", "%s.bco2=%d", component, color); +} + +void Nextion::set_component_pressed_background_color(const char *component, const char *color) { + this->add_no_result_to_queue_with_printf_("set_component_pressed_background_color", "%s.bco2=%s", component, color); +} + +void Nextion::set_component_pressed_background_color(const char *component, Color color) { + this->add_no_result_to_queue_with_printf_("set_component_pressed_background_color", "%s.bco2=%d", component, + display::ColorUtil::color_to_565(color)); +} + +void Nextion::set_component_pic(const char *component, uint8_t pic_id) { + this->add_no_result_to_queue_with_printf_("set_component_pic", "%s.pic=%d", component, pic_id); +} + +void Nextion::set_component_picc(const char *component, uint8_t pic_id) { + this->add_no_result_to_queue_with_printf_("set_component_pic", "%s.picc=%d", component, pic_id); +} + +void Nextion::set_component_font_color(const char *component, uint32_t color) { + this->add_no_result_to_queue_with_printf_("set_component_font_color", "%s.pco=%d", component, color); +} + +void Nextion::set_component_font_color(const char *component, const char *color) { + this->add_no_result_to_queue_with_printf_("set_component_font_color", "%s.pco=%s", component, color); +} + +void Nextion::set_component_font_color(const char *component, Color color) { + this->add_no_result_to_queue_with_printf_("set_component_font_color", "%s.pco=%d", component, + display::ColorUtil::color_to_565(color)); +} + +void Nextion::set_component_pressed_font_color(const char *component, uint32_t color) { + this->add_no_result_to_queue_with_printf_("set_component_pressed_font_color", "%s.pco2=%d", component, color); +} + +void Nextion::set_component_pressed_font_color(const char *component, const char *color) { + this->add_no_result_to_queue_with_printf_("set_component_pressed_font_color", " %s.pco2=%s", component, color); +} + +void Nextion::set_component_pressed_font_color(const char *component, Color color) { + this->add_no_result_to_queue_with_printf_("set_component_pressed_font_color", "%s.pco2=%d", component, + display::ColorUtil::color_to_565(color)); +} + +void Nextion::set_component_text_printf(const char *component, const char *format, ...) { + va_list arg; + va_start(arg, format); + char buffer[256]; + int ret = vsnprintf(buffer, sizeof(buffer), format, arg); + va_end(arg); + if (ret > 0) + this->set_component_text(component, buffer); +} + +// General Nextion +void Nextion::goto_page(const char *page) { this->add_no_result_to_queue_with_printf_("goto_page", "page %s", page); } + +void Nextion::set_backlight_brightness(float brightness) { + if (brightness < 0 || brightness > 1.0) { + ESP_LOGD(TAG, "Brightness out of bounds, percentage range 0-1.0"); + return; + } + this->add_no_result_to_queue_with_set("backlight_brightness", "dim", static_cast(brightness * 100)); +} + +void Nextion::set_auto_wake_on_touch(bool auto_wake) { + this->add_no_result_to_queue_with_set("auto_wake_on_touch", "thup", auto_wake ? 1 : 0); +} + +// General Component +void Nextion::set_component_font(const char *component, uint8_t font_id) { + this->add_no_result_to_queue_with_printf_("set_component_font", "%s.font=%d", component, font_id); +} + +void Nextion::hide_component(const char *component) { + this->add_no_result_to_queue_with_printf_("hide_component", "vis %s,0", component); +} + +void Nextion::show_component(const char *component) { + this->add_no_result_to_queue_with_printf_("show_component", "vis %s,1", component); +} + +void Nextion::enable_component_touch(const char *component) { + this->add_no_result_to_queue_with_printf_("enable_component_touch", "tsw %s,1", component); +} + +void Nextion::disable_component_touch(const char *component) { + this->add_no_result_to_queue_with_printf_("disable_component_touch", "tsw %s,0", component); +} + +void Nextion::set_component_picture(const char *component, const char *picture) { + this->add_no_result_to_queue_with_printf_("set_component_picture", "%s.val=%s", component, picture); +} + +void Nextion::set_component_text(const char *component, const char *text) { + this->add_no_result_to_queue_with_printf_("set_component_text", "%s.txt=\"%s\"", component, text); +} + +void Nextion::set_component_value(const char *component, int value) { + this->add_no_result_to_queue_with_printf_("set_component_value", "%s.val=%d", component, value); +} + +void Nextion::add_waveform_data(int component_id, uint8_t channel_number, uint8_t value) { + this->add_no_result_to_queue_with_printf_("add_waveform_data", "add %d,%u,%u", component_id, channel_number, value); +} + +void Nextion::open_waveform_channel(int component_id, uint8_t channel_number, uint8_t value) { + this->add_no_result_to_queue_with_printf_("open_waveform_channel", "addt %d,%u,%u", component_id, channel_number, + value); +} + +void Nextion::set_component_coordinates(const char *component, int x, int y) { + this->add_no_result_to_queue_with_printf_("set_component_coordinates command 1", "%s.xcen=%d", component, x); + this->add_no_result_to_queue_with_printf_("set_component_coordinates command 2", "%s.ycen=%d", component, y); +} + +// Drawing +void Nextion::display_picture(int picture_id, int x_start, int y_start) { + this->add_no_result_to_queue_with_printf_("display_picture", "pic %d %d %d", x_start, y_start, picture_id); +} + +void Nextion::fill_area(int x1, int y1, int width, int height, const char *color) { + this->add_no_result_to_queue_with_printf_("fill_area", "fill %d,%d,%d,%d,%s", x1, y1, width, height, color); +} + +void Nextion::fill_area(int x1, int y1, int width, int height, Color color) { + this->add_no_result_to_queue_with_printf_("fill_area", "fill %d,%d,%d,%d,%d", x1, y1, width, height, + display::ColorUtil::color_to_565(color)); +} + +void Nextion::line(int x1, int y1, int x2, int y2, const char *color) { + this->add_no_result_to_queue_with_printf_("line", "line %d,%d,%d,%d,%s", x1, y1, x2, y2, color); +} + +void Nextion::line(int x1, int y1, int x2, int y2, Color color) { + this->add_no_result_to_queue_with_printf_("line", "line %d,%d,%d,%d,%d", x1, y1, x2, y2, + display::ColorUtil::color_to_565(color)); +} + +void Nextion::rectangle(int x1, int y1, int width, int height, const char *color) { + this->add_no_result_to_queue_with_printf_("draw", "draw %d,%d,%d,%d,%s", x1, y1, x1 + width, y1 + height, color); +} + +void Nextion::rectangle(int x1, int y1, int width, int height, Color color) { + this->add_no_result_to_queue_with_printf_("draw", "draw %d,%d,%d,%d,%d", x1, y1, x1 + width, y1 + height, + display::ColorUtil::color_to_565(color)); +} + +void Nextion::circle(int center_x, int center_y, int radius, const char *color) { + this->add_no_result_to_queue_with_printf_("cir", "cir %d,%d,%d,%s", center_x, center_y, radius, color); +} + +void Nextion::circle(int center_x, int center_y, int radius, Color color) { + this->add_no_result_to_queue_with_printf_("cir", "cir %d,%d,%d,%d", center_x, center_y, radius, + display::ColorUtil::color_to_565(color)); +} + +void Nextion::filled_circle(int center_x, int center_y, int radius, const char *color) { + this->add_no_result_to_queue_with_printf_("cirs", "cirs %d,%d,%d,%s", center_x, center_y, radius, color); +} + +void Nextion::filled_circle(int center_x, int center_y, int radius, Color color) { + this->add_no_result_to_queue_with_printf_("cirs", "cirs %d,%d,%d,%d", center_x, center_y, radius, + display::ColorUtil::color_to_565(color)); +} + +#ifdef USE_TIME +void Nextion::set_nextion_rtc_time(time::ESPTime time) { + this->add_no_result_to_queue_with_printf_("rtc0", "rtc0=%u", time.year); + this->add_no_result_to_queue_with_printf_("rtc1", "rtc1=%u", time.month); + this->add_no_result_to_queue_with_printf_("rtc2", "rtc2=%u", time.day_of_month); + this->add_no_result_to_queue_with_printf_("rtc3", "rtc3=%u", time.hour); + this->add_no_result_to_queue_with_printf_("rtc4", "rtc4=%u", time.minute); + this->add_no_result_to_queue_with_printf_("rtc5", "rtc5=%u", time.second); +} +#endif + +} // namespace nextion +} // namespace esphome diff --git a/esphome/components/nextion/nextion_component.cpp b/esphome/components/nextion/nextion_component.cpp new file mode 100644 index 0000000000..bbb2cf6cb2 --- /dev/null +++ b/esphome/components/nextion/nextion_component.cpp @@ -0,0 +1,116 @@ +#include "nextion_component.h" + +namespace esphome { +namespace nextion { + +void NextionComponent::set_background_color(Color bco) { + if (this->variable_name_ == this->variable_name_to_send_) { + return; // This is a variable. no need to set color + } + this->bco_ = bco; + this->bco_needs_update_ = true; + this->bco_is_set_ = true; + this->update_component_settings(); +} + +void NextionComponent::set_background_pressed_color(Color bco2) { + if (this->variable_name_ == this->variable_name_to_send_) { + return; // This is a variable. no need to set color + } + + this->bco2_ = bco2; + this->bco2_needs_update_ = true; + this->bco2_is_set_ = true; + this->update_component_settings(); +} + +void NextionComponent::set_foreground_color(Color pco) { + if (this->variable_name_ == this->variable_name_to_send_) { + return; // This is a variable. no need to set color + } + this->pco_ = pco; + this->pco_needs_update_ = true; + this->pco_is_set_ = true; + this->update_component_settings(); +} + +void NextionComponent::set_foreground_pressed_color(Color pco2) { + if (this->variable_name_ == this->variable_name_to_send_) { + return; // This is a variable. no need to set color + } + this->pco2_ = pco2; + this->pco2_needs_update_ = true; + this->pco2_is_set_ = true; + this->update_component_settings(); +} + +void NextionComponent::set_font_id(uint8_t font_id) { + if (this->variable_name_ == this->variable_name_to_send_) { + return; // This is a variable. no need to set color + } + this->font_id_ = font_id; + this->font_id_needs_update_ = true; + this->font_id_is_set_ = true; + this->update_component_settings(); +} + +void NextionComponent::set_visible(bool visible) { + if (this->variable_name_ == this->variable_name_to_send_) { + return; // This is a variable. no need to set color + } + this->visible_ = visible; + this->visible_needs_update_ = true; + this->visible_is_set_ = true; + this->update_component_settings(); +} + +void NextionComponent::update_component_settings(bool force_update) { + if (this->nextion_->is_sleeping() || !this->nextion_->is_setup() || !this->visible_is_set_ || + (!this->visible_needs_update_ && !this->visible_)) { + this->needs_to_send_update_ = true; + return; + } + + if (this->visible_needs_update_ || (force_update && this->visible_is_set_)) { + std::string name_to_send = this->variable_name_; + + size_t pos = name_to_send.find_last_of('.'); + if (pos != std::string::npos) { + name_to_send = name_to_send.substr(pos + 1); + } + + this->visible_needs_update_ = false; + + if (this->visible_) { + this->nextion_->show_component(name_to_send.c_str()); + this->send_state_to_nextion(); + } else { + this->nextion_->hide_component(name_to_send.c_str()); + return; + } + } + + if (this->bco_needs_update_ || (force_update && this->bco2_is_set_)) { + this->nextion_->set_component_background_color(this->variable_name_.c_str(), this->bco_); + this->bco_needs_update_ = false; + } + if (this->bco2_needs_update_ || (force_update && this->bco2_is_set_)) { + this->nextion_->set_component_pressed_background_color(this->variable_name_.c_str(), this->bco2_); + this->bco2_needs_update_ = false; + } + if (this->pco_needs_update_ || (force_update && this->pco_is_set_)) { + this->nextion_->set_component_font_color(this->variable_name_.c_str(), this->pco_); + this->pco_needs_update_ = false; + } + if (this->pco2_needs_update_ || (force_update && this->pco2_is_set_)) { + this->nextion_->set_component_pressed_font_color(this->variable_name_.c_str(), this->pco2_); + this->pco2_needs_update_ = false; + } + + if (this->font_id_needs_update_ || (force_update && this->font_id_is_set_)) { + this->nextion_->set_component_font(this->variable_name_.c_str(), this->font_id_); + this->font_id_needs_update_ = false; + } +} +} // namespace nextion +} // namespace esphome diff --git a/esphome/components/nextion/nextion_component.h b/esphome/components/nextion/nextion_component.h new file mode 100644 index 0000000000..2f3c4f3c16 --- /dev/null +++ b/esphome/components/nextion/nextion_component.h @@ -0,0 +1,49 @@ +#pragma once +#include "esphome/core/defines.h" +#include "esphome/core/color.h" +#include "nextion_base.h" + +namespace esphome { +namespace nextion { +class NextionComponent; + +class NextionComponent : public NextionComponentBase { + public: + void update_component_settings() override { this->update_component_settings(false); }; + + void update_component_settings(bool force_update) override; + + void set_background_color(Color bco); + void set_background_pressed_color(Color bco2); + void set_foreground_color(Color pco); + void set_foreground_pressed_color(Color pco2); + void set_font_id(uint8_t font_id); + void set_visible(bool visible); + + protected: + NextionBase *nextion_; + + bool bco_needs_update_ = false; + bool bco_is_set_ = false; + Color bco_; + bool bco2_needs_update_ = false; + bool bco2_is_set_ = false; + Color bco2_; + bool pco_needs_update_ = false; + bool pco_is_set_ = false; + Color pco_; + bool pco2_needs_update_ = false; + bool pco2_is_set_ = false; + Color pco2_; + uint8_t font_id_ = 0; + bool font_id_needs_update_ = false; + bool font_id_is_set_ = false; + + bool visible_ = true; + bool visible_needs_update_ = false; + bool visible_is_set_ = false; + + // void send_state_to_nextion() = 0; +}; +} // namespace nextion +} // namespace esphome diff --git a/esphome/components/nextion/nextion_component_base.h b/esphome/components/nextion/nextion_component_base.h new file mode 100644 index 0000000000..71ad803bc4 --- /dev/null +++ b/esphome/components/nextion/nextion_component_base.h @@ -0,0 +1,95 @@ +#pragma once +#include +#include "esphome/core/defines.h" + +namespace esphome { +namespace nextion { + +enum NextionQueueType { + NO_RESULT = 0, + SENSOR = 1, + BINARY_SENSOR = 2, + SWITCH = 3, + TEXT_SENSOR = 4, + WAVEFORM_SENSOR = 5, +}; + +static const char *const NEXTION_QUEUE_TYPE_STRINGS[] = {"NO_RESULT", "SENSOR", "BINARY_SENSOR", + "SWITCH", "TEXT_SENSOR", "WAVEFORM_SENSOR"}; + +class NextionComponentBase; + +class NextionQueue { + public: + virtual ~NextionQueue() = default; + NextionComponentBase *component; + uint32_t queue_time = 0; +}; + +class NextionComponentBase { + public: + virtual ~NextionComponentBase() = default; + + void set_variable_name(const std::string &variable_name, const std::string &variable_name_to_send = "") { + variable_name_ = variable_name; + if (variable_name_to_send.empty()) { + variable_name_to_send_ = variable_name; + } else { + variable_name_to_send_ = variable_name_to_send; + } + } + + virtual void update_component_settings(){}; + virtual void update_component_settings(bool force_update){}; + + virtual void update_component(){}; + virtual void process_sensor(const std::string &variable_name, int state){}; + virtual void process_touch(uint8_t page_id, uint8_t component_id, bool on){}; + virtual void process_text(const std::string &variable_name, const std::string &text_value){}; + virtual void process_bool(const std::string &variable_name, bool on){}; + + virtual void set_state(float state){}; + virtual void set_state(float state, bool publish){}; + virtual void set_state(float state, bool publish, bool send_to_nextion){}; + + virtual void set_state(bool state){}; + virtual void set_state(bool state, bool publish){}; + virtual void set_state(bool state, bool publish, bool send_to_nextion){}; + + virtual void set_state(const std::string &state) {} + virtual void set_state(const std::string &state, bool publish) {} + virtual void set_state(const std::string &state, bool publish, bool send_to_nextion){}; + + uint8_t get_component_id() { return this->component_id_; } + void set_component_id(uint8_t component_id) { component_id_ = component_id; } + + uint8_t get_wave_channel_id() { return this->wave_chan_id_; } + void set_wave_channel_id(uint8_t wave_chan_id) { this->wave_chan_id_ = wave_chan_id; } + + std::vector get_wave_buffer() { return this->wave_buffer_; } + size_t get_wave_buffer_size() { return this->wave_buffer_.size(); } + + std::string get_variable_name() { return this->variable_name_; } + std::string get_variable_name_to_send() { return this->variable_name_to_send_; } + virtual NextionQueueType get_queue_type() { return NextionQueueType::NO_RESULT; } + virtual std::string get_queue_type_string() { return NEXTION_QUEUE_TYPE_STRINGS[this->get_queue_type()]; } + virtual void set_state_from_int(int state_value, bool publish, bool send_to_nextion){}; + virtual void set_state_from_string(const std::string &state_value, bool publish, bool send_to_nextion){}; + virtual void send_state_to_nextion(){}; + bool get_needs_to_send_update() { return this->needs_to_send_update_; } + uint8_t get_wave_chan_id() { return this->wave_chan_id_; } + void set_wave_max_length(int wave_max_length) { this->wave_max_length_ = wave_max_length; } + + protected: + std::string variable_name_; + std::string variable_name_to_send_; + + uint8_t component_id_ = 0; + uint8_t wave_chan_id_ = UINT8_MAX; + std::vector wave_buffer_; + int wave_max_length_ = 255; + + bool needs_to_send_update_; +}; +} // namespace nextion +} // namespace esphome diff --git a/esphome/components/nextion/nextion_upload.cpp b/esphome/components/nextion/nextion_upload.cpp new file mode 100644 index 0000000000..6a681af6c5 --- /dev/null +++ b/esphome/components/nextion/nextion_upload.cpp @@ -0,0 +1,343 @@ + +#include "nextion.h" +#include "esphome/core/application.h" +#include "esphome/core/util.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace nextion { +static const char *const TAG = "nextion_upload"; + +#if defined(USE_TFT_UPLOAD) && (defined(USE_ETHERNET) || defined(USE_WIFI)) + +// Followed guide +// https://unofficialnextion.com/t/nextion-upload-protocol-v1-2-the-fast-one/1044/2 + +int Nextion::upload_by_chunks_(HTTPClient *http, int range_start) { + int range_end = 0; + + if (range_start == 0 && this->transfer_buffer_size_ > 16384) { // Start small at the first run in case of a big skip + range_end = 16384 - 1; + } else { + range_end = range_start + this->transfer_buffer_size_ - 1; + } + + if (range_end > this->tft_size_) + range_end = this->tft_size_; + + bool begin_status = false; +#ifdef ARDUINO_ARCH_ESP32 + begin_status = http->begin(this->tft_url_.c_str()); +#endif +#ifdef ARDUINO_ARCH_ESP8266 +#ifndef CLANG_TIDY + http->setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); + http->setRedirectLimit(3); + begin_status = http->begin(*this->get_wifi_client_(), this->tft_url_.c_str()); +#endif +#endif + + char range_header[64]; + sprintf(range_header, "bytes=%d-%d", range_start, range_end); + + ESP_LOGD(TAG, "Requesting range: %s", range_header); + + int tries = 1; + int code = 0; + while (tries <= 5) { +#ifdef ARDUINO_ARCH_ESP32 + begin_status = http->begin(this->tft_url_.c_str()); +#endif +#ifndef CLANG_TIDY +#ifdef ARDUINO_ARCH_ESP8266 + begin_status = http->begin(*this->get_wifi_client_(), this->tft_url_.c_str()); +#endif +#endif + + ++tries; + if (!begin_status) { + ESP_LOGD(TAG, "upload_by_chunks_: connection failed"); + continue; + } + + http->addHeader("Range", range_header); + + code = http->GET(); + if (code == 200 || code == 206) { + break; + } + ESP_LOGW(TAG, "HTTP Request failed; URL: %s; Error: %s, retries(%d/5)", this->tft_url_.c_str(), + HTTPClient::errorToString(code).c_str(), tries); + http->end(); + App.feed_wdt(); + delay(500); // NOLINT + } + + if (tries > 5) { + return -1; + } + + std::string recv_string; + size_t size = 0; + int sent = 0; + int range = range_end - range_start; + + while (sent < range) { + size = http->getStreamPtr()->available(); + if (!size) { + App.feed_wdt(); + delay(0); + continue; + } + int c = http->getStreamPtr()->readBytes( + &this->transfer_buffer_[sent], ((size > this->transfer_buffer_size_) ? this->transfer_buffer_size_ : size)); + sent += c; + } + http->end(); + ESP_LOGN(TAG, "this->content_length_ %d sent %d", this->content_length_, sent); + for (uint32_t i = 0; i < range; i += 4096) { + this->write_array(&this->transfer_buffer_[i], 4096); + this->content_length_ -= 4096; + ESP_LOGN(TAG, "this->content_length_ %d range %d range_end %d range_start %d", this->content_length_, range, + range_end, range_start); + + if (!this->upload_first_chunk_sent_) { + this->upload_first_chunk_sent_ = true; + delay(500); // NOLINT + App.feed_wdt(); + } + + this->recv_ret_string_(recv_string, 2048, true); + if (recv_string[0] == 0x08) { + uint32_t result = 0; + for (int i = 0; i < 4; ++i) { + result += static_cast(recv_string[i + 1]) << (8 * i); + } + if (result > 0) { + ESP_LOGD(TAG, "Nextion reported new range %d", result); + this->content_length_ = this->tft_size_ - result; + return result; + } + } + recv_string.clear(); + } + return range_end + 1; +} + +void Nextion::upload_tft() { + if (this->is_updating_) { + ESP_LOGD(TAG, "Currently updating"); + return; + } + + if (!network_is_connected()) { + ESP_LOGD(TAG, "network is not connected"); + return; + } + + this->is_updating_ = true; + + HTTPClient http; + http.setTimeout(15000); // Yes 15 seconds.... Helps 8266s along + bool begin_status = false; +#ifdef ARDUINO_ARCH_ESP32 + begin_status = http.begin(this->tft_url_.c_str()); +#endif +#ifdef ARDUINO_ARCH_ESP8266 +#ifndef CLANG_TIDY + http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); + http.setRedirectLimit(3); + begin_status = http.begin(*this->get_wifi_client_(), this->tft_url_.c_str()); +#endif +#endif + + if (!begin_status) { + this->is_updating_ = false; + ESP_LOGD(TAG, "connection failed"); +#ifdef ARDUINO_ARCH_ESP32 + if (psramFound()) + free(this->transfer_buffer_); + else +#endif + delete this->transfer_buffer_; + return; + } else { + ESP_LOGD(TAG, "Connected"); + } + + http.addHeader("Range", "bytes=0-255"); + const char *header_names[] = {"Content-Range"}; + http.collectHeaders(header_names, 1); + ESP_LOGD(TAG, "Requesting URL: %s", this->tft_url_.c_str()); + + http.setReuse(true); + // try up to 5 times. DNS sometimes needs a second try or so + int tries = 1; + int code = http.GET(); + delay(100); // NOLINT + + App.feed_wdt(); + while (code != 200 && code != 206 && tries <= 5) { + ESP_LOGW(TAG, "HTTP Request failed; URL: %s; Error: %s, retrying (%d/5)", this->tft_url_.c_str(), + HTTPClient::errorToString(code).c_str(), tries); + + delay(250); // NOLINT + App.feed_wdt(); + code = http.GET(); + ++tries; + } + + if ((code != 200 && code != 206) || tries > 5) { + this->upload_end_(); + } + + String content_range_string = http.header("Content-Range"); + content_range_string.remove(0, 12); + this->content_length_ = content_range_string.toInt(); + this->tft_size_ = content_length_; + http.end(); + + if (this->content_length_ < 4096) { + ESP_LOGE(TAG, "Failed to get file size"); + this->upload_end_(); + } + + ESP_LOGD(TAG, "Updating Nextion %s...", this->device_model_.c_str()); + // The Nextion will ignore the update command if it is sleeping + + this->send_command_("sleep=0"); + this->set_backlight_brightness(1.0); + delay(250); // NOLINT + + App.feed_wdt(); + + char command[128]; + // Tells the Nextion the content length of the tft file and baud rate it will be sent at + // Once the Nextion accepts the command it will wait until the file is successfully uploaded + // If it fails for any reason a power cycle of the display will be needed + sprintf(command, "whmi-wris %d,%d,1", this->content_length_, this->parent_->get_baud_rate()); + + // Clear serial receive buffer + uint8_t d; + while (this->available()) { + this->read_byte(&d); + }; + + this->send_command_(command); + + App.feed_wdt(); + + std::string response; + ESP_LOGD(TAG, "Waiting for upgrade response"); + this->recv_ret_string_(response, 2000, true); // This can take some time to return + + // The Nextion display will, if it's ready to accept data, send a 0x05 byte. + ESP_LOGD(TAG, "Upgrade response is %s %zu", response.c_str(), response.length()); + + for (int i = 0; i < response.length(); i++) { + ESP_LOGD(TAG, "Available %d : 0x%02X", i, response[i]); + } + + if (response.find(0x05) != std::string::npos) { + ESP_LOGD(TAG, "preparation for tft update done"); + } else { + ESP_LOGD(TAG, "preparation for tft update failed %d \"%s\"", response[0], response.c_str()); + this->upload_end_(); + } + + // Nextion wants 4096 bytes at a time. Make chunk_size a multiple of 4096 +#ifdef ARDUINO_ARCH_ESP32 + uint32_t chunk_size = 8192; + if (psramFound()) { + chunk_size = this->content_length_; + } else { + if (ESP.getFreeHeap() > 40960) { // 32K to keep on hand + int chunk = int((ESP.getFreeHeap() - 32768) / 4096); + chunk_size = chunk * 4096; + chunk_size = chunk_size > 65536 ? 65536 : chunk_size; + } else if (ESP.getFreeHeap() < 10240) { + chunk_size = 4096; + } + } +#else + uint32_t chunk_size = ESP.getFreeHeap() < 10240 ? 4096 : 8192; +#endif + + if (this->transfer_buffer_ == nullptr) { +#ifdef ARDUINO_ARCH_ESP32 + if (psramFound()) { + ESP_LOGD(TAG, "Allocating PSRAM buffer size %d, Free PSRAM size is %u", chunk_size, ESP.getFreePsram()); + this->transfer_buffer_ = (uint8_t *) ps_malloc(chunk_size); + if (this->transfer_buffer_ == nullptr) { + ESP_LOGE(TAG, "Could not allocate buffer size %d!", chunk_size); + this->upload_end_(); + } + } else { +#endif + ESP_LOGD(TAG, "Allocating buffer size %d, Heap size is %u", chunk_size, ESP.getFreeHeap()); + this->transfer_buffer_ = new uint8_t[chunk_size]; + if (!this->transfer_buffer_) { // Try a smaller size + ESP_LOGD(TAG, "Could not allocate buffer size: %d trying 4096 instead", chunk_size); + chunk_size = 4096; + ESP_LOGD(TAG, "Allocating %d buffer", chunk_size); + this->transfer_buffer_ = new uint8_t[chunk_size]; + + if (!this->transfer_buffer_) + this->upload_end_(); +#ifdef ARDUINO_ARCH_ESP32 + } +#endif + } + + this->transfer_buffer_size_ = chunk_size; + } + + ESP_LOGD(TAG, "Updating tft from \"%s\" with a file size of %d using %zu chunksize, Heap Size %d", + this->tft_url_.c_str(), this->content_length_, this->transfer_buffer_size_, ESP.getFreeHeap()); + + int result = 0; + while (this->content_length_ > 0) { + result = this->upload_by_chunks_(&http, result); + if (result < 0) { + ESP_LOGD(TAG, "Error updating Nextion!"); + this->upload_end_(); + } + App.feed_wdt(); + ESP_LOGD(TAG, "Heap Size %d, Bytes left %d", ESP.getFreeHeap(), this->content_length_); + } + ESP_LOGD(TAG, "Succesfully updated Nextion!"); + + this->upload_end_(); +} + +void Nextion::upload_end_() { + ESP_LOGD(TAG, "Restarting Nextion"); + this->soft_reset(); + delay(1500); // NOLINT + ESP_LOGD(TAG, "Restarting esphome"); + ESP.restart(); +} + +#ifdef ARDUINO_ARCH_ESP8266 +WiFiClient *Nextion::get_wifi_client_() { + if (this->tft_url_.compare(0, 6, "https:") == 0) { + if (this->wifi_client_secure_ == nullptr) { + this->wifi_client_secure_ = new BearSSL::WiFiClientSecure(); + this->wifi_client_secure_->setInsecure(); + this->wifi_client_secure_->setBufferSizes(512, 512); + } + return this->wifi_client_secure_; + } + + if (this->wifi_client_ == nullptr) { + this->wifi_client_ = new WiFiClient(); + } + return this->wifi_client_; +} +#endif + +#else +void Nextion::upload_tft() { ESP_LOGW(TAG, "tft_url, WIFI or Ethernet components are needed. Cannot upload."); } +#endif +} // namespace nextion +} // namespace esphome diff --git a/esphome/components/nextion/sensor/__init__.py b/esphome/components/nextion/sensor/__init__.py new file mode 100644 index 0000000000..f8a383e3ac --- /dev/null +++ b/esphome/components/nextion/sensor/__init__.py @@ -0,0 +1,99 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor + +from esphome.const import ( + CONF_ID, + UNIT_EMPTY, + ICON_EMPTY, + CONF_COMPONENT_ID, + DEVICE_CLASS_EMPTY, +) +from .. import nextion_ns, CONF_NEXTION_ID + +from ..base_component import ( + setup_component_core_, + CONFIG_SENSOR_COMPONENT_SCHEMA, + CONF_VARIABLE_NAME, + CONF_COMPONENT_NAME, + CONF_PRECISION, + CONF_WAVE_CHANNEL_ID, + CONF_WAVE_MAX_VALUE, + CONF_WAVEFORM_SEND_LAST_VALUE, + CONF_WAVE_MAX_LENGTH, +) + + +CODEOWNERS = ["@senexcrenshaw"] + +NextionSensor = nextion_ns.class_("NextionSensor", sensor.Sensor, cg.PollingComponent) + + +def CheckWaveID(value): + value = cv.int_(value) + if value < 0 or value > 3: + raise cv.Invalid(f"Valid range for {CONF_WAVE_CHANNEL_ID} is 0-3") + return value + + +def _validate(config): + if CONF_WAVE_CHANNEL_ID in config and CONF_COMPONENT_ID not in config: + raise cv.Invalid( + f"{CONF_COMPONENT_ID} is required when {CONF_WAVE_CHANNEL_ID} is set" + ) + + return config + + +CONFIG_SCHEMA = cv.All( + sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 2, DEVICE_CLASS_EMPTY) + .extend( + { + cv.GenerateID(): cv.declare_id(NextionSensor), + cv.Optional(CONF_PRECISION, default=0): cv.int_range(min=0, max=8), + cv.Optional(CONF_WAVE_CHANNEL_ID): CheckWaveID, + cv.Optional(CONF_COMPONENT_ID): cv.uint8_t, + cv.Optional(CONF_WAVE_MAX_LENGTH, default=255): cv.int_range( + min=1, max=1024 + ), + cv.Optional(CONF_WAVE_MAX_VALUE, default=100): cv.int_range( + min=1, max=1024 + ), + cv.Optional(CONF_WAVEFORM_SEND_LAST_VALUE, default=True): cv.boolean, + } + ) + .extend(CONFIG_SENSOR_COMPONENT_SCHEMA) + .extend(cv.polling_component_schema("never")), + cv.has_exactly_one_key(CONF_COMPONENT_ID, CONF_COMPONENT_NAME, CONF_VARIABLE_NAME), + _validate, +) + + +async def to_code(config): + + hub = await cg.get_variable(config[CONF_NEXTION_ID]) + var = cg.new_Pvariable(config[CONF_ID], hub) + await cg.register_component(var, config) + await sensor.register_sensor(var, config) + + cg.add(hub.register_sensor_component(var)) + + await setup_component_core_(var, config, ".val") + + if CONF_PRECISION in config: + cg.add(var.set_precision(config[CONF_PRECISION])) + + if CONF_COMPONENT_ID in config: + cg.add(var.set_component_id(config[CONF_COMPONENT_ID])) + + if CONF_WAVE_CHANNEL_ID in config: + cg.add(var.set_wave_channel_id(config[CONF_WAVE_CHANNEL_ID])) + + if CONF_WAVEFORM_SEND_LAST_VALUE in config: + cg.add(var.set_waveform_send_last_value(config[CONF_WAVEFORM_SEND_LAST_VALUE])) + + if CONF_WAVE_MAX_VALUE in config: + cg.add(var.set_wave_max_value(config[CONF_WAVE_MAX_VALUE])) + + if CONF_WAVE_MAX_LENGTH in config: + cg.add(var.set_wave_max_length(config[CONF_WAVE_MAX_LENGTH])) diff --git a/esphome/components/nextion/sensor/nextion_sensor.cpp b/esphome/components/nextion/sensor/nextion_sensor.cpp new file mode 100644 index 0000000000..bbcb465d85 --- /dev/null +++ b/esphome/components/nextion/sensor/nextion_sensor.cpp @@ -0,0 +1,110 @@ +#include "nextion_sensor.h" +#include "esphome/core/util.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace nextion { + +static const char *const TAG = "nextion_sensor"; + +void NextionSensor::process_sensor(const std::string &variable_name, int state) { + if (!this->nextion_->is_setup()) + return; + + if (this->wave_chan_id_ == UINT8_MAX && this->variable_name_ == variable_name) { + this->publish_state(state); + ESP_LOGD(TAG, "Processed sensor \"%s\" state %d", variable_name.c_str(), state); + } +} + +void NextionSensor::add_to_wave_buffer(float state) { + this->needs_to_send_update_ = true; + + int wave_state = (int) ((state / (float) this->wave_maxvalue_) * 100); + + wave_buffer_.push_back(wave_state); + + if (this->wave_buffer_.size() > this->wave_max_length_) { + this->wave_buffer_.erase(this->wave_buffer_.begin()); + } +} + +void NextionSensor::update() { + if (!this->nextion_->is_setup()) + return; + + if (this->wave_chan_id_ == UINT8_MAX) { + this->nextion_->add_to_get_queue(this); + } else { + if (this->send_last_value_) { + this->add_to_wave_buffer(this->last_value_); + } + + this->wave_update_(); + } +} + +void NextionSensor::set_state(float state, bool publish, bool send_to_nextion) { + if (!this->nextion_->is_setup()) + return; + + if (isnan(state)) + return; + + if (this->wave_chan_id_ == UINT8_MAX) { + if (send_to_nextion) { + if (this->nextion_->is_sleeping() || !this->visible_) { + this->needs_to_send_update_ = true; + } else { + this->needs_to_send_update_ = false; + + if (this->precision_ > 0) { + double to_multiply = pow(10, this->precision_); + int state_value = (int) (state * to_multiply); + + this->nextion_->add_no_result_to_queue_with_set(this, (int) state_value); + } else { + this->nextion_->add_no_result_to_queue_with_set(this, (int) state); + } + } + } + } else { + if (this->send_last_value_) { + this->last_value_ = state; // Update will handle setting the buffer + } else { + this->add_to_wave_buffer(state); + } + } + + if (this->wave_chan_id_ == UINT8_MAX) { + if (publish) { + this->publish_state(state); + } else { + this->raw_state = state; + this->state = state; + this->has_state_ = true; + } + } + this->update_component_settings(); + + ESP_LOGN(TAG, "Wrote state for sensor \"%s\" state %lf", this->variable_name_.c_str(), state); +} + +void NextionSensor::wave_update_() { + if (this->nextion_->is_sleeping() || this->wave_buffer_.empty()) { + return; + } + +#ifdef NEXTION_PROTOCOL_LOG + size_t buffer_to_send = + this->wave_buffer_.size() < 255 ? this->wave_buffer_.size() : 255; // ADDT command can only send 255 + + ESP_LOGN(TAG, "wave_update send %zu of %zu value(s) to wave nextion component id %d and wave channel id %d", + buffer_to_send, this->wave_buffer_.size(), this->component_id_, this->wave_chan_id_); +#endif + + this->nextion_->add_addt_command_to_queue(this); +} + +} // namespace nextion +} // namespace esphome diff --git a/esphome/components/nextion/sensor/nextion_sensor.h b/esphome/components/nextion/sensor/nextion_sensor.h new file mode 100644 index 0000000000..e4dde9a513 --- /dev/null +++ b/esphome/components/nextion/sensor/nextion_sensor.h @@ -0,0 +1,49 @@ +#pragma once +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "../nextion_component.h" +#include "../nextion_base.h" + +namespace esphome { +namespace nextion { +class NextionSensor; + +class NextionSensor : public NextionComponent, public sensor::Sensor, public PollingComponent { + public: + NextionSensor(NextionBase *nextion) { this->nextion_ = nextion; } + void send_state_to_nextion() override { this->set_state(this->state, false, true); }; + + void update_component() override { this->update(); } + void update() override; + void add_to_wave_buffer(float state); + void set_precision(uint8_t precision) { this->precision_ = precision; } + void set_component_id(uint8_t component_id) { component_id_ = component_id; } + void set_wave_channel_id(uint8_t wave_chan_id) { this->wave_chan_id_ = wave_chan_id; } + void set_wave_max_value(uint32_t wave_maxvalue) { this->wave_maxvalue_ = wave_maxvalue; } + void process_sensor(const std::string &variable_name, int state) override; + + void set_state(float state) override { this->set_state(state, true, true); } + void set_state(float state, bool publish) override { this->set_state(state, publish, true); } + void set_state(float state, bool publish, bool send_to_nextion) override; + + void set_waveform_send_last_value(bool send_last_value) { this->send_last_value_ = send_last_value; } + uint8_t get_wave_chan_id() { return this->wave_chan_id_; } + void set_wave_max_length(int wave_max_length) { this->wave_max_length_ = wave_max_length; } + NextionQueueType get_queue_type() override { + return this->wave_chan_id_ == UINT8_MAX ? NextionQueueType::SENSOR : NextionQueueType::WAVEFORM_SENSOR; + } + void set_state_from_string(const std::string &state_value, bool publish, bool send_to_nextion) override {} + void set_state_from_int(int state_value, bool publish, bool send_to_nextion) override { + this->set_state(state_value, publish, send_to_nextion); + } + + protected: + uint8_t precision_ = 0; + uint32_t wave_maxvalue_ = 255; + + float last_value_ = 0; + bool send_last_value_ = true; + void wave_update_(); +}; +} // namespace nextion +} // namespace esphome diff --git a/esphome/components/nextion/switch/__init__.py b/esphome/components/nextion/switch/__init__.py new file mode 100644 index 0000000000..068681fa14 --- /dev/null +++ b/esphome/components/nextion/switch/__init__.py @@ -0,0 +1,39 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import switch + +from esphome.const import CONF_ID +from .. import nextion_ns, CONF_NEXTION_ID + +from ..base_component import ( + setup_component_core_, + CONF_COMPONENT_NAME, + CONF_VARIABLE_NAME, + CONFIG_SWITCH_COMPONENT_SCHEMA, +) + +CODEOWNERS = ["@senexcrenshaw"] + +NextionSwitch = nextion_ns.class_("NextionSwitch", switch.Switch, cg.PollingComponent) + +CONFIG_SCHEMA = cv.All( + switch.SWITCH_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(NextionSwitch), + } + ) + .extend(CONFIG_SWITCH_COMPONENT_SCHEMA) + .extend(cv.polling_component_schema("never")), + cv.has_exactly_one_key(CONF_COMPONENT_NAME, CONF_VARIABLE_NAME), +) + + +async def to_code(config): + hub = await cg.get_variable(config[CONF_NEXTION_ID]) + var = cg.new_Pvariable(config[CONF_ID], hub) + await cg.register_component(var, config) + await switch.register_switch(var, config) + + cg.add(hub.register_switch_component(var)) + + await setup_component_core_(var, config, ".val") diff --git a/esphome/components/nextion/switch/nextion_switch.cpp b/esphome/components/nextion/switch/nextion_switch.cpp new file mode 100644 index 0000000000..1f32ad3425 --- /dev/null +++ b/esphome/components/nextion/switch/nextion_switch.cpp @@ -0,0 +1,52 @@ +#include "nextion_switch.h" +#include "esphome/core/util.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace nextion { + +static const char *const TAG = "nextion_switch"; + +void NextionSwitch::process_bool(const std::string &variable_name, bool on) { + if (!this->nextion_->is_setup()) + return; + if (this->variable_name_ == variable_name) { + this->publish_state(on); + + ESP_LOGD(TAG, "Processed switch \"%s\" state %s", variable_name.c_str(), state ? "ON" : "OFF"); + } +} + +void NextionSwitch::update() { + if (!this->nextion_->is_setup()) + return; + this->nextion_->add_to_get_queue(this); +} + +void NextionSwitch::set_state(bool state, bool publish, bool send_to_nextion) { + if (!this->nextion_->is_setup()) + return; + + if (send_to_nextion) { + if (this->nextion_->is_sleeping() || !this->visible_) { + this->needs_to_send_update_ = true; + } else { + this->needs_to_send_update_ = false; + this->nextion_->add_no_result_to_queue_with_set(this, (int) state); + } + } + if (publish) { + this->publish_state(state); + } else { + this->state = state; + } + + this->update_component_settings(); + + ESP_LOGN(TAG, "Updated switch \"%s\" state %s", this->variable_name_.c_str(), ONOFF(state)); +} + +void NextionSwitch::write_state(bool state) { this->set_state(state); } + +} // namespace nextion +} // namespace esphome diff --git a/esphome/components/nextion/switch/nextion_switch.h b/esphome/components/nextion/switch/nextion_switch.h new file mode 100644 index 0000000000..1548287473 --- /dev/null +++ b/esphome/components/nextion/switch/nextion_switch.h @@ -0,0 +1,34 @@ +#pragma once +#include "esphome/core/component.h" +#include "esphome/components/switch/switch.h" +#include "../nextion_component.h" +#include "../nextion_base.h" + +namespace esphome { +namespace nextion { +class NextionSwitch; + +class NextionSwitch : public NextionComponent, public switch_::Switch, public PollingComponent { + public: + NextionSwitch(NextionBase *nextion) { this->nextion_ = nextion; } + + void update() override; + void update_component() override { this->update(); } + void process_bool(const std::string &variable_name, bool on) override; + + void set_state(bool state) override { this->set_state(state, true, true); } + void set_state(bool state, bool publish) override { this->set_state(state, publish, true); } + void set_state(bool state, bool publish, bool send_to_nextion) override; + + void send_state_to_nextion() override { this->set_state(this->state, false, true); }; + NextionQueueType get_queue_type() override { return NextionQueueType::SWITCH; } + void set_state_from_string(const std::string &state_value, bool publish, bool send_to_nextion) override {} + void set_state_from_int(int state_value, bool publish, bool send_to_nextion) override { + this->set_state(state_value != 0, publish, send_to_nextion); + } + + protected: + void write_state(bool state) override; +}; +} // namespace nextion +} // namespace esphome diff --git a/esphome/components/nextion/text_sensor/__init__.py b/esphome/components/nextion/text_sensor/__init__.py new file mode 100644 index 0000000000..9c170dd807 --- /dev/null +++ b/esphome/components/nextion/text_sensor/__init__.py @@ -0,0 +1,38 @@ +from esphome.components import text_sensor +import esphome.config_validation as cv +import esphome.codegen as cg +from esphome.const import CONF_ID + +from .. import nextion_ns, CONF_NEXTION_ID + +from ..base_component import ( + setup_component_core_, + CONFIG_TEXT_COMPONENT_SCHEMA, +) + +CODEOWNERS = ["@senexcrenshaw"] + +NextionTextSensor = nextion_ns.class_( + "NextionTextSensor", text_sensor.TextSensor, cg.PollingComponent +) + +CONFIG_SCHEMA = ( + text_sensor.TEXT_SENSOR_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(NextionTextSensor), + } + ) + .extend(CONFIG_TEXT_COMPONENT_SCHEMA) + .extend(cv.polling_component_schema("never")) +) + + +async def to_code(config): + hub = await cg.get_variable(config[CONF_NEXTION_ID]) + var = cg.new_Pvariable(config[CONF_ID], hub) + await cg.register_component(var, config) + await text_sensor.register_text_sensor(var, config) + + cg.add(hub.register_textsensor_component(var)) + + await setup_component_core_(var, config, ".txt") diff --git a/esphome/components/nextion/text_sensor/nextion_textsensor.cpp b/esphome/components/nextion/text_sensor/nextion_textsensor.cpp new file mode 100644 index 0000000000..08f032df74 --- /dev/null +++ b/esphome/components/nextion/text_sensor/nextion_textsensor.cpp @@ -0,0 +1,49 @@ +#include "nextion_textsensor.h" +#include "esphome/core/util.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace nextion { +static const char *const TAG = "nextion_textsensor"; + +void NextionTextSensor::process_text(const std::string &variable_name, const std::string &text_value) { + if (!this->nextion_->is_setup()) + return; + if (this->variable_name_ == variable_name) { + this->publish_state(text_value); + ESP_LOGD(TAG, "Processed text_sensor \"%s\" state \"%s\"", variable_name.c_str(), text_value.c_str()); + } +} + +void NextionTextSensor::update() { + if (!this->nextion_->is_setup()) + return; + this->nextion_->add_to_get_queue(this); +} + +void NextionTextSensor::set_state(const std::string &state, bool publish, bool send_to_nextion) { + if (!this->nextion_->is_setup()) + return; + + if (send_to_nextion) { + if (this->nextion_->is_sleeping() || !this->visible_) { + this->needs_to_send_update_ = true; + } else { + this->nextion_->add_no_result_to_queue_with_set(this, state); + } + } + + if (publish) { + this->publish_state(state); + } else { + this->state = state; + this->has_state_ = true; + } + + this->update_component_settings(); + + ESP_LOGN(TAG, "Wrote state for text_sensor \"%s\" state \"%s\"", this->variable_name_.c_str(), state.c_str()); +} + +} // namespace nextion +} // namespace esphome diff --git a/esphome/components/nextion/text_sensor/nextion_textsensor.h b/esphome/components/nextion/text_sensor/nextion_textsensor.h new file mode 100644 index 0000000000..5716d0a008 --- /dev/null +++ b/esphome/components/nextion/text_sensor/nextion_textsensor.h @@ -0,0 +1,32 @@ +#pragma once +#include "esphome/core/component.h" +#include "esphome/components/text_sensor/text_sensor.h" +#include "../nextion_component.h" +#include "../nextion_base.h" + +namespace esphome { +namespace nextion { +class NextionTextSensor; + +class NextionTextSensor : public NextionComponent, public text_sensor::TextSensor, public PollingComponent { + public: + NextionTextSensor(NextionBase *nextion) { this->nextion_ = nextion; } + void update() override; + void update_component() override { this->update(); } + void on_state_changed(const std::string &state); + + void process_text(const std::string &variable_name, const std::string &text_value) override; + + void set_state(const std::string &state, bool publish) override { this->set_state(state, publish, true); } + void set_state(const std::string &state) override { this->set_state(state, true, true); } + void set_state(const std::string &state, bool publish, bool send_to_nextion) override; + + void send_state_to_nextion() override { this->set_state(this->state, false, true); }; + NextionQueueType get_queue_type() override { return NextionQueueType::TEXT_SENSOR; } + void set_state_from_int(int state_value, bool publish, bool send_to_nextion) override {} + void set_state_from_string(const std::string &state_value, bool publish, bool send_to_nextion) override { + this->set_state(state_value, publish, send_to_nextion); + } +}; +} // namespace nextion +} // namespace esphome diff --git a/esphome/components/nfc/ndef_message.cpp b/esphome/components/nfc/ndef_message.cpp index caba833932..4e295d4469 100644 --- a/esphome/components/nfc/ndef_message.cpp +++ b/esphome/components/nfc/ndef_message.cpp @@ -3,7 +3,7 @@ namespace esphome { namespace nfc { -static const char *TAG = "nfc.ndef_message"; +static const char *const TAG = "nfc.ndef_message"; NdefMessage::NdefMessage(std::vector &data) { ESP_LOGV(TAG, "Building NdefMessage with %zu bytes", data.size()); diff --git a/esphome/components/nfc/ndef_record.cpp b/esphome/components/nfc/ndef_record.cpp index 53fa221e41..a75f5978ec 100644 --- a/esphome/components/nfc/ndef_record.cpp +++ b/esphome/components/nfc/ndef_record.cpp @@ -3,7 +3,7 @@ namespace esphome { namespace nfc { -static const char* TAG = "nfc.ndef_record"; +static const char *const TAG = "nfc.ndef_record"; uint32_t NdefRecord::get_encoded_size() { uint32_t size = 2; diff --git a/esphome/components/nfc/ndef_record.h b/esphome/components/nfc/ndef_record.h index bc03ccf093..2059444882 100644 --- a/esphome/components/nfc/ndef_record.h +++ b/esphome/components/nfc/ndef_record.h @@ -16,42 +16,42 @@ static const uint8_t TNF_UNCHANGED = 0x06; static const uint8_t TNF_RESERVED = 0x07; static const uint8_t PAYLOAD_IDENTIFIERS_COUNT = 0x23; -static const char *PAYLOAD_IDENTIFIERS[] = {"", - "http://www.", - "https://www.", - "http://", - "https://", - "tel:", - "mailto:", - "ftp://anonymous:anonymous@", - "ftp://ftp.", - "ftps://", - "sftp://", - "smb://", - "nfs://", - "ftp://", - "dav://", - "news:", - "telnet://", - "imap:", - "rtsp://", - "urn:", - "pop:", - "sip:", - "sips:", - "tftp:", - "btspp://", - "btl2cap://", - "btgoep://", - "tcpobex://", - "irdaobex://", - "file://", - "urn:epc:id:", - "urn:epc:tag:", - "urn:epc:pat:", - "urn:epc:raw:", - "urn:epc:", - "urn:nfc:"}; +static const char *const PAYLOAD_IDENTIFIERS[] = {"", + "http://www.", + "https://www.", + "http://", + "https://", + "tel:", + "mailto:", + "ftp://anonymous:anonymous@", + "ftp://ftp.", + "ftps://", + "sftp://", + "smb://", + "nfs://", + "ftp://", + "dav://", + "news:", + "telnet://", + "imap:", + "rtsp://", + "urn:", + "pop:", + "sip:", + "sips:", + "tftp:", + "btspp://", + "btl2cap://", + "btgoep://", + "tcpobex://", + "irdaobex://", + "file://", + "urn:epc:id:", + "urn:epc:tag:", + "urn:epc:pat:", + "urn:epc:raw:", + "urn:epc:", + "urn:nfc:"}; class NdefRecord { public: diff --git a/esphome/components/nfc/nfc.cpp b/esphome/components/nfc/nfc.cpp index d1f7cbe15f..01f63e6ec9 100644 --- a/esphome/components/nfc/nfc.cpp +++ b/esphome/components/nfc/nfc.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace nfc { -static const char *TAG = "nfc"; +static const char *const TAG = "nfc"; std::string format_uid(std::vector &uid) { char buf[(uid.size() * 2) + uid.size() - 1]; diff --git a/esphome/components/nfc/nfc.h b/esphome/components/nfc/nfc.h index cf56f9f4de..d482131057 100644 --- a/esphome/components/nfc/nfc.h +++ b/esphome/components/nfc/nfc.h @@ -32,9 +32,9 @@ static const uint8_t MIFARE_CMD_READ = 0x30; static const uint8_t MIFARE_CMD_WRITE = 0xA0; static const uint8_t MIFARE_CMD_WRITE_ULTRALIGHT = 0xA2; -static const char *MIFARE_CLASSIC = "Mifare Classic"; -static const char *NFC_FORUM_TYPE_2 = "NFC Forum Type 2"; -static const char *ERROR = "Error"; +static const char *const MIFARE_CLASSIC = "Mifare Classic"; +static const char *const NFC_FORUM_TYPE_2 = "NFC Forum Type 2"; +static const char *const ERROR = "Error"; static const uint8_t DEFAULT_KEY[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; static const uint8_t NDEF_KEY[6] = {0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7}; diff --git a/esphome/components/nfc/nfc_tag.cpp b/esphome/components/nfc/nfc_tag.cpp index 39e84e2032..c5c15b00ec 100644 --- a/esphome/components/nfc/nfc_tag.cpp +++ b/esphome/components/nfc/nfc_tag.cpp @@ -3,7 +3,7 @@ namespace esphome { namespace nfc { -static const char *TAG = "nfc.tag"; +static const char *const TAG = "nfc.tag"; } // namespace nfc } // namespace esphome diff --git a/esphome/components/ntc/ntc.cpp b/esphome/components/ntc/ntc.cpp index 9446508b0b..80a11384b9 100644 --- a/esphome/components/ntc/ntc.cpp +++ b/esphome/components/ntc/ntc.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace ntc { -static const char *TAG = "ntc"; +static const char *const TAG = "ntc"; void NTC::setup() { this->sensor_->add_on_state_callback([this](float value) { this->process_(value); }); diff --git a/esphome/components/number/__init__.py b/esphome/components/number/__init__.py new file mode 100644 index 0000000000..bf95cb1b31 --- /dev/null +++ b/esphome/components/number/__init__.py @@ -0,0 +1,169 @@ +from typing import Optional +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import automation +from esphome.components import mqtt +from esphome.const import ( + CONF_ABOVE, + CONF_BELOW, + CONF_ICON, + CONF_ID, + CONF_INTERNAL, + CONF_ON_VALUE, + CONF_ON_VALUE_RANGE, + CONF_TRIGGER_ID, + CONF_NAME, + CONF_MQTT_ID, + CONF_VALUE, + ICON_EMPTY, +) +from esphome.core import CORE, coroutine_with_priority + +CODEOWNERS = ["@esphome/core"] +IS_PLATFORM_COMPONENT = True + +number_ns = cg.esphome_ns.namespace("number") +Number = number_ns.class_("Number", cg.Nameable) +NumberPtr = Number.operator("ptr") + +# Triggers +NumberStateTrigger = number_ns.class_( + "NumberStateTrigger", automation.Trigger.template(cg.float_) +) +ValueRangeTrigger = number_ns.class_( + "ValueRangeTrigger", automation.Trigger.template(cg.float_), cg.Component +) + +# Actions +NumberSetAction = number_ns.class_("NumberSetAction", automation.Action) + +# Conditions +NumberInRangeCondition = number_ns.class_( + "NumberInRangeCondition", automation.Condition +) + +icon = cv.icon + + +NUMBER_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend( + { + cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTNumberComponent), + cv.GenerateID(): cv.declare_id(Number), + cv.Optional(CONF_ICON, default=ICON_EMPTY): icon, + cv.Optional(CONF_ON_VALUE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(NumberStateTrigger), + } + ), + cv.Optional(CONF_ON_VALUE_RANGE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ValueRangeTrigger), + cv.Optional(CONF_ABOVE): cv.float_, + cv.Optional(CONF_BELOW): cv.float_, + }, + cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW), + ), + } +) + + +async def setup_number_core_( + var, config, *, min_value: float, max_value: float, step: Optional[float] +): + cg.add(var.set_name(config[CONF_NAME])) + if CONF_INTERNAL in config: + cg.add(var.set_internal(config[CONF_INTERNAL])) + + cg.add(var.traits.set_icon(config[CONF_ICON])) + cg.add(var.traits.set_min_value(min_value)) + cg.add(var.traits.set_max_value(max_value)) + if step is not None: + cg.add(var.traits.set_step(step)) + + for conf in config.get(CONF_ON_VALUE, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [(float, "x")], conf) + for conf in config.get(CONF_ON_VALUE_RANGE, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await cg.register_component(trigger, conf) + if CONF_ABOVE in conf: + template_ = await cg.templatable(conf[CONF_ABOVE], [(float, "x")], float) + cg.add(trigger.set_min(template_)) + if CONF_BELOW in conf: + template_ = await cg.templatable(conf[CONF_BELOW], [(float, "x")], float) + cg.add(trigger.set_max(template_)) + await automation.build_automation(trigger, [(float, "x")], conf) + + if CONF_MQTT_ID in config: + mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) + await mqtt.register_mqtt_component(mqtt_, config) + + +async def register_number( + var, config, *, min_value: float, max_value: float, step: Optional[float] = None +): + if not CORE.has_id(config[CONF_ID]): + var = cg.Pvariable(config[CONF_ID], var) + cg.add(cg.App.register_number(var)) + await setup_number_core_( + var, config, min_value=min_value, max_value=max_value, step=step + ) + + +async def new_number( + config, *, min_value: float, max_value: float, step: Optional[float] = None +): + var = cg.new_Pvariable(config[CONF_ID]) + await register_number( + var, config, min_value=min_value, max_value=max_value, step=step + ) + return var + + +NUMBER_IN_RANGE_CONDITION_SCHEMA = cv.All( + { + cv.Required(CONF_ID): cv.use_id(Number), + cv.Optional(CONF_ABOVE): cv.float_, + cv.Optional(CONF_BELOW): cv.float_, + }, + cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW), +) + + +@automation.register_condition( + "number.in_range", NumberInRangeCondition, NUMBER_IN_RANGE_CONDITION_SCHEMA +) +async def number_in_range_to_code(config, condition_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(condition_id, template_arg, paren) + + if CONF_ABOVE in config: + cg.add(var.set_min(config[CONF_ABOVE])) + if CONF_BELOW in config: + cg.add(var.set_max(config[CONF_BELOW])) + + return var + + +@coroutine_with_priority(40.0) +async def to_code(config): + cg.add_define("USE_NUMBER") + cg.add_global(number_ns.using) + + +@automation.register_action( + "number.set", + NumberSetAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(Number), + cv.Required(CONF_VALUE): cv.templatable(cv.float_), + } + ), +) +async def number_set_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + template_ = await cg.templatable(config[CONF_VALUE], args, float) + cg.add(var.set_value(template_)) + return var diff --git a/esphome/components/number/automation.cpp b/esphome/components/number/automation.cpp new file mode 100644 index 0000000000..a0b169427f --- /dev/null +++ b/esphome/components/number/automation.cpp @@ -0,0 +1,47 @@ +#include "automation.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace number { + +static const char *const TAG = "number.automation"; + +void ValueRangeTrigger::setup() { + this->rtc_ = global_preferences.make_preference(this->parent_->get_object_id_hash()); + bool initial_state; + if (this->rtc_.load(&initial_state)) { + this->previous_in_range_ = initial_state; + } + + this->parent_->add_on_state_callback([this](float state) { this->on_state_(state); }); +} +float ValueRangeTrigger::get_setup_priority() const { return setup_priority::HARDWARE; } + +void ValueRangeTrigger::on_state_(float state) { + if (isnan(state)) + return; + + float local_min = this->min_.value(state); + float local_max = this->max_.value(state); + + bool in_range; + if (isnan(local_min) && isnan(local_max)) { + in_range = this->previous_in_range_; + } else if (isnan(local_min)) { + in_range = state <= local_max; + } else if (isnan(local_max)) { + in_range = state >= local_min; + } else { + in_range = local_min <= state && state <= local_max; + } + + if (in_range != this->previous_in_range_ && in_range) { + this->trigger(state); + } + + this->previous_in_range_ = in_range; + this->rtc_.save(&in_range); +} + +} // namespace number +} // namespace esphome diff --git a/esphome/components/number/automation.h b/esphome/components/number/automation.h new file mode 100644 index 0000000000..9e812f8c49 --- /dev/null +++ b/esphome/components/number/automation.h @@ -0,0 +1,76 @@ +#pragma once + +#include "esphome/components/number/number.h" +#include "esphome/core/automation.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace number { + +class NumberStateTrigger : public Trigger { + public: + explicit NumberStateTrigger(Number *parent) { + parent->add_on_state_callback([this](float value) { this->trigger(value); }); + } +}; + +template class NumberSetAction : public Action { + public: + NumberSetAction(Number *number) : number_(number) {} + TEMPLATABLE_VALUE(float, value) + + void play(Ts... x) override { + auto call = this->number_->make_call(); + call.set_value(this->value_.value(x...)); + call.perform(); + } + + protected: + Number *number_; +}; + +class ValueRangeTrigger : public Trigger, public Component { + public: + explicit ValueRangeTrigger(Number *parent) : parent_(parent) {} + + template void set_min(V min) { this->min_ = min; } + template void set_max(V max) { this->max_ = max; } + + void setup() override; + float get_setup_priority() const override; + + protected: + void on_state_(float state); + + Number *parent_; + ESPPreferenceObject rtc_; + bool previous_in_range_{false}; + TemplatableValue min_{NAN}; + TemplatableValue max_{NAN}; +}; + +template class NumberInRangeCondition : public Condition { + public: + NumberInRangeCondition(Number *parent) : parent_(parent) {} + + void set_min(float min) { this->min_ = min; } + void set_max(float max) { this->max_ = max; } + bool check(Ts... x) override { + const float state = this->parent_->state; + if (isnan(this->min_)) { + return state <= this->max_; + } else if (isnan(this->max_)) { + return state >= this->min_; + } else { + return this->min_ <= state && state <= this->max_; + } + } + + protected: + Number *parent_; + float min_{NAN}; + float max_{NAN}; +}; + +} // namespace number +} // namespace esphome diff --git a/esphome/components/number/number.cpp b/esphome/components/number/number.cpp new file mode 100644 index 0000000000..dbc1c88a5d --- /dev/null +++ b/esphome/components/number/number.cpp @@ -0,0 +1,47 @@ +#include "number.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace number { + +static const char *const TAG = "number"; + +void NumberCall::perform() { + ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str()); + if (!this->value_.has_value() || isnan(*this->value_)) { + ESP_LOGW(TAG, "No value set for NumberCall"); + return; + } + + const auto &traits = this->parent_->traits; + auto value = *this->value_; + + float min_value = traits.get_min_value(); + if (value < min_value) { + ESP_LOGW(TAG, " Value %f must not be less than minimum %f", value, min_value); + return; + } + float max_value = traits.get_max_value(); + if (value > max_value) { + ESP_LOGW(TAG, " Value %f must not be greater than maximum %f", value, max_value); + return; + } + ESP_LOGD(TAG, " Value: %f", *this->value_); + this->parent_->control(*this->value_); +} + +void Number::publish_state(float state) { + this->has_state_ = true; + this->state = state; + ESP_LOGD(TAG, "'%s': Sending state %f", this->get_name().c_str(), state); + this->state_callback_.call(state); +} + +void Number::add_on_state_callback(std::function &&callback) { + this->state_callback_.add(std::move(callback)); +} + +uint32_t Number::hash_base() { return 2282307003UL; } + +} // namespace number +} // namespace esphome diff --git a/esphome/components/number/number.h b/esphome/components/number/number.h new file mode 100644 index 0000000000..e32b53187b --- /dev/null +++ b/esphome/components/number/number.h @@ -0,0 +1,91 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/helpers.h" + +namespace esphome { +namespace number { + +#define LOG_NUMBER(prefix, type, obj) \ + if ((obj) != nullptr) { \ + ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \ + if (!(obj)->traits.get_icon().empty()) { \ + ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->traits.get_icon().c_str()); \ + } \ + } + +class Number; + +class NumberCall { + public: + explicit NumberCall(Number *parent) : parent_(parent) {} + void perform(); + + NumberCall &set_value(float value) { + value_ = value; + return *this; + } + const optional &get_value() const { return value_; } + + protected: + Number *const parent_; + optional value_; +}; + +class NumberTraits { + public: + void set_min_value(float min_value) { min_value_ = min_value; } + float get_min_value() const { return min_value_; } + void set_max_value(float max_value) { max_value_ = max_value; } + float get_max_value() const { return max_value_; } + void set_step(float step) { step_ = step; } + float get_step() const { return step_; } + void set_icon(std::string icon) { icon_ = std::move(icon); } + const std::string &get_icon() const { return icon_; } + + protected: + float min_value_ = NAN; + float max_value_ = NAN; + float step_ = NAN; + std::string icon_; +}; + +/** Base-class for all numbers. + * + * A number can use publish_state to send out a new value. + */ +class Number : public Nameable { + public: + float state; + + void publish_state(float state); + + NumberCall make_call() { return NumberCall(this); } + void set(float value) { make_call().set_value(value).perform(); } + + void add_on_state_callback(std::function &&callback); + + NumberTraits traits; + + /// Return whether this number has gotten a full state yet. + bool has_state() const { return has_state_; } + + protected: + friend class NumberCall; + + /** Set the value of the number, this is a virtual method that each number integration must implement. + * + * This method is called by the NumberCall. + * + * @param value The value as validated by the NumberCall. + */ + virtual void control(float value) = 0; + + uint32_t hash_base() override; + + CallbackManager state_callback_; + bool has_state_{false}; +}; + +} // namespace number +} // namespace esphome diff --git a/esphome/components/ota/__init__.py b/esphome/components/ota/__init__.py index 7ee7ef47ca..75641ad399 100644 --- a/esphome/components/ota/__init__.py +++ b/esphome/components/ota/__init__.py @@ -1,6 +1,7 @@ from esphome.cpp_generator import RawExpression import esphome.codegen as cg import esphome.config_validation as cv +from esphome import automation from esphome.const import ( CONF_ID, CONF_NUM_ATTEMPTS, @@ -8,14 +9,29 @@ from esphome.const import ( CONF_PORT, CONF_REBOOT_TIMEOUT, CONF_SAFE_MODE, + CONF_TRIGGER_ID, ) from esphome.core import CORE, coroutine_with_priority CODEOWNERS = ["@esphome/core"] DEPENDENCIES = ["network"] +CONF_ON_STATE_CHANGE = "on_state_change" +CONF_ON_BEGIN = "on_begin" +CONF_ON_PROGRESS = "on_progress" +CONF_ON_END = "on_end" +CONF_ON_ERROR = "on_error" + ota_ns = cg.esphome_ns.namespace("ota") +OTAState = ota_ns.enum("OTAState") OTAComponent = ota_ns.class_("OTAComponent", cg.Component) +OTAStateChangeTrigger = ota_ns.class_( + "OTAStateChangeTrigger", automation.Trigger.template() +) +OTAStartTrigger = ota_ns.class_("OTAStartTrigger", automation.Trigger.template()) +OTAProgressTrigger = ota_ns.class_("OTAProgressTrigger", automation.Trigger.template()) +OTAEndTrigger = ota_ns.class_("OTAEndTrigger", automation.Trigger.template()) +OTAErrorTrigger = ota_ns.class_("OTAErrorTrigger", automation.Trigger.template()) CONFIG_SCHEMA = cv.Schema( { @@ -27,6 +43,31 @@ CONFIG_SCHEMA = cv.Schema( CONF_REBOOT_TIMEOUT, default="5min" ): cv.positive_time_period_milliseconds, cv.Optional(CONF_NUM_ATTEMPTS, default="10"): cv.positive_not_null_int, + cv.Optional(CONF_ON_STATE_CHANGE): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAStateChangeTrigger), + } + ), + cv.Optional(CONF_ON_BEGIN): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAStartTrigger), + } + ), + cv.Optional(CONF_ON_ERROR): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAErrorTrigger), + } + ), + cv.Optional(CONF_ON_PROGRESS): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAProgressTrigger), + } + ), + cv.Optional(CONF_ON_END): automation.validate_automation( + { + cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(OTAEndTrigger), + } + ), } ).extend(cv.COMPONENT_SCHEMA) @@ -49,3 +90,27 @@ async def to_code(config): cg.add_library("Update", None) elif CORE.is_esp32: cg.add_library("Hash", None) + + use_state_callback = False + for conf in config.get(CONF_ON_STATE_CHANGE, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [(OTAState, "state")], conf) + use_state_callback = True + for conf in config.get(CONF_ON_BEGIN, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + use_state_callback = True + for conf in config.get(CONF_ON_PROGRESS, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [(float, "x")], conf) + use_state_callback = True + for conf in config.get(CONF_ON_END, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [], conf) + use_state_callback = True + for conf in config.get(CONF_ON_ERROR, []): + trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) + await automation.build_automation(trigger, [(int, "x")], conf) + use_state_callback = True + if use_state_callback: + cg.add_define("USE_OTA_STATE_CALLBACK") diff --git a/esphome/components/ota/automation.h b/esphome/components/ota/automation.h new file mode 100644 index 0000000000..6c8aca3705 --- /dev/null +++ b/esphome/components/ota/automation.h @@ -0,0 +1,71 @@ +#pragma once + +#include "esphome/core/defines.h" +#ifdef USE_OTA_STATE_CALLBACK + +#include "esphome/core/component.h" +#include "esphome/core/automation.h" +#include "esphome/components/ota/ota_component.h" + +namespace esphome { +namespace ota { + +class OTAStateChangeTrigger : public Trigger { + public: + explicit OTAStateChangeTrigger(OTAComponent *parent) { + parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { + if (!parent->is_failed()) { + return trigger(state); + } + }); + } +}; + +class OTAStartTrigger : public Trigger<> { + public: + explicit OTAStartTrigger(OTAComponent *parent) { + parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { + if (state == OTA_STARTED && !parent->is_failed()) { + trigger(); + } + }); + } +}; + +class OTAProgressTrigger : public Trigger { + public: + explicit OTAProgressTrigger(OTAComponent *parent) { + parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { + if (state == OTA_IN_PROGRESS && !parent->is_failed()) { + trigger(progress); + } + }); + } +}; + +class OTAEndTrigger : public Trigger<> { + public: + explicit OTAEndTrigger(OTAComponent *parent) { + parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { + if (state == OTA_COMPLETED && !parent->is_failed()) { + trigger(); + } + }); + } +}; + +class OTAErrorTrigger : public Trigger { + public: + explicit OTAErrorTrigger(OTAComponent *parent) { + parent->add_on_state_callback([this, parent](OTAState state, float progress, uint8_t error) { + if (state == OTA_ERROR && !parent->is_failed()) { + trigger(error); + } + }); + } +}; + +} // namespace ota +} // namespace esphome + +#endif // USE_OTA_STATE_CALLBACK diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index c8221d1bdf..71f8101704 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -1,7 +1,6 @@ #include "ota_component.h" #include "esphome/core/log.h" -#include "esphome/core/helpers.h" #include "esphome/core/application.h" #include "esphome/core/util.h" @@ -15,9 +14,9 @@ namespace esphome { namespace ota { -static const char *TAG = "ota"; +static const char *const TAG = "ota"; -uint8_t OTA_VERSION_1_0 = 1; +static const uint8_t OTA_VERSION_1_0 = 1; void OTAComponent::setup() { this->server_ = new WiFiServer(this->port_); @@ -25,6 +24,7 @@ void OTAComponent::setup() { this->dump_config(); } + void OTAComponent::dump_config() { ESP_LOGCONFIG(TAG, "Over-The-Air Updates:"); ESP_LOGCONFIG(TAG, " Address: %s:%u", network_get_address().c_str(), this->port_); @@ -71,6 +71,9 @@ void OTAComponent::handle_() { ESP_LOGD(TAG, "Starting OTA Update from %s...", this->client_.remoteIP().toString().c_str()); this->status_set_warning(); +#ifdef USE_OTA_STATE_CALLBACK + this->state_callback_.call(OTA_STARTED, 0.0f, 0); +#endif if (!this->wait_receive_(buf, 5)) { ESP_LOGW(TAG, "Reading magic bytes failed!"); @@ -241,6 +244,9 @@ void OTAComponent::handle_() { last_progress = now; float percentage = (total * 100.0f) / ota_size; ESP_LOGD(TAG, "OTA in progress: %0.1f%%", percentage); +#ifdef USE_OTA_STATE_CALLBACK + this->state_callback_.call(OTA_IN_PROGRESS, percentage, 0); +#endif // slow down OTA update to avoid getting killed by task watchdog (task_wdt) delay(10); } @@ -268,6 +274,9 @@ void OTAComponent::handle_() { delay(10); ESP_LOGI(TAG, "OTA update finished!"); this->status_clear_warning(); +#ifdef USE_OTA_STATE_CALLBACK + this->state_callback_.call(OTA_COMPLETED, 100.0f, 0); +#endif delay(100); // NOLINT App.safe_reboot(); @@ -296,6 +305,9 @@ error: #endif this->status_momentary_error("onerror", 5000); +#ifdef USE_OTA_STATE_CALLBACK + this->state_callback_.call(OTA_ERROR, 0.0f, static_cast(error_code)); +#endif #ifdef ARDUINO_ARCH_ESP8266 global_preferences.prevent_write(false); @@ -400,5 +412,11 @@ void OTAComponent::on_safe_shutdown() { this->clean_rtc(); } +#ifdef USE_OTA_STATE_CALLBACK +void OTAComponent::add_on_state_callback(std::function &&callback) { + this->state_callback_.add(std::move(callback)); +} +#endif + } // namespace ota } // namespace esphome diff --git a/esphome/components/ota/ota_component.h b/esphome/components/ota/ota_component.h index f16725e324..8b5830295e 100644 --- a/esphome/components/ota/ota_component.h +++ b/esphome/components/ota/ota_component.h @@ -2,6 +2,7 @@ #include "esphome/core/component.h" #include "esphome/core/preferences.h" +#include "esphome/core/helpers.h" #include #include @@ -32,6 +33,8 @@ enum OTAResponseTypes { OTA_RESPONSE_ERROR_UNKNOWN = 255, }; +enum OTAState { OTA_COMPLETED = 0, OTA_STARTED, OTA_IN_PROGRESS, OTA_ERROR }; + /// OTAComponent provides a simple way to integrate Over-the-Air updates into your app using ArduinoOTA. class OTAComponent : public Component { public: @@ -49,6 +52,10 @@ class OTAComponent : public Component { bool should_enter_safe_mode(uint8_t num_attempts, uint32_t enable_time); +#ifdef USE_OTA_STATE_CALLBACK + void add_on_state_callback(std::function &&callback); +#endif + // ========== INTERNAL METHODS ========== // (In most use cases you won't need these) void setup() override; @@ -82,6 +89,10 @@ class OTAComponent : public Component { uint32_t safe_mode_rtc_value_; uint8_t safe_mode_num_attempts_; ESPPreferenceObject rtc_; + +#ifdef USE_OTA_STATE_CALLBACK + CallbackManager state_callback_{}; +#endif }; } // namespace ota diff --git a/esphome/components/output/automation.cpp b/esphome/components/output/automation.cpp index 57d68dacf7..5533a6bee4 100644 --- a/esphome/components/output/automation.cpp +++ b/esphome/components/output/automation.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace output { -static const char *TAG = "output.automation"; +static const char *const TAG = "output.automation"; } // namespace output } // namespace esphome diff --git a/esphome/components/output/float_output.cpp b/esphome/components/output/float_output.cpp index d9f2db559c..99ba798cb9 100644 --- a/esphome/components/output/float_output.cpp +++ b/esphome/components/output/float_output.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace output { -static const char *TAG = "output.float"; +static const char *const TAG = "output.float"; void FloatOutput::set_max_power(float max_power) { this->max_power_ = clamp(max_power, this->min_power_, 1.0f); // Clamp to MIN>=MAX>=1.0 @@ -31,6 +31,10 @@ void FloatOutput::set_level(float state) { #endif if (this->is_inverted()) state = 1.0f - state; + if (state == 0.0f) { // regardless of min_power_, 0.0 means off + this->write_state(state); + return; + } float adjusted_value = (state * (this->max_power_ - this->min_power_)) + this->min_power_; this->write_state(adjusted_value); } diff --git a/esphome/components/output/switch/output_switch.cpp b/esphome/components/output/switch/output_switch.cpp index e2c3b19f28..8db45f3a2b 100644 --- a/esphome/components/output/switch/output_switch.cpp +++ b/esphome/components/output/switch/output_switch.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace output { -static const char *TAG = "output.switch"; +static const char *const TAG = "output.switch"; void OutputSwitch::dump_config() { LOG_SWITCH("", "Output Switch", this); } void OutputSwitch::setup() { diff --git a/esphome/components/partition/light_partition.cpp b/esphome/components/partition/light_partition.cpp index 4bef9ba196..63c0d0186e 100644 --- a/esphome/components/partition/light_partition.cpp +++ b/esphome/components/partition/light_partition.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace partition { -static const char *TAG = "partition.light"; +static const char *const TAG = "partition.light"; } // namespace partition } // namespace esphome diff --git a/esphome/components/partition/light_partition.h b/esphome/components/partition/light_partition.h index 11589ee539..687fe562d1 100644 --- a/esphome/components/partition/light_partition.h +++ b/esphome/components/partition/light_partition.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "esphome/core/component.h" #include "esphome/components/light/addressable_light.h" @@ -31,7 +33,7 @@ class AddressableSegment { class PartitionLightOutput : public light::AddressableLight { public: - explicit PartitionLightOutput(std::vector segments) : segments_(segments) { + explicit PartitionLightOutput(std::vector segments) : segments_(std::move(segments)) { int32_t off = 0; for (auto &seg : this->segments_) { seg.set_dst_offset(off); diff --git a/esphome/components/pca9685/pca9685_output.cpp b/esphome/components/pca9685/pca9685_output.cpp index 72f5687bb9..c3fd2b60e4 100644 --- a/esphome/components/pca9685/pca9685_output.cpp +++ b/esphome/components/pca9685/pca9685_output.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace pca9685 { -static const char *TAG = "pca9685"; +static const char *const TAG = "pca9685"; const uint8_t PCA9685_MODE_INVERTED = 0x10; const uint8_t PCA9685_MODE_OUTPUT_ONACK = 0x08; diff --git a/esphome/components/pcd8544/pcd_8544.cpp b/esphome/components/pcd8544/pcd_8544.cpp index 85614874ee..5651d60b15 100644 --- a/esphome/components/pcd8544/pcd_8544.cpp +++ b/esphome/components/pcd8544/pcd_8544.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace pcd8544 { -static const char *TAG = "pcd_8544"; +static const char *const TAG = "pcd_8544"; void PCD8544::setup_pins_() { this->spi_setup(); diff --git a/esphome/components/pcf8574/__init__.py b/esphome/components/pcf8574/__init__.py index e96c526cb0..52e38febaa 100644 --- a/esphome/components/pcf8574/__init__.py +++ b/esphome/components/pcf8574/__init__.py @@ -38,21 +38,13 @@ async def to_code(config): cg.add(var.set_pcf8575(config[CONF_PCF8575])) -def validate_pcf8574_gpio_mode(value): - value = cv.string(value) - if value.upper() == "INPUT_PULLUP": - raise cv.Invalid( - "INPUT_PULLUP mode has been removed in 1.14 and been combined into " - "INPUT mode (they were the same thing). Please use INPUT instead." - ) - return cv.enum(PCF8674_GPIO_MODES, upper=True)(value) - - PCF8574_OUTPUT_PIN_SCHEMA = cv.Schema( { cv.Required(CONF_PCF8574): cv.use_id(PCF8574Component), cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="OUTPUT"): validate_pcf8574_gpio_mode, + cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum( + PCF8674_GPIO_MODES, upper=True + ), cv.Optional(CONF_INVERTED, default=False): cv.boolean, } ) @@ -60,7 +52,9 @@ PCF8574_INPUT_PIN_SCHEMA = cv.Schema( { cv.Required(CONF_PCF8574): cv.use_id(PCF8574Component), cv.Required(CONF_NUMBER): cv.int_, - cv.Optional(CONF_MODE, default="INPUT"): validate_pcf8574_gpio_mode, + cv.Optional(CONF_MODE, default="INPUT"): cv.enum( + PCF8674_GPIO_MODES, upper=True + ), cv.Optional(CONF_INVERTED, default=False): cv.boolean, } ) diff --git a/esphome/components/pcf8574/pcf8574.cpp b/esphome/components/pcf8574/pcf8574.cpp index 6cadf565de..02817df888 100644 --- a/esphome/components/pcf8574/pcf8574.cpp +++ b/esphome/components/pcf8574/pcf8574.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace pcf8574 { -static const char *TAG = "pcf8574"; +static const char *const TAG = "pcf8574"; void PCF8574Component::setup() { ESP_LOGCONFIG(TAG, "Setting up PCF8574..."); diff --git a/esphome/components/pid/pid_autotuner.cpp b/esphome/components/pid/pid_autotuner.cpp index e8b006b8d7..83e4b6be32 100644 --- a/esphome/components/pid/pid_autotuner.cpp +++ b/esphome/components/pid/pid_autotuner.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace pid { -static const char *TAG = "pid.autotune"; +static const char *const TAG = "pid.autotune"; /* * # PID Autotuner diff --git a/esphome/components/pid/pid_climate.cpp b/esphome/components/pid/pid_climate.cpp index b4660feb32..dac4426698 100644 --- a/esphome/components/pid/pid_climate.cpp +++ b/esphome/components/pid/pid_climate.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace pid { -static const char *TAG = "pid.climate"; +static const char *const TAG = "pid.climate"; void PIDClimate::setup() { this->sensor_->add_on_state_callback([this](float state) { @@ -20,7 +20,12 @@ void PIDClimate::setup() { restore->to_call(this).perform(); } else { // restore from defaults, change_away handles those for us - this->mode = climate::CLIMATE_MODE_HEAT_COOL; + if (supports_heat_() && supports_cool_()) + this->mode = climate::CLIMATE_MODE_HEAT_COOL; + else if (supports_cool_()) + this->mode = climate::CLIMATE_MODE_COOL; + else if (supports_heat_()) + this->mode = climate::CLIMATE_MODE_HEAT; this->target_temperature = this->default_target_temperature_; } } @@ -41,11 +46,13 @@ climate::ClimateTraits PIDClimate::traits() { traits.set_supports_current_temperature(true); traits.set_supports_two_point_target_temperature(false); - traits.set_supported_modes({climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_HEAT_COOL}); + traits.set_supported_modes({climate::CLIMATE_MODE_OFF}); if (supports_cool_()) traits.add_supported_mode(climate::CLIMATE_MODE_COOL); if (supports_heat_()) traits.add_supported_mode(climate::CLIMATE_MODE_HEAT); + if (supports_heat_() && supports_cool_()) + traits.add_supported_mode(climate::CLIMATE_MODE_HEAT_COOL); traits.set_supports_action(true); return traits; @@ -93,13 +100,8 @@ void PIDClimate::write_output_(float value) { } void PIDClimate::handle_non_auto_mode_() { // in non-auto mode, switch directly to appropriate action - // - HEAT mode / COOL mode -> Output at ±100% // - OFF mode -> Output at 0% - if (this->mode == climate::CLIMATE_MODE_HEAT) { - this->write_output_(1.0); - } else if (this->mode == climate::CLIMATE_MODE_COOL) { - this->write_output_(-1.0); - } else if (this->mode == climate::CLIMATE_MODE_OFF) { + if (this->mode == climate::CLIMATE_MODE_OFF) { this->write_output_(0.0); } else { assert(false); @@ -132,7 +134,7 @@ void PIDClimate::update_pid_() { } } - if (this->mode != climate::CLIMATE_MODE_HEAT_COOL) { + if (this->mode == climate::CLIMATE_MODE_OFF) { this->handle_non_auto_mode_(); } else { this->write_output_(value); diff --git a/esphome/components/pid/sensor/pid_climate_sensor.cpp b/esphome/components/pid/sensor/pid_climate_sensor.cpp index f60627b6ac..2a76c775d3 100644 --- a/esphome/components/pid/sensor/pid_climate_sensor.cpp +++ b/esphome/components/pid/sensor/pid_climate_sensor.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace pid { -static const char *TAG = "pid.sensor"; +static const char *const TAG = "pid.sensor"; void PIDClimateSensor::setup() { this->parent_->add_on_pid_computed_callback([this]() { this->update_from_parent_(); }); diff --git a/esphome/components/pmsx003/pmsx003.cpp b/esphome/components/pmsx003/pmsx003.cpp index 489442c637..abea287c0b 100644 --- a/esphome/components/pmsx003/pmsx003.cpp +++ b/esphome/components/pmsx003/pmsx003.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace pmsx003 { -static const char *TAG = "pmsx003"; +static const char *const TAG = "pmsx003"; void PMSX003Component::set_pm_1_0_sensor(sensor::Sensor *pm_1_0_sensor) { pm_1_0_sensor_ = pm_1_0_sensor; } void PMSX003Component::set_pm_2_5_sensor(sensor::Sensor *pm_2_5_sensor) { pm_2_5_sensor_ = pm_2_5_sensor; } diff --git a/esphome/components/pn532/pn532.cpp b/esphome/components/pn532/pn532.cpp index 5e0ec458ab..fc84f30078 100644 --- a/esphome/components/pn532/pn532.cpp +++ b/esphome/components/pn532/pn532.cpp @@ -9,7 +9,7 @@ namespace esphome { namespace pn532 { -static const char *TAG = "pn532"; +static const char *const TAG = "pn532"; void PN532::setup() { ESP_LOGCONFIG(TAG, "Setting up PN532..."); diff --git a/esphome/components/pn532/pn532_mifare_classic.cpp b/esphome/components/pn532/pn532_mifare_classic.cpp index 93d394330d..119d1ceef6 100644 --- a/esphome/components/pn532/pn532_mifare_classic.cpp +++ b/esphome/components/pn532/pn532_mifare_classic.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace pn532 { -static const char *TAG = "pn532.mifare_classic"; +static const char *const TAG = "pn532.mifare_classic"; nfc::NfcTag *PN532::read_mifare_classic_tag_(std::vector &uid) { uint8_t current_block = 4; diff --git a/esphome/components/pn532/pn532_mifare_ultralight.cpp b/esphome/components/pn532/pn532_mifare_ultralight.cpp index 09d94c42b1..1787a3c225 100644 --- a/esphome/components/pn532/pn532_mifare_ultralight.cpp +++ b/esphome/components/pn532/pn532_mifare_ultralight.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace pn532 { -static const char *TAG = "pn532.mifare_ultralight"; +static const char *const TAG = "pn532.mifare_ultralight"; nfc::NfcTag *PN532::read_mifare_ultralight_tag_(std::vector &uid) { if (!this->is_mifare_ultralight_formatted_()) { diff --git a/esphome/components/pn532_i2c/pn532_i2c.cpp b/esphome/components/pn532_i2c/pn532_i2c.cpp index 37147c5eb9..25f24758bf 100644 --- a/esphome/components/pn532_i2c/pn532_i2c.cpp +++ b/esphome/components/pn532_i2c/pn532_i2c.cpp @@ -9,7 +9,7 @@ namespace esphome { namespace pn532_i2c { -static const char *TAG = "pn532_i2c"; +static const char *const TAG = "pn532_i2c"; bool PN532I2C::write_data(const std::vector &data) { return this->write_bytes_raw(data.data(), data.size()); } diff --git a/esphome/components/pn532_spi/pn532_spi.cpp b/esphome/components/pn532_spi/pn532_spi.cpp index 6f87ad8ca7..ec32e45b3d 100644 --- a/esphome/components/pn532_spi/pn532_spi.cpp +++ b/esphome/components/pn532_spi/pn532_spi.cpp @@ -9,7 +9,7 @@ namespace esphome { namespace pn532_spi { -static const char *TAG = "pn532_spi"; +static const char *const TAG = "pn532_spi"; void PN532Spi::setup() { ESP_LOGI(TAG, "PN532Spi setup started!"); diff --git a/esphome/components/power_supply/power_supply.cpp b/esphome/components/power_supply/power_supply.cpp index 1f8471ac9f..f50adac6f9 100644 --- a/esphome/components/power_supply/power_supply.cpp +++ b/esphome/components/power_supply/power_supply.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace power_supply { -static const char *TAG = "power_supply"; +static const char *const TAG = "power_supply"; void PowerSupply::setup() { ESP_LOGCONFIG(TAG, "Setting up Power Supply..."); diff --git a/esphome/components/pulse_counter/pulse_counter_sensor.cpp b/esphome/components/pulse_counter/pulse_counter_sensor.cpp index 46b50a3021..9a0cf568a9 100644 --- a/esphome/components/pulse_counter/pulse_counter_sensor.cpp +++ b/esphome/components/pulse_counter/pulse_counter_sensor.cpp @@ -4,9 +4,9 @@ namespace esphome { namespace pulse_counter { -static const char *TAG = "pulse_counter"; +static const char *const TAG = "pulse_counter"; -const char *EDGE_MODE_TO_STRING[] = {"DISABLE", "INCREMENT", "DECREMENT"}; +const char *const EDGE_MODE_TO_STRING[] = {"DISABLE", "INCREMENT", "DECREMENT"}; #ifdef ARDUINO_ARCH_ESP8266 void ICACHE_RAM_ATTR PulseCounterStorage::gpio_intr(PulseCounterStorage *arg) { diff --git a/esphome/components/pulse_meter/pulse_meter_sensor.cpp b/esphome/components/pulse_meter/pulse_meter_sensor.cpp index 539da3a2aa..1a35deba2f 100644 --- a/esphome/components/pulse_meter/pulse_meter_sensor.cpp +++ b/esphome/components/pulse_meter/pulse_meter_sensor.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace pulse_meter { -static const char *TAG = "pulse_meter"; +static const char *const TAG = "pulse_meter"; void PulseMeterSensor::setup() { this->pin_->setup(); diff --git a/esphome/components/pulse_width/pulse_width.cpp b/esphome/components/pulse_width/pulse_width.cpp index 4be536662b..fb998ef4e1 100644 --- a/esphome/components/pulse_width/pulse_width.cpp +++ b/esphome/components/pulse_width/pulse_width.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace pulse_width { -static const char *TAG = "pulse_width"; +static const char *const TAG = "pulse_width"; void ICACHE_RAM_ATTR PulseWidthSensorStore::gpio_intr(PulseWidthSensorStore *arg) { const bool new_level = arg->pin_->digital_read(); diff --git a/esphome/components/pzem004t/pzem004t.cpp b/esphome/components/pzem004t/pzem004t.cpp index b9deab1949..969ce8fa10 100644 --- a/esphome/components/pzem004t/pzem004t.cpp +++ b/esphome/components/pzem004t/pzem004t.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace pzem004t { -static const char *TAG = "pzem004t"; +static const char *const TAG = "pzem004t"; void PZEM004T::loop() { const uint32_t now = millis(); diff --git a/esphome/components/pzem004t/sensor.py b/esphome/components/pzem004t/sensor.py index e3859f090c..b358b8c650 100644 --- a/esphome/components/pzem004t/sensor.py +++ b/esphome/components/pzem004t/sensor.py @@ -12,8 +12,8 @@ from esphome.const import ( DEVICE_CLASS_POWER, DEVICE_CLASS_VOLTAGE, ICON_EMPTY, + LAST_RESET_TYPE_AUTO, STATE_CLASS_MEASUREMENT, - STATE_CLASS_NONE, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT, @@ -47,7 +47,8 @@ CONFIG_SCHEMA = ( ICON_EMPTY, 0, DEVICE_CLASS_ENERGY, - STATE_CLASS_NONE, + STATE_CLASS_MEASUREMENT, + LAST_RESET_TYPE_AUTO, ), } ) diff --git a/esphome/components/pzemac/pzemac.cpp b/esphome/components/pzemac/pzemac.cpp index c79508d22f..b1a9607304 100644 --- a/esphome/components/pzemac/pzemac.cpp +++ b/esphome/components/pzemac/pzemac.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace pzemac { -static const char *TAG = "pzemac"; +static const char *const TAG = "pzemac"; static const uint8_t PZEM_CMD_READ_IN_REGISTERS = 0x04; static const uint8_t PZEM_REGISTER_COUNT = 10; // 10x 16-bit registers diff --git a/esphome/components/pzemac/sensor.py b/esphome/components/pzemac/sensor.py index 778c5054a0..1dd77a0371 100644 --- a/esphome/components/pzemac/sensor.py +++ b/esphome/components/pzemac/sensor.py @@ -17,8 +17,8 @@ from esphome.const import ( DEVICE_CLASS_ENERGY, ICON_EMPTY, ICON_CURRENT_AC, + LAST_RESET_TYPE_AUTO, STATE_CLASS_MEASUREMENT, - STATE_CLASS_NONE, UNIT_HERTZ, UNIT_VOLT, UNIT_AMPERE, @@ -54,7 +54,8 @@ CONFIG_SCHEMA = ( ICON_EMPTY, 0, DEVICE_CLASS_ENERGY, - STATE_CLASS_NONE, + STATE_CLASS_MEASUREMENT, + LAST_RESET_TYPE_AUTO, ), cv.Optional(CONF_FREQUENCY): sensor.sensor_schema( UNIT_HERTZ, diff --git a/esphome/components/pzemdc/pzemdc.cpp b/esphome/components/pzemdc/pzemdc.cpp index 9bd58410c0..6a31a723a1 100644 --- a/esphome/components/pzemdc/pzemdc.cpp +++ b/esphome/components/pzemdc/pzemdc.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace pzemdc { -static const char *TAG = "pzemdc"; +static const char *const TAG = "pzemdc"; static const uint8_t PZEM_CMD_READ_IN_REGISTERS = 0x04; static const uint8_t PZEM_REGISTER_COUNT = 10; // 10x 16-bit registers diff --git a/esphome/components/qmc5883l/qmc5883l.cpp b/esphome/components/qmc5883l/qmc5883l.cpp index f809f9dfb3..43e7939cf1 100644 --- a/esphome/components/qmc5883l/qmc5883l.cpp +++ b/esphome/components/qmc5883l/qmc5883l.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace qmc5883l { -static const char *TAG = "qmc5883l"; +static const char *const TAG = "qmc5883l"; static const uint8_t QMC5883L_ADDRESS = 0x0D; static const uint8_t QMC5883L_REGISTER_DATA_X_LSB = 0x00; diff --git a/esphome/components/rc522/rc522.cpp b/esphome/components/rc522/rc522.cpp index ced58760ad..789ab6197b 100644 --- a/esphome/components/rc522/rc522.cpp +++ b/esphome/components/rc522/rc522.cpp @@ -9,7 +9,7 @@ namespace rc522 { static const uint8_t WAIT_I_RQ = 0x30; // RxIRq and IdleIRq -static const char *TAG = "rc522"; +static const char *const TAG = "rc522"; static const uint8_t RESET_COUNT = 5; diff --git a/esphome/components/rc522_i2c/rc522_i2c.cpp b/esphome/components/rc522_i2c/rc522_i2c.cpp index fe88f567c0..896e27214a 100644 --- a/esphome/components/rc522_i2c/rc522_i2c.cpp +++ b/esphome/components/rc522_i2c/rc522_i2c.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace rc522_i2c { -static const char *TAG = "rc522_i2c"; +static const char *const TAG = "rc522_i2c"; void RC522I2C::dump_config() { RC522::dump_config(); diff --git a/esphome/components/rc522_spi/rc522_spi.cpp b/esphome/components/rc522_spi/rc522_spi.cpp index 1865b36da6..fe1f6097e2 100644 --- a/esphome/components/rc522_spi/rc522_spi.cpp +++ b/esphome/components/rc522_spi/rc522_spi.cpp @@ -7,7 +7,7 @@ namespace esphome { namespace rc522_spi { -static const char *TAG = "rc522_spi"; +static const char *const TAG = "rc522_spi"; void RC522Spi::setup() { ESP_LOGI(TAG, "SPI Setup"); diff --git a/esphome/components/rdm6300/rdm6300.cpp b/esphome/components/rdm6300/rdm6300.cpp index 2d2ba61824..434b9f5720 100644 --- a/esphome/components/rdm6300/rdm6300.cpp +++ b/esphome/components/rdm6300/rdm6300.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace rdm6300 { -static const char *TAG = "rdm6300"; +static const char *const TAG = "rdm6300"; static const uint8_t RDM6300_START_BYTE = 0x02; static const uint8_t RDM6300_END_BYTE = 0x03; diff --git a/esphome/components/remote_base/__init__.py b/esphome/components/remote_base/__init__.py index 99778c3088..7c27c1b736 100644 --- a/esphome/components/remote_base/__init__.py +++ b/esphome/components/remote_base/__init__.py @@ -176,7 +176,9 @@ validate_binary_sensor = cv.validate_registry_entry( TRIGGER_REGISTRY = SimpleRegistry() DUMPER_REGISTRY = Registry( { - cv.GenerateID(CONF_RECEIVER_ID): cv.use_id(RemoteReceiverBase), + cv.Optional(CONF_RECEIVER_ID): cv.invalid( + "This has been removed in ESPHome 1.20.0 and the dumper attaches directly to the parent receiver." + ), } ) @@ -228,8 +230,6 @@ async def build_dumpers(config): dumpers = [] for conf in config: dumper = await cg.build_registry_entry(DUMPER_REGISTRY, conf) - receiver = await cg.get_variable(conf[CONF_RECEIVER_ID]) - cg.add(receiver.register_dumper(dumper)) dumpers.append(dumper) return dumpers @@ -698,7 +698,7 @@ RC_SWITCH_TRANSMITTER = cv.Schema( } ) -rc_switch_protocols = ns.rc_switch_protocols +rc_switch_protocols = ns.RC_SWITCH_PROTOCOLS RCSwitchData = ns.struct("RCSwitchData") RCSwitchBase = ns.class_("RCSwitchBase") RCSwitchTrigger = ns.class_("RCSwitchTrigger", RemoteReceiverTrigger) @@ -866,7 +866,8 @@ def rc_switch_dumper(var, config): ) = declare_protocol("Samsung") SAMSUNG_SCHEMA = cv.Schema( { - cv.Required(CONF_DATA): cv.hex_uint32_t, + cv.Required(CONF_DATA): cv.hex_uint64_t, + cv.Optional(CONF_NBITS, default=32): cv.int_range(32, 64), } ) @@ -878,6 +879,7 @@ def samsung_binary_sensor(var, config): cg.StructInitializer( SamsungData, ("data", config[CONF_DATA]), + ("nbits", config[CONF_NBITS]), ) ) ) @@ -895,8 +897,10 @@ def samsung_dumper(var, config): @register_action("samsung", SamsungAction, SAMSUNG_SCHEMA) async def samsung_action(var, config, args): - template_ = await cg.templatable(config[CONF_DATA], args, cg.uint32) + template_ = await cg.templatable(config[CONF_DATA], args, cg.uint64) cg.add(var.set_data(template_)) + template_ = await cg.templatable(config[CONF_NBITS], args, cg.uint8) + cg.add(var.set_nbits(template_)) # Samsung36 diff --git a/esphome/components/remote_base/jvc_protocol.cpp b/esphome/components/remote_base/jvc_protocol.cpp index 94d15c2304..f43a28bdc5 100644 --- a/esphome/components/remote_base/jvc_protocol.cpp +++ b/esphome/components/remote_base/jvc_protocol.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace remote_base { -static const char *TAG = "remote.jvc"; +static const char *const TAG = "remote.jvc"; static const uint8_t NBITS = 16; static const uint32_t HEADER_HIGH_US = 8400; diff --git a/esphome/components/remote_base/lg_protocol.cpp b/esphome/components/remote_base/lg_protocol.cpp index c01348ef94..a3e7f9828b 100644 --- a/esphome/components/remote_base/lg_protocol.cpp +++ b/esphome/components/remote_base/lg_protocol.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace remote_base { -static const char *TAG = "remote.lg"; +static const char *const TAG = "remote.lg"; static const uint32_t HEADER_HIGH_US = 8000; static const uint32_t HEADER_LOW_US = 4000; diff --git a/esphome/components/remote_base/nec_protocol.cpp b/esphome/components/remote_base/nec_protocol.cpp index e352d051c0..79a30903a4 100644 --- a/esphome/components/remote_base/nec_protocol.cpp +++ b/esphome/components/remote_base/nec_protocol.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace remote_base { -static const char *TAG = "remote.nec"; +static const char *const TAG = "remote.nec"; static const uint32_t HEADER_HIGH_US = 9000; static const uint32_t HEADER_LOW_US = 4500; diff --git a/esphome/components/remote_base/panasonic_protocol.cpp b/esphome/components/remote_base/panasonic_protocol.cpp index 4a19f8c1fc..fd4f7c4bf7 100644 --- a/esphome/components/remote_base/panasonic_protocol.cpp +++ b/esphome/components/remote_base/panasonic_protocol.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace remote_base { -static const char *TAG = "remote.panasonic"; +static const char *const TAG = "remote.panasonic"; static const uint32_t HEADER_HIGH_US = 3502; static const uint32_t HEADER_LOW_US = 1750; diff --git a/esphome/components/remote_base/pioneer_protocol.cpp b/esphome/components/remote_base/pioneer_protocol.cpp index 49a27e08e7..74a3998f11 100644 --- a/esphome/components/remote_base/pioneer_protocol.cpp +++ b/esphome/components/remote_base/pioneer_protocol.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace remote_base { -static const char *TAG = "remote.pioneer"; +static const char *const TAG = "remote.pioneer"; static const uint32_t HEADER_HIGH_US = 9000; static const uint32_t HEADER_LOW_US = 4500; diff --git a/esphome/components/remote_base/raw_protocol.cpp b/esphome/components/remote_base/raw_protocol.cpp index 62d578caf9..3446dcdbb8 100644 --- a/esphome/components/remote_base/raw_protocol.cpp +++ b/esphome/components/remote_base/raw_protocol.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace remote_base { -static const char *TAG = "remote.raw"; +static const char *const TAG = "remote.raw"; bool RawDumper::dump(RemoteReceiveData src) { char buffer[256]; diff --git a/esphome/components/remote_base/rc5_protocol.cpp b/esphome/components/remote_base/rc5_protocol.cpp index a6165492ac..80af2f9ad8 100644 --- a/esphome/components/remote_base/rc5_protocol.cpp +++ b/esphome/components/remote_base/rc5_protocol.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace remote_base { -static const char *TAG = "remote.rc5"; +static const char *const TAG = "remote.rc5"; static const uint32_t BIT_TIME_US = 889; static const uint8_t NBITS = 14; diff --git a/esphome/components/remote_base/rc_switch_protocol.cpp b/esphome/components/remote_base/rc_switch_protocol.cpp index dc4438a84e..6b7d1b725a 100644 --- a/esphome/components/remote_base/rc_switch_protocol.cpp +++ b/esphome/components/remote_base/rc_switch_protocol.cpp @@ -4,17 +4,17 @@ namespace esphome { namespace remote_base { -static const char *TAG = "remote.rc_switch"; +static const char *const TAG = "remote.rc_switch"; -RCSwitchBase rc_switch_protocols[9] = {RCSwitchBase(0, 0, 0, 0, 0, 0, false), - RCSwitchBase(350, 10850, 350, 1050, 1050, 350, false), - RCSwitchBase(650, 6500, 650, 1300, 1300, 650, false), - RCSwitchBase(3000, 7100, 400, 1100, 900, 600, false), - RCSwitchBase(380, 2280, 380, 1140, 1140, 380, false), - RCSwitchBase(3000, 7000, 500, 1000, 1000, 500, false), - RCSwitchBase(10350, 450, 450, 900, 900, 450, true), - RCSwitchBase(300, 9300, 150, 900, 900, 150, false), - RCSwitchBase(250, 2500, 250, 1250, 250, 250, false)}; +const RCSwitchBase RC_SWITCH_PROTOCOLS[9] = {RCSwitchBase(0, 0, 0, 0, 0, 0, false), + RCSwitchBase(350, 10850, 350, 1050, 1050, 350, false), + RCSwitchBase(650, 6500, 650, 1300, 1300, 650, false), + RCSwitchBase(3000, 7100, 400, 1100, 900, 600, false), + RCSwitchBase(380, 2280, 380, 1140, 1140, 380, false), + RCSwitchBase(3000, 7000, 500, 1000, 1000, 500, false), + RCSwitchBase(10350, 450, 450, 900, 900, 450, true), + RCSwitchBase(300, 9300, 150, 900, 900, 150, false), + RCSwitchBase(250, 2500, 250, 1250, 250, 250, false)}; RCSwitchBase::RCSwitchBase(uint32_t sync_high, uint32_t sync_low, uint32_t zero_high, uint32_t zero_low, uint32_t one_high, uint32_t one_low, bool inverted) @@ -132,7 +132,7 @@ optional RCSwitchBase::decode(RemoteReceiveData &src) const { uint8_t out_nbits; for (uint8_t i = 1; i <= 8; i++) { src.reset(); - RCSwitchBase *protocol = &rc_switch_protocols[i]; + const RCSwitchBase *protocol = &RC_SWITCH_PROTOCOLS[i]; if (protocol->decode(src, &out.code, &out_nbits) && out_nbits >= 3) { out.protocol = i; return out; @@ -246,7 +246,7 @@ bool RCSwitchDumper::dump(RemoteReceiveData src) { src.reset(); uint64_t out_data; uint8_t out_nbits; - RCSwitchBase *protocol = &rc_switch_protocols[i]; + const RCSwitchBase *protocol = &RC_SWITCH_PROTOCOLS[i]; if (protocol->decode(src, &out_data, &out_nbits) && out_nbits >= 3) { char buffer[65]; for (uint8_t j = 0; j < out_nbits; j++) diff --git a/esphome/components/remote_base/rc_switch_protocol.h b/esphome/components/remote_base/rc_switch_protocol.h index 8362899cec..fc465dbd5d 100644 --- a/esphome/components/remote_base/rc_switch_protocol.h +++ b/esphome/components/remote_base/rc_switch_protocol.h @@ -60,7 +60,7 @@ class RCSwitchBase { bool inverted_{}; }; -extern RCSwitchBase rc_switch_protocols[9]; +extern const RCSwitchBase RC_SWITCH_PROTOCOLS[9]; uint64_t decode_binary_string(const std::string &data); diff --git a/esphome/components/remote_base/remote_base.cpp b/esphome/components/remote_base/remote_base.cpp index 04407ad3b1..7198f2d917 100644 --- a/esphome/components/remote_base/remote_base.cpp +++ b/esphome/components/remote_base/remote_base.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace remote_base { -static const char *TAG = "remote_base"; +static const char *const TAG = "remote_base"; #ifdef ARDUINO_ARCH_ESP32 RemoteRMTChannel::RemoteRMTChannel(uint8_t mem_block_num) : mem_block_num_(mem_block_num) { diff --git a/esphome/components/remote_base/remote_base.h b/esphome/components/remote_base/remote_base.h index 916fe29c1f..16c732df83 100644 --- a/esphome/components/remote_base/remote_base.h +++ b/esphome/components/remote_base/remote_base.h @@ -33,7 +33,7 @@ class RemoteTransmitData { const std::vector &get_data() const { return this->data_; } - void set_data(std::vector data) { + void set_data(const std::vector &data) { this->data_.clear(); this->data_.reserve(data.size()); for (auto dat : data) diff --git a/esphome/components/remote_base/samsung36_protocol.cpp b/esphome/components/remote_base/samsung36_protocol.cpp index 22b158fd8a..9ef8ade205 100644 --- a/esphome/components/remote_base/samsung36_protocol.cpp +++ b/esphome/components/remote_base/samsung36_protocol.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace remote_base { -static const char *TAG = "remote.samsung36"; +static const char *const TAG = "remote.samsung36"; static const uint8_t NBITS = 78; diff --git a/esphome/components/remote_base/samsung_protocol.cpp b/esphome/components/remote_base/samsung_protocol.cpp index 25f68ceb97..0f2605d865 100644 --- a/esphome/components/remote_base/samsung_protocol.cpp +++ b/esphome/components/remote_base/samsung_protocol.cpp @@ -4,9 +4,8 @@ namespace esphome { namespace remote_base { -static const char *TAG = "remote.samsung"; +static const char *const TAG = "remote.samsung"; -static const uint8_t NBITS = 32; static const uint32_t HEADER_HIGH_US = 4500; static const uint32_t HEADER_LOW_US = 4500; static const uint32_t BIT_HIGH_US = 560; @@ -17,12 +16,12 @@ static const uint32_t FOOTER_LOW_US = 560; void SamsungProtocol::encode(RemoteTransmitData *dst, const SamsungData &data) { dst->set_carrier_frequency(38000); - dst->reserve(4 + NBITS * 2u); + dst->reserve(4 + data.nbits * 2u); dst->item(HEADER_HIGH_US, HEADER_LOW_US); - for (uint32_t mask = 1UL << (NBITS - 1); mask != 0; mask >>= 1) { - if (data.data & mask) + for (uint8_t bit = data.nbits; bit > 0; bit--) { + if ((data.data >> (bit - 1)) & 1) dst->item(BIT_HIGH_US, BIT_ONE_LOW_US); else dst->item(BIT_HIGH_US, BIT_ZERO_LOW_US); @@ -33,16 +32,20 @@ void SamsungProtocol::encode(RemoteTransmitData *dst, const SamsungData &data) { optional SamsungProtocol::decode(RemoteReceiveData src) { SamsungData out{ .data = 0, + .nbits = 0, }; if (!src.expect_item(HEADER_HIGH_US, HEADER_LOW_US)) return {}; - for (uint8_t i = 0; i < NBITS; i++) { - out.data <<= 1UL; + for (out.nbits = 0; out.nbits < 64; out.nbits++) { if (src.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) { - out.data |= 1UL; + out.data = (out.data << 1) | 1; } else if (src.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) { - out.data |= 0UL; + out.data = (out.data << 1) | 0; + } else if (out.nbits >= 31) { + if (!src.expect_mark(FOOTER_HIGH_US)) + return {}; + return out; } else { return {}; } @@ -52,7 +55,9 @@ optional SamsungProtocol::decode(RemoteReceiveData src) { return {}; return out; } -void SamsungProtocol::dump(const SamsungData &data) { ESP_LOGD(TAG, "Received Samsung: data=0x%08X", data.data); } +void SamsungProtocol::dump(const SamsungData &data) { + ESP_LOGD(TAG, "Received Samsung: data=0x%" PRIX64 ", nbits=%d", data.data, data.nbits); +} } // namespace remote_base } // namespace esphome diff --git a/esphome/components/remote_base/samsung_protocol.h b/esphome/components/remote_base/samsung_protocol.h index f7a54788e5..41434f2889 100644 --- a/esphome/components/remote_base/samsung_protocol.h +++ b/esphome/components/remote_base/samsung_protocol.h @@ -7,9 +7,10 @@ namespace esphome { namespace remote_base { struct SamsungData { - uint32_t data; + uint64_t data; + uint8_t nbits; - bool operator==(const SamsungData &rhs) const { return data == rhs.data; } + bool operator==(const SamsungData &rhs) const { return data == rhs.data && nbits == rhs.nbits; } }; class SamsungProtocol : public RemoteProtocol { @@ -23,11 +24,13 @@ DECLARE_REMOTE_PROTOCOL(Samsung) template class SamsungAction : public RemoteTransmitterActionBase { public: - TEMPLATABLE_VALUE(uint32_t, data) + TEMPLATABLE_VALUE(uint64_t, data) + TEMPLATABLE_VALUE(uint8_t, nbits) void encode(RemoteTransmitData *dst, Ts... x) override { SamsungData data{}; data.data = this->data_.value(x...); + data.nbits = this->nbits_.value(x...); SamsungProtocol().encode(dst, data); } }; diff --git a/esphome/components/remote_base/sony_protocol.cpp b/esphome/components/remote_base/sony_protocol.cpp index 97318f6608..3bb643266a 100644 --- a/esphome/components/remote_base/sony_protocol.cpp +++ b/esphome/components/remote_base/sony_protocol.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace remote_base { -static const char *TAG = "remote.sony"; +static const char *const TAG = "remote.sony"; static const uint32_t HEADER_HIGH_US = 2400; static const uint32_t HEADER_LOW_US = 600; diff --git a/esphome/components/remote_receiver/__init__.py b/esphome/components/remote_receiver/__init__.py index fed52803cb..7158368ed8 100644 --- a/esphome/components/remote_receiver/__init__.py +++ b/esphome/components/remote_receiver/__init__.py @@ -54,7 +54,10 @@ async def to_code(config): else: var = cg.new_Pvariable(config[CONF_ID], pin) - await remote_base.build_dumpers(config[CONF_DUMP]) + dumpers = await remote_base.build_dumpers(config[CONF_DUMP]) + for dumper in dumpers: + cg.add(var.register_dumper(dumper)) + await remote_base.build_triggers(config) await cg.register_component(var, config) diff --git a/esphome/components/remote_receiver/remote_receiver_esp32.cpp b/esphome/components/remote_receiver/remote_receiver_esp32.cpp index 97806f8a52..b2ddc69b1c 100644 --- a/esphome/components/remote_receiver/remote_receiver_esp32.cpp +++ b/esphome/components/remote_receiver/remote_receiver_esp32.cpp @@ -7,7 +7,7 @@ namespace esphome { namespace remote_receiver { -static const char *TAG = "remote_receiver.esp32"; +static const char *const TAG = "remote_receiver.esp32"; void RemoteReceiverComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up Remote Receiver..."); diff --git a/esphome/components/remote_receiver/remote_receiver_esp8266.cpp b/esphome/components/remote_receiver/remote_receiver_esp8266.cpp index cafbc34d69..3fb68caca2 100644 --- a/esphome/components/remote_receiver/remote_receiver_esp8266.cpp +++ b/esphome/components/remote_receiver/remote_receiver_esp8266.cpp @@ -7,7 +7,7 @@ namespace esphome { namespace remote_receiver { -static const char *TAG = "remote_receiver.esp8266"; +static const char *const TAG = "remote_receiver.esp8266"; void ICACHE_RAM_ATTR HOT RemoteReceiverComponentStore::gpio_intr(RemoteReceiverComponentStore *arg) { const uint32_t now = micros(); diff --git a/esphome/components/remote_transmitter/remote_transmitter.cpp b/esphome/components/remote_transmitter/remote_transmitter.cpp index 877b190a1d..425418ff39 100644 --- a/esphome/components/remote_transmitter/remote_transmitter.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace remote_transmitter { -static const char *TAG = "remote_transmitter"; +static const char *const TAG = "remote_transmitter"; } // namespace remote_transmitter } // namespace esphome diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp index d5e4a7b1b8..3d3e26160a 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp32.cpp @@ -7,7 +7,7 @@ namespace esphome { namespace remote_transmitter { -static const char *TAG = "remote_transmitter"; +static const char *const TAG = "remote_transmitter"; void RemoteTransmitterComponent::setup() {} diff --git a/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp b/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp index e8906e87aa..f8735fe763 100644 --- a/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp +++ b/esphome/components/remote_transmitter/remote_transmitter_esp8266.cpp @@ -7,7 +7,7 @@ namespace esphome { namespace remote_transmitter { -static const char *TAG = "remote_transmitter"; +static const char *const TAG = "remote_transmitter"; void RemoteTransmitterComponent::setup() { this->pin_->setup(); diff --git a/esphome/components/remote_transmitter/switch.py b/esphome/components/remote_transmitter/switch.py deleted file mode 100644 index 3a2e43a31a..0000000000 --- a/esphome/components/remote_transmitter/switch.py +++ /dev/null @@ -1,33 +0,0 @@ -import esphome.config_validation as cv -from esphome.components.remote_base import BINARY_SENSOR_REGISTRY -from esphome.util import OrderedDict - - -def show_new(value): - from esphome import yaml_util - - for key in BINARY_SENSOR_REGISTRY: - if key in value: - break - else: - raise cv.Invalid( - "This platform has been removed in 1.13, please see the docs for updated " - "instructions." - ) - - val = value[key] - args = [("platform", "template")] - if "id" in value: - args.append(("id", value["id"])) - if "name" in value: - args.append(("name", value["name"])) - args.append(("turn_on_action", {f"remote_transmitter.transmit_{key}": val})) - - text = yaml_util.dump([OrderedDict(args)]) - raise cv.Invalid( - "This platform has been removed in 1.13, please change to:\n\n{}\n\n." - "".format(text) - ) - - -CONFIG_SCHEMA = show_new diff --git a/esphome/components/resistance/resistance_sensor.cpp b/esphome/components/resistance/resistance_sensor.cpp index 7c48bbbc08..1380354a5f 100644 --- a/esphome/components/resistance/resistance_sensor.cpp +++ b/esphome/components/resistance/resistance_sensor.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace resistance { -static const char *TAG = "resistance"; +static const char *const TAG = "resistance"; void ResistanceSensor::dump_config() { LOG_SENSOR("", "Resistance Sensor", this); diff --git a/esphome/components/restart/restart_switch.cpp b/esphome/components/restart/restart_switch.cpp index f66ebc616e..ea46c5f910 100644 --- a/esphome/components/restart/restart_switch.cpp +++ b/esphome/components/restart/restart_switch.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace restart { -static const char *TAG = "restart"; +static const char *const TAG = "restart"; void RestartSwitch::write_state(bool state) { // Acknowledge diff --git a/esphome/components/rf_bridge/rf_bridge.cpp b/esphome/components/rf_bridge/rf_bridge.cpp index a63523917a..a4259e5aa2 100644 --- a/esphome/components/rf_bridge/rf_bridge.cpp +++ b/esphome/components/rf_bridge/rf_bridge.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace rf_bridge { -static const char *TAG = "rf_bridge"; +static const char *const TAG = "rf_bridge"; void RFBridgeComponent::ack_() { ESP_LOGV(TAG, "Sending ACK"); @@ -114,7 +114,7 @@ bool RFBridgeComponent::parse_bridge_byte_(uint8_t byte) { return false; } -void RFBridgeComponent::write_byte_str_(std::string codes) { +void RFBridgeComponent::write_byte_str_(const std::string &codes) { uint8_t code; int size = codes.length(); for (int i = 0; i < size; i += 2) { @@ -160,7 +160,7 @@ void RFBridgeComponent::send_code(RFBridgeData data) { this->flush(); } -void RFBridgeComponent::send_advanced_code(RFBridgeAdvancedData data) { +void RFBridgeComponent::send_advanced_code(const RFBridgeAdvancedData &data) { ESP_LOGD(TAG, "Sending advanced code: length=0x%02X protocol=0x%02X code=0x%s", data.length, data.protocol, data.code.c_str()); this->write(RF_CODE_START); @@ -209,7 +209,7 @@ void RFBridgeComponent::start_bucket_sniffing() { this->flush(); } -void RFBridgeComponent::send_raw(std::string raw_code) { +void RFBridgeComponent::send_raw(const std::string &raw_code) { ESP_LOGD(TAG, "Sending Raw Code: %s", raw_code.c_str()); this->write_byte_str_(raw_code); diff --git a/esphome/components/rf_bridge/rf_bridge.h b/esphome/components/rf_bridge/rf_bridge.h index 573bb2df3f..2fa4eb05c5 100644 --- a/esphome/components/rf_bridge/rf_bridge.h +++ b/esphome/components/rf_bridge/rf_bridge.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "esphome/core/component.h" #include "esphome/components/uart/uart.h" #include "esphome/core/automation.h" @@ -52,19 +54,19 @@ class RFBridgeComponent : public uart::UARTDevice, public Component { this->advanced_data_callback_.add(std::move(callback)); } void send_code(RFBridgeData data); - void send_advanced_code(RFBridgeAdvancedData data); + void send_advanced_code(const RFBridgeAdvancedData &data); void learn(); void start_advanced_sniffing(); void stop_advanced_sniffing(); void start_bucket_sniffing(); - void send_raw(std::string code); + void send_raw(const std::string &code); void beep(uint16_t ms); protected: void ack_(); void decode_(); bool parse_bridge_byte_(uint8_t byte); - void write_byte_str_(std::string codes); + void write_byte_str_(const std::string &codes); std::vector rx_buffer_; uint32_t last_bridge_byte_{0}; @@ -83,7 +85,8 @@ class RFBridgeReceivedCodeTrigger : public Trigger { class RFBridgeReceivedAdvancedCodeTrigger : public Trigger { public: explicit RFBridgeReceivedAdvancedCodeTrigger(RFBridgeComponent *parent) { - parent->add_on_advanced_code_received_callback([this](RFBridgeAdvancedData data) { this->trigger(data); }); + parent->add_on_advanced_code_received_callback( + [this](RFBridgeAdvancedData data) { this->trigger(std::move(data)); }); } }; diff --git a/esphome/components/rgbww/light.py b/esphome/components/rgbww/light.py index d152fbc6db..41cba1f7a1 100644 --- a/esphome/components/rgbww/light.py +++ b/esphome/components/rgbww/light.py @@ -18,19 +18,22 @@ RGBWWLightOutput = rgbww_ns.class_("RGBWWLightOutput", light.LightOutput) CONF_CONSTANT_BRIGHTNESS = "constant_brightness" CONF_COLOR_INTERLOCK = "color_interlock" -CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend( - { - cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(RGBWWLightOutput), - cv.Required(CONF_RED): cv.use_id(output.FloatOutput), - cv.Required(CONF_GREEN): cv.use_id(output.FloatOutput), - cv.Required(CONF_BLUE): cv.use_id(output.FloatOutput), - cv.Required(CONF_COLD_WHITE): cv.use_id(output.FloatOutput), - cv.Required(CONF_WARM_WHITE): cv.use_id(output.FloatOutput), - cv.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature, - cv.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): cv.color_temperature, - cv.Optional(CONF_CONSTANT_BRIGHTNESS, default=False): cv.boolean, - cv.Optional(CONF_COLOR_INTERLOCK, default=False): cv.boolean, - } +CONFIG_SCHEMA = cv.All( + light.RGB_LIGHT_SCHEMA.extend( + { + cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(RGBWWLightOutput), + cv.Required(CONF_RED): cv.use_id(output.FloatOutput), + cv.Required(CONF_GREEN): cv.use_id(output.FloatOutput), + cv.Required(CONF_BLUE): cv.use_id(output.FloatOutput), + cv.Required(CONF_COLD_WHITE): cv.use_id(output.FloatOutput), + cv.Required(CONF_WARM_WHITE): cv.use_id(output.FloatOutput), + cv.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature, + cv.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): cv.color_temperature, + cv.Optional(CONF_CONSTANT_BRIGHTNESS, default=False): cv.boolean, + cv.Optional(CONF_COLOR_INTERLOCK, default=False): cv.boolean, + } + ), + light.validate_color_temperature_channels, ) diff --git a/esphome/components/rotary_encoder/rotary_encoder.cpp b/esphome/components/rotary_encoder/rotary_encoder.cpp index 0398489dca..8ef6f932c5 100644 --- a/esphome/components/rotary_encoder/rotary_encoder.cpp +++ b/esphome/components/rotary_encoder/rotary_encoder.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace rotary_encoder { -static const char *TAG = "rotary_encoder"; +static const char *const TAG = "rotary_encoder"; // based on https://github.com/jkDesignDE/MechInputs/blob/master/QEIx4.cpp static const uint8_t STATE_LUT_MASK = 0x1C; // clears upper counter increment/decrement bits and pin states diff --git a/esphome/components/rtttl/rtttl.cpp b/esphome/components/rtttl/rtttl.cpp index da50e1cbe9..84cc2ce48c 100644 --- a/esphome/components/rtttl/rtttl.cpp +++ b/esphome/components/rtttl/rtttl.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace rtttl { -static const char* TAG = "rtttl"; +static const char *const TAG = "rtttl"; static const uint32_t DOUBLE_NOTE_GAP_MS = 10; diff --git a/esphome/components/rtttl/rtttl.h b/esphome/components/rtttl/rtttl.h index 76d8241ddf..ec6fe7f98f 100644 --- a/esphome/components/rtttl/rtttl.h +++ b/esphome/components/rtttl/rtttl.h @@ -7,8 +7,6 @@ namespace esphome { namespace rtttl { -extern uint32_t global_rtttl_id; - class Rtttl : public Component { public: void set_output(output::FloatOutput *output) { output_ = output; } diff --git a/esphome/components/ruuvi_ble/ruuvi_ble.cpp b/esphome/components/ruuvi_ble/ruuvi_ble.cpp index 897e4a2504..e3e9f42305 100644 --- a/esphome/components/ruuvi_ble/ruuvi_ble.cpp +++ b/esphome/components/ruuvi_ble/ruuvi_ble.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace ruuvi_ble { -static const char *TAG = "ruuvi_ble"; +static const char *const TAG = "ruuvi_ble"; bool parse_ruuvi_data_byte(const esp32_ble_tracker::adv_data_t &adv_data, RuuviParseResult &result) { const uint8_t data_type = adv_data[0]; diff --git a/esphome/components/ruuvitag/ruuvitag.cpp b/esphome/components/ruuvitag/ruuvitag.cpp index 2963b777d1..f4e4a72270 100644 --- a/esphome/components/ruuvitag/ruuvitag.cpp +++ b/esphome/components/ruuvitag/ruuvitag.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace ruuvitag { -static const char *TAG = "ruuvitag"; +static const char *const TAG = "ruuvitag"; void RuuviTag::dump_config() { ESP_LOGCONFIG(TAG, "RuuviTag"); diff --git a/esphome/components/scd30/scd30.cpp b/esphome/components/scd30/scd30.cpp index e658397e35..3eda98d41d 100644 --- a/esphome/components/scd30/scd30.cpp +++ b/esphome/components/scd30/scd30.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace scd30 { -static const char *TAG = "scd30"; +static const char *const TAG = "scd30"; static const uint16_t SCD30_CMD_GET_FIRMWARE_VERSION = 0xd100; static const uint16_t SCD30_CMD_START_CONTINUOUS_MEASUREMENTS = 0x0010; diff --git a/esphome/components/script/script.cpp b/esphome/components/script/script.cpp index e99931ced6..46bcef905b 100644 --- a/esphome/components/script/script.cpp +++ b/esphome/components/script/script.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace script { -static const char *TAG = "script"; +static const char *const TAG = "script"; void SingleScript::execute() { if (this->is_action_running()) { diff --git a/esphome/components/sdm_meter/sdm_meter.cpp b/esphome/components/sdm_meter/sdm_meter.cpp index de7c42421e..2348c88938 100644 --- a/esphome/components/sdm_meter/sdm_meter.cpp +++ b/esphome/components/sdm_meter/sdm_meter.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace sdm_meter { -static const char *TAG = "sdm_meter"; +static const char *const TAG = "sdm_meter"; static const uint8_t MODBUS_CMD_READ_IN_REGISTERS = 0x04; static const uint8_t MODBUS_REGISTER_COUNT = 80; // 74 x 16-bit registers diff --git a/esphome/components/sdm_meter/sensor.py b/esphome/components/sdm_meter/sensor.py index 39ef280fef..ce560b9d4b 100644 --- a/esphome/components/sdm_meter/sensor.py +++ b/esphome/components/sdm_meter/sensor.py @@ -25,8 +25,8 @@ from esphome.const import ( ICON_CURRENT_AC, ICON_EMPTY, ICON_FLASH, + LAST_RESET_TYPE_AUTO, STATE_CLASS_MEASUREMENT, - STATE_CLASS_NONE, UNIT_AMPERE, UNIT_DEGREES, UNIT_EMPTY, @@ -88,24 +88,36 @@ CONFIG_SCHEMA = ( STATE_CLASS_MEASUREMENT, ), cv.Optional(CONF_IMPORT_ACTIVE_ENERGY): sensor.sensor_schema( - UNIT_WATT_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY, STATE_CLASS_NONE + UNIT_WATT_HOURS, + ICON_EMPTY, + 2, + DEVICE_CLASS_ENERGY, + STATE_CLASS_MEASUREMENT, + LAST_RESET_TYPE_AUTO, ), cv.Optional(CONF_EXPORT_ACTIVE_ENERGY): sensor.sensor_schema( - UNIT_WATT_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY, STATE_CLASS_NONE + UNIT_WATT_HOURS, + ICON_EMPTY, + 2, + DEVICE_CLASS_ENERGY, + STATE_CLASS_MEASUREMENT, + LAST_RESET_TYPE_AUTO, ), cv.Optional(CONF_IMPORT_REACTIVE_ENERGY): sensor.sensor_schema( UNIT_VOLT_AMPS_REACTIVE_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY, - STATE_CLASS_NONE, + STATE_CLASS_MEASUREMENT, + LAST_RESET_TYPE_AUTO, ), cv.Optional(CONF_EXPORT_REACTIVE_ENERGY): sensor.sensor_schema( UNIT_VOLT_AMPS_REACTIVE_HOURS, ICON_EMPTY, 2, DEVICE_CLASS_ENERGY, - STATE_CLASS_NONE, + STATE_CLASS_MEASUREMENT, + LAST_RESET_TYPE_AUTO, ), } ) diff --git a/esphome/components/sds011/sds011.cpp b/esphome/components/sds011/sds011.cpp index ddcbdbb712..0c04ff557f 100644 --- a/esphome/components/sds011/sds011.cpp +++ b/esphome/components/sds011/sds011.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace sds011 { -static const char *TAG = "sds011"; +static const char *const TAG = "sds011"; static const uint8_t SDS011_MSG_REQUEST_LENGTH = 19; static const uint8_t SDS011_MSG_RESPONSE_LENGTH = 10; diff --git a/esphome/components/senseair/senseair.cpp b/esphome/components/senseair/senseair.cpp index 80b67dfa17..8fbb6f69db 100644 --- a/esphome/components/senseair/senseair.cpp +++ b/esphome/components/senseair/senseair.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace senseair { -static const char *TAG = "senseair"; +static const char *const TAG = "senseair"; static const uint8_t SENSEAIR_REQUEST_LENGTH = 8; static const uint8_t SENSEAIR_PPM_STATUS_RESPONSE_LENGTH = 13; static const uint8_t SENSEAIR_ABC_PERIOD_RESPONSE_LENGTH = 7; diff --git a/esphome/components/sensor/__init__.py b/esphome/components/sensor/__init__.py index 89bde9476a..0a0c3a9214 100644 --- a/esphome/components/sensor/__init__.py +++ b/esphome/components/sensor/__init__.py @@ -17,6 +17,7 @@ from esphome.const import ( CONF_ICON, CONF_ID, CONF_INTERNAL, + CONF_LAST_RESET_TYPE, CONF_ON_RAW_VALUE, CONF_ON_VALUE, CONF_ON_VALUE_RANGE, @@ -30,6 +31,9 @@ from esphome.const import ( CONF_NAME, CONF_MQTT_ID, CONF_FORCE_UPDATE, + LAST_RESET_TYPE_AUTO, + LAST_RESET_TYPE_NEVER, + LAST_RESET_TYPE_NONE, UNIT_EMPTY, ICON_EMPTY, DEVICE_CLASS_EMPTY, @@ -79,6 +83,15 @@ STATE_CLASSES = { } validate_state_class = cv.enum(STATE_CLASSES, lower=True, space="_") +LastResetTypes = sensor_ns.enum("LastResetType") +LAST_RESET_TYPES = { + LAST_RESET_TYPE_NONE: LastResetTypes.LAST_RESET_TYPE_NONE, + LAST_RESET_TYPE_NEVER: LastResetTypes.LAST_RESET_TYPE_NEVER, + LAST_RESET_TYPE_AUTO: LastResetTypes.LAST_RESET_TYPE_AUTO, +} +validate_last_reset_type = cv.enum(LAST_RESET_TYPES, lower=True, space="_") + + IS_PLATFORM_COMPONENT = True @@ -168,6 +181,7 @@ SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend( cv.Optional(CONF_ACCURACY_DECIMALS): accuracy_decimals, cv.Optional(CONF_DEVICE_CLASS): device_class, cv.Optional(CONF_STATE_CLASS): validate_state_class, + cv.Optional(CONF_LAST_RESET_TYPE): validate_last_reset_type, cv.Optional(CONF_FORCE_UPDATE, default=False): cv.boolean, cv.Optional(CONF_EXPIRE_AFTER): cv.All( cv.requires_component("mqtt"), @@ -202,6 +216,7 @@ def sensor_schema( accuracy_decimals_: int, device_class_: Optional[str] = DEVICE_CLASS_EMPTY, state_class_: Optional[str] = STATE_CLASS_NONE, + last_reset_type_: Optional[str] = LAST_RESET_TYPE_NONE, ) -> cv.Schema: schema = SENSOR_SCHEMA if unit_of_measurement_ != UNIT_EMPTY: @@ -230,6 +245,14 @@ def sensor_schema( schema = schema.extend( {cv.Optional(CONF_STATE_CLASS, default=state_class_): validate_state_class} ) + if last_reset_type_ != LAST_RESET_TYPE_NONE: + schema = schema.extend( + { + cv.Optional( + CONF_LAST_RESET_TYPE, default=last_reset_type_ + ): validate_last_reset_type + } + ) return schema @@ -479,6 +502,8 @@ async def setup_sensor_core_(var, config): cg.add(var.set_icon(config[CONF_ICON])) if CONF_ACCURACY_DECIMALS in config: cg.add(var.set_accuracy_decimals(config[CONF_ACCURACY_DECIMALS])) + if CONF_LAST_RESET_TYPE in config: + cg.add(var.set_last_reset_type(config[CONF_LAST_RESET_TYPE])) cg.add(var.set_force_update(config[CONF_FORCE_UPDATE])) if config.get(CONF_FILTERS): # must exist and not be empty filters = await build_filters(config[CONF_FILTERS]) diff --git a/esphome/components/sensor/automation.cpp b/esphome/components/sensor/automation.cpp index 1e8f3f4c3e..f53c43d1f6 100644 --- a/esphome/components/sensor/automation.cpp +++ b/esphome/components/sensor/automation.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace sensor { -static const char *TAG = "sensor.automation"; +static const char *const TAG = "sensor.automation"; } // namespace sensor } // namespace esphome diff --git a/esphome/components/sensor/filter.cpp b/esphome/components/sensor/filter.cpp index 57ffe9b482..bbe47b43ec 100644 --- a/esphome/components/sensor/filter.cpp +++ b/esphome/components/sensor/filter.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace sensor { -static const char *TAG = "sensor.filter"; +static const char *const TAG = "sensor.filter"; // Filter uint32_t Filter::expected_interval(uint32_t input) { return input; } @@ -237,7 +237,7 @@ optional FilterOutValueFilter::new_value(float value) { return value; } else { int8_t accuracy = this->parent_->get_accuracy_decimals(); - float accuracy_mult = pow10f(accuracy); + float accuracy_mult = powf(10.0f, accuracy); float rounded_filter_out = roundf(accuracy_mult * this->value_to_filter_out_); float rounded_value = roundf(accuracy_mult * value); if (rounded_filter_out == rounded_value) diff --git a/esphome/components/sensor/filter.h b/esphome/components/sensor/filter.h index 5b06d002fa..270a91ccff 100644 --- a/esphome/components/sensor/filter.h +++ b/esphome/components/sensor/filter.h @@ -1,8 +1,9 @@ #pragma once -#include #include "esphome/core/component.h" #include "esphome/core/helpers.h" +#include +#include namespace esphome { namespace sensor { @@ -335,7 +336,7 @@ class CalibrateLinearFilter : public Filter { class CalibratePolynomialFilter : public Filter { public: - CalibratePolynomialFilter(const std::vector &coefficients) : coefficients_(coefficients) {} + CalibratePolynomialFilter(std::vector coefficients) : coefficients_(std::move(coefficients)) {} optional new_value(float value) override; protected: diff --git a/esphome/components/sensor/sensor.cpp b/esphome/components/sensor/sensor.cpp index d7be618c2a..6e8765a8df 100644 --- a/esphome/components/sensor/sensor.cpp +++ b/esphome/components/sensor/sensor.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace sensor { -static const char *TAG = "sensor"; +static const char *const TAG = "sensor"; const char *state_class_to_string(StateClass state_class) { switch (state_class) { @@ -16,6 +16,18 @@ const char *state_class_to_string(StateClass state_class) { } } +const char *last_reset_type_to_string(LastResetType last_reset_type) { + switch (last_reset_type) { + case LAST_RESET_TYPE_NEVER: + return "never"; + case LAST_RESET_TYPE_AUTO: + return "auto"; + case LAST_RESET_TYPE_NONE: + default: + return ""; + } +} + void Sensor::publish_state(float state) { this->raw_state = state; this->raw_callback_.call(state); @@ -28,7 +40,6 @@ void Sensor::publish_state(float state) { this->filter_list_->input(state); } } -void Sensor::push_new_value(float state) { this->publish_state(state); } std::string Sensor::unit_of_measurement() { return ""; } std::string Sensor::icon() { return ""; } uint32_t Sensor::update_interval() { return 0; } @@ -65,6 +76,7 @@ void Sensor::set_state_class(const std::string &state_class) { ESP_LOGW(TAG, "'%s' - Unrecognized state class %s", this->get_name().c_str(), state_class.c_str()); } } +void Sensor::set_last_reset_type(LastResetType last_reset_type) { this->last_reset_type = last_reset_type; } std::string Sensor::get_unit_of_measurement() { if (this->unit_of_measurement_.has_value()) return *this->unit_of_measurement_; @@ -104,9 +116,7 @@ void Sensor::clear_filters() { } this->filter_list_ = nullptr; } -float Sensor::get_value() const { return this->state; } float Sensor::get_state() const { return this->state; } -float Sensor::get_raw_value() const { return this->raw_state; } float Sensor::get_raw_state() const { return this->raw_state; } std::string Sensor::unique_id() { return ""; } diff --git a/esphome/components/sensor/sensor.h b/esphome/components/sensor/sensor.h index fe528603c4..b9908b6cbe 100644 --- a/esphome/components/sensor/sensor.h +++ b/esphome/components/sensor/sensor.h @@ -8,21 +8,25 @@ namespace esphome { namespace sensor { #define LOG_SENSOR(prefix, type, obj) \ - if (obj != nullptr) { \ - ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, obj->get_name().c_str()); \ - if (!obj->get_device_class().empty()) { \ - ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, obj->get_device_class().c_str()); \ + if ((obj) != nullptr) { \ + ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \ + if (!(obj)->get_device_class().empty()) { \ + ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \ } \ - ESP_LOGCONFIG(TAG, "%s State Class: '%s'", prefix, state_class_to_string(obj->state_class)); \ - ESP_LOGCONFIG(TAG, "%s Unit of Measurement: '%s'", prefix, obj->get_unit_of_measurement().c_str()); \ - ESP_LOGCONFIG(TAG, "%s Accuracy Decimals: %d", prefix, obj->get_accuracy_decimals()); \ - if (!obj->get_icon().empty()) { \ - ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, obj->get_icon().c_str()); \ + ESP_LOGCONFIG(TAG, "%s State Class: '%s'", prefix, state_class_to_string((obj)->state_class)); \ + if ((obj)->state_class == sensor::STATE_CLASS_MEASUREMENT && \ + (obj)->last_reset_type != sensor::LAST_RESET_TYPE_NONE) { \ + ESP_LOGCONFIG(TAG, "%s Last Reset Type: '%s'", prefix, last_reset_type_to_string((obj)->last_reset_type)); \ } \ - if (!obj->unique_id().empty()) { \ - ESP_LOGV(TAG, "%s Unique ID: '%s'", prefix, obj->unique_id().c_str()); \ + ESP_LOGCONFIG(TAG, "%s Unit of Measurement: '%s'", prefix, (obj)->get_unit_of_measurement().c_str()); \ + ESP_LOGCONFIG(TAG, "%s Accuracy Decimals: %d", prefix, (obj)->get_accuracy_decimals()); \ + if (!(obj)->get_icon().empty()) { \ + ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \ } \ - if (obj->get_force_update()) { \ + if (!(obj)->unique_id().empty()) { \ + ESP_LOGV(TAG, "%s Unique ID: '%s'", prefix, (obj)->unique_id().c_str()); \ + } \ + if ((obj)->get_force_update()) { \ ESP_LOGV(TAG, "%s Force Update: YES", prefix); \ } \ } @@ -37,6 +41,20 @@ enum StateClass : uint8_t { const char *state_class_to_string(StateClass state_class); +/** + * Sensor last reset types + */ +enum LastResetType : uint8_t { + /// This sensor does not support resetting. ie, it is not accumulative + LAST_RESET_TYPE_NONE = 0, + /// This sensor is expected to never reset its value + LAST_RESET_TYPE_NEVER = 1, + /// This sensor may reset and Home Assistant will watch for this + LAST_RESET_TYPE_AUTO = 2, +}; + +const char *last_reset_type_to_string(LastResetType last_reset_type); + /** Base-class for all sensors. * * A sensor has unit of measurement and can use publish_state to send out a new value with the specified accuracy. @@ -87,12 +105,8 @@ class Sensor : public Nameable { /// Clear the entire filter chain. void clear_filters(); - /// Getter-syntax for .value. Please use .state instead. - float get_value() const ESPDEPRECATED(".value is deprecated, please use .state"); /// Getter-syntax for .state. float get_state() const; - /// Getter-syntax for .raw_value. Please use .raw_state instead. - float get_raw_value() const ESPDEPRECATED(".raw_value is deprecated, please use .raw_state"); /// Getter-syntax for .raw_state float get_raw_state() const; @@ -114,12 +128,6 @@ class Sensor : public Nameable { */ void publish_state(float state); - /** Push a new value to the MQTT front-end. - * - * Note: deprecated, please use publish_state. - */ - void push_new_value(float state) ESPDEPRECATED("push_new_value is deprecated. Please use .publish_state instead"); - // ========== INTERNAL METHODS ========== // (In most use cases you won't need these) /// Add a callback that will be called every time a filtered value arrives. @@ -165,6 +173,12 @@ class Sensor : public Nameable { */ virtual std::string device_class(); + // The Last reset type of this sensor + LastResetType last_reset_type{LAST_RESET_TYPE_NONE}; + + /// Manually set the Home Assistant last reset type for this sensor. + void set_last_reset_type(LastResetType last_reset_type); + /** A unique ID for this sensor, empty for no unique id. See unique ID requirements: * https://developers.home-assistant.io/docs/en/entity_registry_index.html#unique-id-requirements * diff --git a/esphome/components/servo/servo.cpp b/esphome/components/servo/servo.cpp index 57baf4aecf..0b018ddb2e 100644 --- a/esphome/components/servo/servo.cpp +++ b/esphome/components/servo/servo.cpp @@ -4,9 +4,9 @@ namespace esphome { namespace servo { -static const char *TAG = "servo"; +static const char *const TAG = "servo"; -uint32_t global_servo_id = 1911044085ULL; +uint32_t global_servo_id = 1911044085ULL; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) void Servo::dump_config() { ESP_LOGCONFIG(TAG, "Servo:"); diff --git a/esphome/components/servo/servo.h b/esphome/components/servo/servo.h index 937706d44d..d95a524a8b 100644 --- a/esphome/components/servo/servo.h +++ b/esphome/components/servo/servo.h @@ -9,7 +9,7 @@ namespace esphome { namespace servo { -extern uint32_t global_servo_id; +extern uint32_t global_servo_id; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) class Servo : public Component { public: diff --git a/esphome/components/sgp30/sensor.py b/esphome/components/sgp30/sensor.py index f393627eda..7a3e870f6d 100644 --- a/esphome/components/sgp30/sensor.py +++ b/esphome/components/sgp30/sensor.py @@ -3,13 +3,16 @@ import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import ( CONF_ID, + CONF_BASELINE, DEVICE_CLASS_EMPTY, + CONF_ECO2, + CONF_TVOC, ICON_RADIATOR, STATE_CLASS_MEASUREMENT, UNIT_PARTS_PER_MILLION, UNIT_PARTS_PER_BILLION, + UNIT_EMPTY, ICON_MOLECULE_CO2, - CONF_TVOC, ) DEPENDENCIES = ["i2c"] @@ -17,10 +20,9 @@ DEPENDENCIES = ["i2c"] sgp30_ns = cg.esphome_ns.namespace("sgp30") SGP30Component = sgp30_ns.class_("SGP30Component", cg.PollingComponent, i2c.I2CDevice) -CONF_ECO2 = "eco2" -CONF_BASELINE = "baseline" CONF_ECO2_BASELINE = "eco2_baseline" CONF_TVOC_BASELINE = "tvoc_baseline" +CONF_STORE_BASELINE = "store_baseline" CONF_UPTIME = "uptime" CONF_COMPENSATION = "compensation" CONF_HUMIDITY_SOURCE = "humidity_source" @@ -44,6 +46,13 @@ CONFIG_SCHEMA = ( DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT, ), + cv.Optional(CONF_ECO2_BASELINE): sensor.sensor_schema( + UNIT_EMPTY, ICON_MOLECULE_CO2, 0, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_TVOC_BASELINE): sensor.sensor_schema( + UNIT_EMPTY, ICON_RADIATOR, 0, DEVICE_CLASS_EMPTY + ), + cv.Optional(CONF_STORE_BASELINE, default=True): cv.boolean, cv.Optional(CONF_BASELINE): cv.Schema( { cv.Required(CONF_ECO2_BASELINE): cv.hex_uint16_t, @@ -58,7 +67,7 @@ CONFIG_SCHEMA = ( ), } ) - .extend(cv.polling_component_schema("60s")) + .extend(cv.polling_component_schema("1s")) .extend(i2c.i2c_device_schema(0x58)) ) @@ -76,6 +85,17 @@ async def to_code(config): sens = await sensor.new_sensor(config[CONF_TVOC]) cg.add(var.set_tvoc_sensor(sens)) + if CONF_ECO2_BASELINE in config: + sens = await sensor.new_sensor(config[CONF_ECO2_BASELINE]) + cg.add(var.set_eco2_baseline_sensor(sens)) + + if CONF_TVOC_BASELINE in config: + sens = await sensor.new_sensor(config[CONF_TVOC_BASELINE]) + cg.add(var.set_tvoc_baseline_sensor(sens)) + + if CONF_STORE_BASELINE in config: + cg.add(var.set_store_baseline(config[CONF_STORE_BASELINE])) + if CONF_BASELINE in config: baseline_config = config[CONF_BASELINE] cg.add(var.set_eco2_baseline(baseline_config[CONF_ECO2_BASELINE])) diff --git a/esphome/components/sgp30/sgp30.cpp b/esphome/components/sgp30/sgp30.cpp index 1ef43c1c73..fdc6ae031d 100644 --- a/esphome/components/sgp30/sgp30.cpp +++ b/esphome/components/sgp30/sgp30.cpp @@ -1,10 +1,11 @@ #include "sgp30.h" #include "esphome/core/log.h" +#include "esphome/core/application.h" namespace esphome { namespace sgp30 { -static const char *TAG = "sgp30"; +static const char *const TAG = "sgp30"; static const uint16_t SGP30_CMD_GET_SERIAL_ID = 0x3682; static const uint16_t SGP30_CMD_GET_FEATURESET = 0x202f; @@ -16,11 +17,18 @@ static const uint16_t SGP30_CMD_SET_IAQ_BASELINE = 0x201E; // Sensor baseline should first be relied on after 1H of operation, // if the sensor starts with a baseline value provided -const long IAQ_BASELINE_WARM_UP_SECONDS_WITH_BASELINE_PROVIDED = 3600; +const uint32_t IAQ_BASELINE_WARM_UP_SECONDS_WITH_BASELINE_PROVIDED = 3600; // Sensor baseline could first be relied on after 12H of operation, // if the sensor starts without any prior baseline value provided -const long IAQ_BASELINE_WARM_UP_SECONDS_WITHOUT_BASELINE = 43200; +const uint32_t IAQ_BASELINE_WARM_UP_SECONDS_WITHOUT_BASELINE = 43200; + +// Shortest time interval of 1H for storing baseline values. +// Prevents wear of the flash because of too many write operations +const uint32_t SHORTEST_BASELINE_STORE_INTERVAL = 3600; + +// Store anyway if the baseline difference exceeds the max storage diff value +const uint32_t MAXIMUM_STORAGE_DIFF = 50; void SGP30Component::setup() { ESP_LOGCONFIG(TAG, "Setting up SGP30..."); @@ -73,6 +81,21 @@ void SGP30Component::setup() { return; } + // Hash with compilation time + // This ensures the baseline storage is cleared after OTA + uint32_t hash = fnv1_hash(App.get_compilation_time()); + this->pref_ = global_preferences.make_preference(hash, true); + + if (this->pref_.load(&this->baselines_storage_)) { + ESP_LOGI(TAG, "Loaded eCO2 baseline: 0x%04X, TVOC baseline: 0x%04X", this->baselines_storage_.eco2, + baselines_storage_.tvoc); + this->eco2_baseline_ = this->baselines_storage_.eco2; + this->tvoc_baseline_ = this->baselines_storage_.tvoc; + } + + // Initialize storage timestamp + this->seconds_since_last_store_ = 0; + // Sensor baseline reliability timer if (this->eco2_baseline_ > 0 && this->tvoc_baseline_ > 0) { this->required_warm_up_time_ = IAQ_BASELINE_WARM_UP_SECONDS_WITH_BASELINE_PROVIDED; @@ -110,6 +133,31 @@ void SGP30Component::read_iaq_baseline_() { uint16_t tvocbaseline = (raw_data[1]); ESP_LOGI(TAG, "Current eCO2 baseline: 0x%04X, TVOC baseline: 0x%04X", eco2baseline, tvocbaseline); + if (eco2baseline != this->eco2_baseline_ || tvocbaseline != this->tvoc_baseline_) { + this->eco2_baseline_ = eco2baseline; + this->tvoc_baseline_ = tvocbaseline; + if (this->eco2_sensor_baseline_ != nullptr) + this->eco2_sensor_baseline_->publish_state(this->eco2_baseline_); + if (this->tvoc_sensor_baseline_ != nullptr) + this->tvoc_sensor_baseline_->publish_state(this->tvoc_baseline_); + + // Store baselines after defined interval or if the difference between current and stored baseline becomes too + // much + if (this->store_baseline_ && + (this->seconds_since_last_store_ > SHORTEST_BASELINE_STORE_INTERVAL || + abs(this->baselines_storage_.eco2 - this->eco2_baseline_) > MAXIMUM_STORAGE_DIFF || + abs(this->baselines_storage_.tvoc - this->tvoc_baseline_) > MAXIMUM_STORAGE_DIFF)) { + this->seconds_since_last_store_ = 0; + this->baselines_storage_.eco2 = this->eco2_baseline_; + this->baselines_storage_.tvoc = this->tvoc_baseline_; + if (this->pref_.save(&this->baselines_storage_)) { + ESP_LOGI(TAG, "Store eCO2 baseline: 0x%04X, TVOC baseline: 0x%04X", this->baselines_storage_.eco2, + this->baselines_storage_.tvoc); + } else { + ESP_LOGW(TAG, "Could not store eCO2 and TVOC baselines"); + } + } + } this->status_clear_warning(); }); } else { @@ -171,7 +219,8 @@ void SGP30Component::write_iaq_baseline_(uint16_t eco2_baseline, uint16_t tvoc_b if (!this->write_bytes(SGP30_CMD_SET_IAQ_BASELINE >> 8, data, 7)) { ESP_LOGE(TAG, "Error applying eCO2 baseline: 0x%04X, TVOC baseline: 0x%04X", eco2_baseline, tvoc_baseline); } else - ESP_LOGI(TAG, "Initial eCO2 and TVOC baselines applied successfully!"); + ESP_LOGI(TAG, "Initial baselines applied successfully! eCO2 baseline: 0x%04X, TVOC baseline: 0x%04X", eco2_baseline, + tvoc_baseline); } void SGP30Component::dump_config() { @@ -204,11 +253,14 @@ void SGP30Component::dump_config() { } else { ESP_LOGCONFIG(TAG, " Baseline: No baseline configured"); } - ESP_LOGCONFIG(TAG, " Warm up time: %lds", this->required_warm_up_time_); + ESP_LOGCONFIG(TAG, " Warm up time: %us", this->required_warm_up_time_); } LOG_UPDATE_INTERVAL(this); - LOG_SENSOR(" ", "eCO2", this->eco2_sensor_); - LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_); + LOG_SENSOR(" ", "eCO2 sensor", this->eco2_sensor_); + LOG_SENSOR(" ", "TVOC sensor", this->tvoc_sensor_); + LOG_SENSOR(" ", "eCO2 baseline sensor", this->eco2_sensor_baseline_); + LOG_SENSOR(" ", "TVOC baseline sensor", this->tvoc_sensor_baseline_); + ESP_LOGCONFIG(TAG, "Store baseline: %s", YESNO(this->store_baseline_)); if (this->humidity_sensor_ != nullptr && this->temperature_sensor_ != nullptr) { ESP_LOGCONFIG(TAG, " Compensation:"); LOG_SENSOR(" ", "Temperature Source:", this->temperature_sensor_); @@ -223,7 +275,7 @@ void SGP30Component::update() { this->status_set_warning(); return; } - + this->seconds_since_last_store_ += this->update_interval_ / 1000; this->set_timeout(50, [this]() { uint16_t raw_data[2]; if (!this->read_data_(raw_data, 2)) { @@ -239,6 +291,11 @@ void SGP30Component::update() { this->eco2_sensor_->publish_state(eco2); if (this->tvoc_sensor_ != nullptr) this->tvoc_sensor_->publish_state(tvoc); + + if (this->get_update_interval() != 1000) { + ESP_LOGW(TAG, "Update interval for SGP30 sensor must be set to 1s for optimized readout"); + } + this->status_clear_warning(); this->send_env_data_(); this->read_iaq_baseline_(); diff --git a/esphome/components/sgp30/sgp30.h b/esphome/components/sgp30/sgp30.h index 27572e9c46..91a1c1e9c7 100644 --- a/esphome/components/sgp30/sgp30.h +++ b/esphome/components/sgp30/sgp30.h @@ -3,16 +3,25 @@ #include "esphome/core/component.h" #include "esphome/components/sensor/sensor.h" #include "esphome/components/i2c/i2c.h" +#include "esphome/core/preferences.h" #include namespace esphome { namespace sgp30 { +struct SGP30Baselines { + uint16_t eco2; + uint16_t tvoc; +} PACKED; + /// This class implements support for the Sensirion SGP30 i2c GAS (VOC and CO2eq) sensors. class SGP30Component : public PollingComponent, public i2c::I2CDevice { public: void set_eco2_sensor(sensor::Sensor *eco2) { eco2_sensor_ = eco2; } void set_tvoc_sensor(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; } + void set_eco2_baseline_sensor(sensor::Sensor *eco2_baseline) { eco2_sensor_baseline_ = eco2_baseline; } + void set_tvoc_baseline_sensor(sensor::Sensor *tvoc_baseline) { tvoc_sensor_baseline_ = tvoc_baseline; } + void set_store_baseline(bool store_baseline) { store_baseline_ = store_baseline; } void set_eco2_baseline(uint16_t eco2_baseline) { eco2_baseline_ = eco2_baseline; } void set_tvoc_baseline(uint16_t tvoc_baseline) { tvoc_baseline_ = tvoc_baseline; } void set_humidity_sensor(sensor::Sensor *humidity) { humidity_sensor_ = humidity; } @@ -33,7 +42,10 @@ class SGP30Component : public PollingComponent, public i2c::I2CDevice { uint8_t sht_crc_(uint8_t data1, uint8_t data2); uint64_t serial_number_; uint16_t featureset_; - long required_warm_up_time_; + uint32_t required_warm_up_time_; + uint32_t seconds_since_last_store_; + SGP30Baselines baselines_storage_; + ESPPreferenceObject pref_; enum ErrorCode { COMMUNICATION_FAILED, @@ -45,8 +57,12 @@ class SGP30Component : public PollingComponent, public i2c::I2CDevice { sensor::Sensor *eco2_sensor_{nullptr}; sensor::Sensor *tvoc_sensor_{nullptr}; + sensor::Sensor *eco2_sensor_baseline_{nullptr}; + sensor::Sensor *tvoc_sensor_baseline_{nullptr}; uint16_t eco2_baseline_{0x0000}; uint16_t tvoc_baseline_{0x0000}; + bool store_baseline_; + /// Input sensor for humidity and temperature compensation. sensor::Sensor *humidity_sensor_{nullptr}; sensor::Sensor *temperature_sensor_{nullptr}; diff --git a/esphome/components/sgp40/sensirion_voc_algorithm.cpp b/esphome/components/sgp40/sensirion_voc_algorithm.cpp index c82db2301a..f3cdeee35b 100644 --- a/esphome/components/sgp40/sensirion_voc_algorithm.cpp +++ b/esphome/components/sgp40/sensirion_voc_algorithm.cpp @@ -224,7 +224,7 @@ static fix16_t fix16_exp(fix16_t in_value) { static const uint8_t NUM_EXP_VALUES = 4; static const fix16_t EXP_POS_VALUES[4] = {F16(2.7182818), F16(1.1331485), F16(1.0157477), F16(1.0019550)}; static const fix16_t EXP_NEG_VALUES[4] = {F16(0.3678794), F16(0.8824969), F16(0.9844964), F16(0.9980488)}; - const fix16_t* exp_values; + const fix16_t *exp_values; fix16_t res, arg; uint16_t i; @@ -253,35 +253,35 @@ static fix16_t fix16_exp(fix16_t in_value) { return res; } -static void voc_algorithm_init_instances(VocAlgorithmParams* params); -static void voc_algorithm_mean_variance_estimator_init(VocAlgorithmParams* params); -static void voc_algorithm_mean_variance_estimator_init_instances(VocAlgorithmParams* params); -static void voc_algorithm_mean_variance_estimator_set_parameters(VocAlgorithmParams* params, fix16_t std_initial, +static void voc_algorithm_init_instances(VocAlgorithmParams *params); +static void voc_algorithm_mean_variance_estimator_init(VocAlgorithmParams *params); +static void voc_algorithm_mean_variance_estimator_init_instances(VocAlgorithmParams *params); +static void voc_algorithm_mean_variance_estimator_set_parameters(VocAlgorithmParams *params, fix16_t std_initial, fix16_t tau_mean_variance_hours, fix16_t gating_max_duration_minutes); -static void voc_algorithm_mean_variance_estimator_set_states(VocAlgorithmParams* params, fix16_t mean, fix16_t std, +static void voc_algorithm_mean_variance_estimator_set_states(VocAlgorithmParams *params, fix16_t mean, fix16_t std, fix16_t uptime_gamma); -static fix16_t voc_algorithm_mean_variance_estimator_get_std(VocAlgorithmParams* params); -static fix16_t voc_algorithm_mean_variance_estimator_get_mean(VocAlgorithmParams* params); -static void voc_algorithm_mean_variance_estimator_calculate_gamma(VocAlgorithmParams* params, +static fix16_t voc_algorithm_mean_variance_estimator_get_std(VocAlgorithmParams *params); +static fix16_t voc_algorithm_mean_variance_estimator_get_mean(VocAlgorithmParams *params); +static void voc_algorithm_mean_variance_estimator_calculate_gamma(VocAlgorithmParams *params, fix16_t voc_index_from_prior); -static void voc_algorithm_mean_variance_estimator_process(VocAlgorithmParams* params, fix16_t sraw, +static void voc_algorithm_mean_variance_estimator_process(VocAlgorithmParams *params, fix16_t sraw, fix16_t voc_index_from_prior); -static void voc_algorithm_mean_variance_estimator_sigmoid_init(VocAlgorithmParams* params); -static void voc_algorithm_mean_variance_estimator_sigmoid_set_parameters(VocAlgorithmParams* params, fix16_t l, +static void voc_algorithm_mean_variance_estimator_sigmoid_init(VocAlgorithmParams *params); +static void voc_algorithm_mean_variance_estimator_sigmoid_set_parameters(VocAlgorithmParams *params, fix16_t l, fix16_t x0, fix16_t k); -static fix16_t voc_algorithm_mean_variance_estimator_sigmoid_process(VocAlgorithmParams* params, fix16_t sample); -static void voc_algorithm_mox_model_init(VocAlgorithmParams* params); -static void voc_algorithm_mox_model_set_parameters(VocAlgorithmParams* params, fix16_t sraw_std, fix16_t sraw_mean); -static fix16_t voc_algorithm_mox_model_process(VocAlgorithmParams* params, fix16_t sraw); -static void voc_algorithm_sigmoid_scaled_init(VocAlgorithmParams* params); -static void voc_algorithm_sigmoid_scaled_set_parameters(VocAlgorithmParams* params, fix16_t offset); -static fix16_t voc_algorithm_sigmoid_scaled_process(VocAlgorithmParams* params, fix16_t sample); -static void voc_algorithm_adaptive_lowpass_init(VocAlgorithmParams* params); -static void voc_algorithm_adaptive_lowpass_set_parameters(VocAlgorithmParams* params); -static fix16_t voc_algorithm_adaptive_lowpass_process(VocAlgorithmParams* params, fix16_t sample); +static fix16_t voc_algorithm_mean_variance_estimator_sigmoid_process(VocAlgorithmParams *params, fix16_t sample); +static void voc_algorithm_mox_model_init(VocAlgorithmParams *params); +static void voc_algorithm_mox_model_set_parameters(VocAlgorithmParams *params, fix16_t sraw_std, fix16_t sraw_mean); +static fix16_t voc_algorithm_mox_model_process(VocAlgorithmParams *params, fix16_t sraw); +static void voc_algorithm_sigmoid_scaled_init(VocAlgorithmParams *params); +static void voc_algorithm_sigmoid_scaled_set_parameters(VocAlgorithmParams *params, fix16_t offset); +static fix16_t voc_algorithm_sigmoid_scaled_process(VocAlgorithmParams *params, fix16_t sample); +static void voc_algorithm_adaptive_lowpass_init(VocAlgorithmParams *params); +static void voc_algorithm_adaptive_lowpass_set_parameters(VocAlgorithmParams *params); +static fix16_t voc_algorithm_adaptive_lowpass_process(VocAlgorithmParams *params, fix16_t sample); -void voc_algorithm_init(VocAlgorithmParams* params) { +void voc_algorithm_init(VocAlgorithmParams *params) { params->mVoc_Index_Offset = F16(VOC_ALGORITHM_VOC_INDEX_OFFSET_DEFAULT); params->mTau_Mean_Variance_Hours = F16(VOC_ALGORITHM_TAU_MEAN_VARIANCE_HOURS); params->mGating_Max_Duration_Minutes = F16(VOC_ALGORITHM_GATING_MAX_DURATION_MINUTES); @@ -292,7 +292,7 @@ void voc_algorithm_init(VocAlgorithmParams* params) { voc_algorithm_init_instances(params); } -static void voc_algorithm_init_instances(VocAlgorithmParams* params) { +static void voc_algorithm_init_instances(VocAlgorithmParams *params) { voc_algorithm_mean_variance_estimator_init(params); voc_algorithm_mean_variance_estimator_set_parameters( params, params->mSraw_Std_Initial, params->mTau_Mean_Variance_Hours, params->mGating_Max_Duration_Minutes); @@ -305,17 +305,17 @@ static void voc_algorithm_init_instances(VocAlgorithmParams* params) { voc_algorithm_adaptive_lowpass_set_parameters(params); } -void voc_algorithm_get_states(VocAlgorithmParams* params, int32_t* state0, int32_t* state1) { +void voc_algorithm_get_states(VocAlgorithmParams *params, int32_t *state0, int32_t *state1) { *state0 = voc_algorithm_mean_variance_estimator_get_mean(params); *state1 = voc_algorithm_mean_variance_estimator_get_std(params); } -void voc_algorithm_set_states(VocAlgorithmParams* params, int32_t state0, int32_t state1) { +void voc_algorithm_set_states(VocAlgorithmParams *params, int32_t state0, int32_t state1) { voc_algorithm_mean_variance_estimator_set_states(params, state0, state1, F16(VOC_ALGORITHM_PERSISTENCE_UPTIME_GAMMA)); params->mSraw = state0; } -void voc_algorithm_set_tuning_parameters(VocAlgorithmParams* params, int32_t voc_index_offset, +void voc_algorithm_set_tuning_parameters(VocAlgorithmParams *params, int32_t voc_index_offset, int32_t learning_time_hours, int32_t gating_max_duration_minutes, int32_t std_initial) { params->mVoc_Index_Offset = (fix16_from_int(voc_index_offset)); @@ -325,7 +325,7 @@ void voc_algorithm_set_tuning_parameters(VocAlgorithmParams* params, int32_t voc voc_algorithm_init_instances(params); } -void voc_algorithm_process(VocAlgorithmParams* params, int32_t sraw, int32_t* voc_index) { +void voc_algorithm_process(VocAlgorithmParams *params, int32_t sraw, int32_t *voc_index) { if ((params->mUptime <= F16(VOC_ALGORITHM_INITIAL_BLACKOUT))) { params->mUptime = (params->mUptime + F16(VOC_ALGORITHM_SAMPLING_INTERVAL)); } else { @@ -352,56 +352,56 @@ void voc_algorithm_process(VocAlgorithmParams* params, int32_t sraw, int32_t* vo *voc_index = (fix16_cast_to_int((params->mVoc_Index + F16(0.5)))); } -static void voc_algorithm_mean_variance_estimator_init(VocAlgorithmParams* params) { +static void voc_algorithm_mean_variance_estimator_init(VocAlgorithmParams *params) { voc_algorithm_mean_variance_estimator_set_parameters(params, F16(0.), F16(0.), F16(0.)); voc_algorithm_mean_variance_estimator_init_instances(params); } -static void voc_algorithm_mean_variance_estimator_init_instances(VocAlgorithmParams* params) { +static void voc_algorithm_mean_variance_estimator_init_instances(VocAlgorithmParams *params) { voc_algorithm_mean_variance_estimator_sigmoid_init(params); } -static void voc_algorithm_mean_variance_estimator_set_parameters(VocAlgorithmParams* params, fix16_t std_initial, +static void voc_algorithm_mean_variance_estimator_set_parameters(VocAlgorithmParams *params, fix16_t std_initial, fix16_t tau_mean_variance_hours, fix16_t gating_max_duration_minutes) { - params->m_Mean_Variance_Estimator__Gating_Max_Duration_Minutes = gating_max_duration_minutes; - params->m_Mean_Variance_Estimator___Initialized = false; - params->m_Mean_Variance_Estimator___Mean = F16(0.); - params->m_Mean_Variance_Estimator___Sraw_Offset = F16(0.); - params->m_Mean_Variance_Estimator___Std = std_initial; - params->m_Mean_Variance_Estimator___Gamma = + params->m_Mean_Variance_Estimator_Gating_Max_Duration_Minutes = gating_max_duration_minutes; + params->m_Mean_Variance_Estimator_Initialized = false; + params->m_Mean_Variance_Estimator_Mean = F16(0.); + params->m_Mean_Variance_Estimator_Sraw_Offset = F16(0.); + params->m_Mean_Variance_Estimator_Std = std_initial; + params->m_Mean_Variance_Estimator_Gamma = (fix16_div(F16((VOC_ALGORITHM_MEAN_VARIANCE_ESTIMATOR_GAMMA_SCALING * (VOC_ALGORITHM_SAMPLING_INTERVAL / 3600.))), (tau_mean_variance_hours + F16((VOC_ALGORITHM_SAMPLING_INTERVAL / 3600.))))); - params->m_Mean_Variance_Estimator___Gamma_Initial_Mean = + params->m_Mean_Variance_Estimator_Gamma_Initial_Mean = F16(((VOC_ALGORITHM_MEAN_VARIANCE_ESTIMATOR_GAMMA_SCALING * VOC_ALGORITHM_SAMPLING_INTERVAL) / (VOC_ALGORITHM_TAU_INITIAL_MEAN + VOC_ALGORITHM_SAMPLING_INTERVAL))); - params->m_Mean_Variance_Estimator___Gamma_Initial_Variance = + params->m_Mean_Variance_Estimator_Gamma_Initial_Variance = F16(((VOC_ALGORITHM_MEAN_VARIANCE_ESTIMATOR_GAMMA_SCALING * VOC_ALGORITHM_SAMPLING_INTERVAL) / (VOC_ALGORITHM_TAU_INITIAL_VARIANCE + VOC_ALGORITHM_SAMPLING_INTERVAL))); - params->m_Mean_Variance_Estimator__Gamma_Mean = F16(0.); - params->m_Mean_Variance_Estimator__Gamma_Variance = F16(0.); - params->m_Mean_Variance_Estimator___Uptime_Gamma = F16(0.); - params->m_Mean_Variance_Estimator___Uptime_Gating = F16(0.); - params->m_Mean_Variance_Estimator___Gating_Duration_Minutes = F16(0.); + params->m_Mean_Variance_Estimator_Gamma_Mean = F16(0.); + params->m_Mean_Variance_Estimator_Gamma_Variance = F16(0.); + params->m_Mean_Variance_Estimator_Uptime_Gamma = F16(0.); + params->m_Mean_Variance_Estimator_Uptime_Gating = F16(0.); + params->m_Mean_Variance_Estimator_Gating_Duration_Minutes = F16(0.); } -static void voc_algorithm_mean_variance_estimator_set_states(VocAlgorithmParams* params, fix16_t mean, fix16_t std, +static void voc_algorithm_mean_variance_estimator_set_states(VocAlgorithmParams *params, fix16_t mean, fix16_t std, fix16_t uptime_gamma) { - params->m_Mean_Variance_Estimator___Mean = mean; - params->m_Mean_Variance_Estimator___Std = std; - params->m_Mean_Variance_Estimator___Uptime_Gamma = uptime_gamma; - params->m_Mean_Variance_Estimator___Initialized = true; + params->m_Mean_Variance_Estimator_Mean = mean; + params->m_Mean_Variance_Estimator_Std = std; + params->m_Mean_Variance_Estimator_Uptime_Gamma = uptime_gamma; + params->m_Mean_Variance_Estimator_Initialized = true; } -static fix16_t voc_algorithm_mean_variance_estimator_get_std(VocAlgorithmParams* params) { - return params->m_Mean_Variance_Estimator___Std; +static fix16_t voc_algorithm_mean_variance_estimator_get_std(VocAlgorithmParams *params) { + return params->m_Mean_Variance_Estimator_Std; } -static fix16_t voc_algorithm_mean_variance_estimator_get_mean(VocAlgorithmParams* params) { - return (params->m_Mean_Variance_Estimator___Mean + params->m_Mean_Variance_Estimator___Sraw_Offset); +static fix16_t voc_algorithm_mean_variance_estimator_get_mean(VocAlgorithmParams *params) { + return (params->m_Mean_Variance_Estimator_Mean + params->m_Mean_Variance_Estimator_Sraw_Offset); } -static void voc_algorithm_mean_variance_estimator_calculate_gamma(VocAlgorithmParams* params, +static void voc_algorithm_mean_variance_estimator_calculate_gamma(VocAlgorithmParams *params, fix16_t voc_index_from_prior) { fix16_t uptime_limit; fix16_t sigmoid_gamma_mean; @@ -414,155 +414,154 @@ static void voc_algorithm_mean_variance_estimator_calculate_gamma(VocAlgorithmPa fix16_t sigmoid_gating_variance; uptime_limit = F16((VOC_ALGORITHM_MEAN_VARIANCE_ESTIMATOR_FI_X16_MAX - VOC_ALGORITHM_SAMPLING_INTERVAL)); - if ((params->m_Mean_Variance_Estimator___Uptime_Gamma < uptime_limit)) { - params->m_Mean_Variance_Estimator___Uptime_Gamma = - (params->m_Mean_Variance_Estimator___Uptime_Gamma + F16(VOC_ALGORITHM_SAMPLING_INTERVAL)); + if ((params->m_Mean_Variance_Estimator_Uptime_Gamma < uptime_limit)) { + params->m_Mean_Variance_Estimator_Uptime_Gamma = + (params->m_Mean_Variance_Estimator_Uptime_Gamma + F16(VOC_ALGORITHM_SAMPLING_INTERVAL)); } - if ((params->m_Mean_Variance_Estimator___Uptime_Gating < uptime_limit)) { - params->m_Mean_Variance_Estimator___Uptime_Gating = - (params->m_Mean_Variance_Estimator___Uptime_Gating + F16(VOC_ALGORITHM_SAMPLING_INTERVAL)); + if ((params->m_Mean_Variance_Estimator_Uptime_Gating < uptime_limit)) { + params->m_Mean_Variance_Estimator_Uptime_Gating = + (params->m_Mean_Variance_Estimator_Uptime_Gating + F16(VOC_ALGORITHM_SAMPLING_INTERVAL)); } voc_algorithm_mean_variance_estimator_sigmoid_set_parameters(params, F16(1.), F16(VOC_ALGORITHM_INIT_DURATION_MEAN), F16(VOC_ALGORITHM_INIT_TRANSITION_MEAN)); sigmoid_gamma_mean = - voc_algorithm_mean_variance_estimator_sigmoid_process(params, params->m_Mean_Variance_Estimator___Uptime_Gamma); + voc_algorithm_mean_variance_estimator_sigmoid_process(params, params->m_Mean_Variance_Estimator_Uptime_Gamma); gamma_mean = - (params->m_Mean_Variance_Estimator___Gamma + - (fix16_mul((params->m_Mean_Variance_Estimator___Gamma_Initial_Mean - params->m_Mean_Variance_Estimator___Gamma), + (params->m_Mean_Variance_Estimator_Gamma + + (fix16_mul((params->m_Mean_Variance_Estimator_Gamma_Initial_Mean - params->m_Mean_Variance_Estimator_Gamma), sigmoid_gamma_mean))); gating_threshold_mean = (F16(VOC_ALGORITHM_GATING_THRESHOLD) + (fix16_mul(F16((VOC_ALGORITHM_GATING_THRESHOLD_INITIAL - VOC_ALGORITHM_GATING_THRESHOLD)), voc_algorithm_mean_variance_estimator_sigmoid_process( - params, params->m_Mean_Variance_Estimator___Uptime_Gating)))); + params, params->m_Mean_Variance_Estimator_Uptime_Gating)))); voc_algorithm_mean_variance_estimator_sigmoid_set_parameters(params, F16(1.), gating_threshold_mean, F16(VOC_ALGORITHM_GATING_THRESHOLD_TRANSITION)); sigmoid_gating_mean = voc_algorithm_mean_variance_estimator_sigmoid_process(params, voc_index_from_prior); - params->m_Mean_Variance_Estimator__Gamma_Mean = (fix16_mul(sigmoid_gating_mean, gamma_mean)); + params->m_Mean_Variance_Estimator_Gamma_Mean = (fix16_mul(sigmoid_gating_mean, gamma_mean)); voc_algorithm_mean_variance_estimator_sigmoid_set_parameters( params, F16(1.), F16(VOC_ALGORITHM_INIT_DURATION_VARIANCE), F16(VOC_ALGORITHM_INIT_TRANSITION_VARIANCE)); sigmoid_gamma_variance = - voc_algorithm_mean_variance_estimator_sigmoid_process(params, params->m_Mean_Variance_Estimator___Uptime_Gamma); - gamma_variance = (params->m_Mean_Variance_Estimator___Gamma + - (fix16_mul((params->m_Mean_Variance_Estimator___Gamma_Initial_Variance - - params->m_Mean_Variance_Estimator___Gamma), - (sigmoid_gamma_variance - sigmoid_gamma_mean)))); + voc_algorithm_mean_variance_estimator_sigmoid_process(params, params->m_Mean_Variance_Estimator_Uptime_Gamma); + gamma_variance = + (params->m_Mean_Variance_Estimator_Gamma + + (fix16_mul((params->m_Mean_Variance_Estimator_Gamma_Initial_Variance - params->m_Mean_Variance_Estimator_Gamma), + (sigmoid_gamma_variance - sigmoid_gamma_mean)))); gating_threshold_variance = (F16(VOC_ALGORITHM_GATING_THRESHOLD) + (fix16_mul(F16((VOC_ALGORITHM_GATING_THRESHOLD_INITIAL - VOC_ALGORITHM_GATING_THRESHOLD)), voc_algorithm_mean_variance_estimator_sigmoid_process( - params, params->m_Mean_Variance_Estimator___Uptime_Gating)))); + params, params->m_Mean_Variance_Estimator_Uptime_Gating)))); voc_algorithm_mean_variance_estimator_sigmoid_set_parameters(params, F16(1.), gating_threshold_variance, F16(VOC_ALGORITHM_GATING_THRESHOLD_TRANSITION)); sigmoid_gating_variance = voc_algorithm_mean_variance_estimator_sigmoid_process(params, voc_index_from_prior); - params->m_Mean_Variance_Estimator__Gamma_Variance = (fix16_mul(sigmoid_gating_variance, gamma_variance)); - params->m_Mean_Variance_Estimator___Gating_Duration_Minutes = - (params->m_Mean_Variance_Estimator___Gating_Duration_Minutes + + params->m_Mean_Variance_Estimator_Gamma_Variance = (fix16_mul(sigmoid_gating_variance, gamma_variance)); + params->m_Mean_Variance_Estimator_Gating_Duration_Minutes = + (params->m_Mean_Variance_Estimator_Gating_Duration_Minutes + (fix16_mul(F16((VOC_ALGORITHM_SAMPLING_INTERVAL / 60.)), ((fix16_mul((F16(1.) - sigmoid_gating_mean), F16((1. + VOC_ALGORITHM_GATING_MAX_RATIO)))) - F16(VOC_ALGORITHM_GATING_MAX_RATIO))))); - if ((params->m_Mean_Variance_Estimator___Gating_Duration_Minutes < F16(0.))) { - params->m_Mean_Variance_Estimator___Gating_Duration_Minutes = F16(0.); + if ((params->m_Mean_Variance_Estimator_Gating_Duration_Minutes < F16(0.))) { + params->m_Mean_Variance_Estimator_Gating_Duration_Minutes = F16(0.); } - if ((params->m_Mean_Variance_Estimator___Gating_Duration_Minutes > - params->m_Mean_Variance_Estimator__Gating_Max_Duration_Minutes)) { - params->m_Mean_Variance_Estimator___Uptime_Gating = F16(0.); + if ((params->m_Mean_Variance_Estimator_Gating_Duration_Minutes > + params->m_Mean_Variance_Estimator_Gating_Max_Duration_Minutes)) { + params->m_Mean_Variance_Estimator_Uptime_Gating = F16(0.); } } -static void voc_algorithm_mean_variance_estimator_process(VocAlgorithmParams* params, fix16_t sraw, +static void voc_algorithm_mean_variance_estimator_process(VocAlgorithmParams *params, fix16_t sraw, fix16_t voc_index_from_prior) { fix16_t delta_sgp; fix16_t c; fix16_t additional_scaling; - if ((!params->m_Mean_Variance_Estimator___Initialized)) { - params->m_Mean_Variance_Estimator___Initialized = true; - params->m_Mean_Variance_Estimator___Sraw_Offset = sraw; - params->m_Mean_Variance_Estimator___Mean = F16(0.); + if ((!params->m_Mean_Variance_Estimator_Initialized)) { + params->m_Mean_Variance_Estimator_Initialized = true; + params->m_Mean_Variance_Estimator_Sraw_Offset = sraw; + params->m_Mean_Variance_Estimator_Mean = F16(0.); } else { - if (((params->m_Mean_Variance_Estimator___Mean >= F16(100.)) || - (params->m_Mean_Variance_Estimator___Mean <= F16(-100.)))) { - params->m_Mean_Variance_Estimator___Sraw_Offset = - (params->m_Mean_Variance_Estimator___Sraw_Offset + params->m_Mean_Variance_Estimator___Mean); - params->m_Mean_Variance_Estimator___Mean = F16(0.); + if (((params->m_Mean_Variance_Estimator_Mean >= F16(100.)) || + (params->m_Mean_Variance_Estimator_Mean <= F16(-100.)))) { + params->m_Mean_Variance_Estimator_Sraw_Offset = + (params->m_Mean_Variance_Estimator_Sraw_Offset + params->m_Mean_Variance_Estimator_Mean); + params->m_Mean_Variance_Estimator_Mean = F16(0.); } - sraw = (sraw - params->m_Mean_Variance_Estimator___Sraw_Offset); + sraw = (sraw - params->m_Mean_Variance_Estimator_Sraw_Offset); voc_algorithm_mean_variance_estimator_calculate_gamma(params, voc_index_from_prior); - delta_sgp = (fix16_div((sraw - params->m_Mean_Variance_Estimator___Mean), + delta_sgp = (fix16_div((sraw - params->m_Mean_Variance_Estimator_Mean), F16(VOC_ALGORITHM_MEAN_VARIANCE_ESTIMATOR_GAMMA_SCALING))); if ((delta_sgp < F16(0.))) { - c = (params->m_Mean_Variance_Estimator___Std - delta_sgp); + c = (params->m_Mean_Variance_Estimator_Std - delta_sgp); } else { - c = (params->m_Mean_Variance_Estimator___Std + delta_sgp); + c = (params->m_Mean_Variance_Estimator_Std + delta_sgp); } additional_scaling = F16(1.); if ((c > F16(1440.))) { additional_scaling = F16(4.); } - params->m_Mean_Variance_Estimator___Std = (fix16_mul( + params->m_Mean_Variance_Estimator_Std = (fix16_mul( fix16_sqrt((fix16_mul(additional_scaling, (F16(VOC_ALGORITHM_MEAN_VARIANCE_ESTIMATOR_GAMMA_SCALING) - - params->m_Mean_Variance_Estimator__Gamma_Variance)))), - fix16_sqrt(((fix16_mul(params->m_Mean_Variance_Estimator___Std, - (fix16_div(params->m_Mean_Variance_Estimator___Std, + params->m_Mean_Variance_Estimator_Gamma_Variance)))), + fix16_sqrt(((fix16_mul(params->m_Mean_Variance_Estimator_Std, + (fix16_div(params->m_Mean_Variance_Estimator_Std, (fix16_mul(F16(VOC_ALGORITHM_MEAN_VARIANCE_ESTIMATOR_GAMMA_SCALING), additional_scaling)))))) + - (fix16_mul((fix16_div((fix16_mul(params->m_Mean_Variance_Estimator__Gamma_Variance, delta_sgp)), + (fix16_mul((fix16_div((fix16_mul(params->m_Mean_Variance_Estimator_Gamma_Variance, delta_sgp)), additional_scaling)), delta_sgp)))))); - params->m_Mean_Variance_Estimator___Mean = (params->m_Mean_Variance_Estimator___Mean + - (fix16_mul(params->m_Mean_Variance_Estimator__Gamma_Mean, delta_sgp))); + params->m_Mean_Variance_Estimator_Mean = + (params->m_Mean_Variance_Estimator_Mean + (fix16_mul(params->m_Mean_Variance_Estimator_Gamma_Mean, delta_sgp))); } } -static void voc_algorithm_mean_variance_estimator_sigmoid_init(VocAlgorithmParams* params) { +static void voc_algorithm_mean_variance_estimator_sigmoid_init(VocAlgorithmParams *params) { voc_algorithm_mean_variance_estimator_sigmoid_set_parameters(params, F16(0.), F16(0.), F16(0.)); } -static void voc_algorithm_mean_variance_estimator_sigmoid_set_parameters(VocAlgorithmParams* params, fix16_t l, +static void voc_algorithm_mean_variance_estimator_sigmoid_set_parameters(VocAlgorithmParams *params, fix16_t l, fix16_t x0, fix16_t k) { - params->m_Mean_Variance_Estimator___Sigmoid__L = l; - params->m_Mean_Variance_Estimator___Sigmoid__K = k; - params->m_Mean_Variance_Estimator___Sigmoid__X0 = x0; + params->m_Mean_Variance_Estimator_Sigmoid_L = l; + params->m_Mean_Variance_Estimator_Sigmoid_K = k; + params->m_Mean_Variance_Estimator_Sigmoid_X0 = x0; } -static fix16_t voc_algorithm_mean_variance_estimator_sigmoid_process(VocAlgorithmParams* params, fix16_t sample) { +static fix16_t voc_algorithm_mean_variance_estimator_sigmoid_process(VocAlgorithmParams *params, fix16_t sample) { fix16_t x; - x = (fix16_mul(params->m_Mean_Variance_Estimator___Sigmoid__K, - (sample - params->m_Mean_Variance_Estimator___Sigmoid__X0))); + x = (fix16_mul(params->m_Mean_Variance_Estimator_Sigmoid_K, (sample - params->m_Mean_Variance_Estimator_Sigmoid_X0))); if ((x < F16(-50.))) { - return params->m_Mean_Variance_Estimator___Sigmoid__L; + return params->m_Mean_Variance_Estimator_Sigmoid_L; } else if ((x > F16(50.))) { return F16(0.); } else { - return (fix16_div(params->m_Mean_Variance_Estimator___Sigmoid__L, (F16(1.) + fix16_exp(x)))); + return (fix16_div(params->m_Mean_Variance_Estimator_Sigmoid_L, (F16(1.) + fix16_exp(x)))); } } -static void voc_algorithm_mox_model_init(VocAlgorithmParams* params) { +static void voc_algorithm_mox_model_init(VocAlgorithmParams *params) { voc_algorithm_mox_model_set_parameters(params, F16(1.), F16(0.)); } -static void voc_algorithm_mox_model_set_parameters(VocAlgorithmParams* params, fix16_t sraw_std, fix16_t sraw_mean) { - params->m_Mox_Model__Sraw_Std = sraw_std; - params->m_Mox_Model__Sraw_Mean = sraw_mean; +static void voc_algorithm_mox_model_set_parameters(VocAlgorithmParams *params, fix16_t sraw_std, fix16_t sraw_mean) { + params->m_Mox_Model_Sraw_Std = sraw_std; + params->m_Mox_Model_Sraw_Mean = sraw_mean; } -static fix16_t voc_algorithm_mox_model_process(VocAlgorithmParams* params, fix16_t sraw) { - return (fix16_mul((fix16_div((sraw - params->m_Mox_Model__Sraw_Mean), - (-(params->m_Mox_Model__Sraw_Std + F16(VOC_ALGORITHM_SRAW_STD_BONUS))))), +static fix16_t voc_algorithm_mox_model_process(VocAlgorithmParams *params, fix16_t sraw) { + return (fix16_mul((fix16_div((sraw - params->m_Mox_Model_Sraw_Mean), + (-(params->m_Mox_Model_Sraw_Std + F16(VOC_ALGORITHM_SRAW_STD_BONUS))))), F16(VOC_ALGORITHM_VOC_INDEX_GAIN))); } -static void voc_algorithm_sigmoid_scaled_init(VocAlgorithmParams* params) { +static void voc_algorithm_sigmoid_scaled_init(VocAlgorithmParams *params) { voc_algorithm_sigmoid_scaled_set_parameters(params, F16(0.)); } -static void voc_algorithm_sigmoid_scaled_set_parameters(VocAlgorithmParams* params, fix16_t offset) { - params->m_Sigmoid_Scaled__Offset = offset; +static void voc_algorithm_sigmoid_scaled_set_parameters(VocAlgorithmParams *params, fix16_t offset) { + params->m_Sigmoid_Scaled_Offset = offset; } -static fix16_t voc_algorithm_sigmoid_scaled_process(VocAlgorithmParams* params, fix16_t sample) { +static fix16_t voc_algorithm_sigmoid_scaled_process(VocAlgorithmParams *params, fix16_t sample) { fix16_t x; fix16_t shift; @@ -574,46 +573,46 @@ static fix16_t voc_algorithm_sigmoid_scaled_process(VocAlgorithmParams* params, } else { if ((sample >= F16(0.))) { shift = - (fix16_div((F16(VOC_ALGORITHM_SIGMOID_L) - (fix16_mul(F16(5.), params->m_Sigmoid_Scaled__Offset))), F16(4.))); + (fix16_div((F16(VOC_ALGORITHM_SIGMOID_L) - (fix16_mul(F16(5.), params->m_Sigmoid_Scaled_Offset))), F16(4.))); return ((fix16_div((F16(VOC_ALGORITHM_SIGMOID_L) + shift), (F16(1.) + fix16_exp(x)))) - shift); } else { - return (fix16_mul((fix16_div(params->m_Sigmoid_Scaled__Offset, F16(VOC_ALGORITHM_VOC_INDEX_OFFSET_DEFAULT))), + return (fix16_mul((fix16_div(params->m_Sigmoid_Scaled_Offset, F16(VOC_ALGORITHM_VOC_INDEX_OFFSET_DEFAULT))), (fix16_div(F16(VOC_ALGORITHM_SIGMOID_L), (F16(1.) + fix16_exp(x)))))); } } } -static void voc_algorithm_adaptive_lowpass_init(VocAlgorithmParams* params) { +static void voc_algorithm_adaptive_lowpass_init(VocAlgorithmParams *params) { voc_algorithm_adaptive_lowpass_set_parameters(params); } -static void voc_algorithm_adaptive_lowpass_set_parameters(VocAlgorithmParams* params) { - params->m_Adaptive_Lowpass__A1 = +static void voc_algorithm_adaptive_lowpass_set_parameters(VocAlgorithmParams *params) { + params->m_Adaptive_Lowpass_A1 = F16((VOC_ALGORITHM_SAMPLING_INTERVAL / (VOC_ALGORITHM_LP_TAU_FAST + VOC_ALGORITHM_SAMPLING_INTERVAL))); - params->m_Adaptive_Lowpass__A2 = + params->m_Adaptive_Lowpass_A2 = F16((VOC_ALGORITHM_SAMPLING_INTERVAL / (VOC_ALGORITHM_LP_TAU_SLOW + VOC_ALGORITHM_SAMPLING_INTERVAL))); - params->m_Adaptive_Lowpass___Initialized = false; + params->m_Adaptive_Lowpass_Initialized = false; } -static fix16_t voc_algorithm_adaptive_lowpass_process(VocAlgorithmParams* params, fix16_t sample) { +static fix16_t voc_algorithm_adaptive_lowpass_process(VocAlgorithmParams *params, fix16_t sample) { fix16_t abs_delta; fix16_t f1; fix16_t tau_a; fix16_t a3; - if ((!params->m_Adaptive_Lowpass___Initialized)) { - params->m_Adaptive_Lowpass___X1 = sample; - params->m_Adaptive_Lowpass___X2 = sample; - params->m_Adaptive_Lowpass___X3 = sample; - params->m_Adaptive_Lowpass___Initialized = true; + if ((!params->m_Adaptive_Lowpass_Initialized)) { + params->m_Adaptive_Lowpass_X1 = sample; + params->m_Adaptive_Lowpass_X2 = sample; + params->m_Adaptive_Lowpass_X3 = sample; + params->m_Adaptive_Lowpass_Initialized = true; } - params->m_Adaptive_Lowpass___X1 = - ((fix16_mul((F16(1.) - params->m_Adaptive_Lowpass__A1), params->m_Adaptive_Lowpass___X1)) + - (fix16_mul(params->m_Adaptive_Lowpass__A1, sample))); - params->m_Adaptive_Lowpass___X2 = - ((fix16_mul((F16(1.) - params->m_Adaptive_Lowpass__A2), params->m_Adaptive_Lowpass___X2)) + - (fix16_mul(params->m_Adaptive_Lowpass__A2, sample))); - abs_delta = (params->m_Adaptive_Lowpass___X1 - params->m_Adaptive_Lowpass___X2); + params->m_Adaptive_Lowpass_X1 = + ((fix16_mul((F16(1.) - params->m_Adaptive_Lowpass_A1), params->m_Adaptive_Lowpass_X1)) + + (fix16_mul(params->m_Adaptive_Lowpass_A1, sample))); + params->m_Adaptive_Lowpass_X2 = + ((fix16_mul((F16(1.) - params->m_Adaptive_Lowpass_A2), params->m_Adaptive_Lowpass_X2)) + + (fix16_mul(params->m_Adaptive_Lowpass_A2, sample))); + abs_delta = (params->m_Adaptive_Lowpass_X1 - params->m_Adaptive_Lowpass_X2); if ((abs_delta < F16(0.))) { abs_delta = (-abs_delta); } @@ -621,9 +620,9 @@ static fix16_t voc_algorithm_adaptive_lowpass_process(VocAlgorithmParams* params tau_a = ((fix16_mul(F16((VOC_ALGORITHM_LP_TAU_SLOW - VOC_ALGORITHM_LP_TAU_FAST)), f1)) + F16(VOC_ALGORITHM_LP_TAU_FAST)); a3 = (fix16_div(F16(VOC_ALGORITHM_SAMPLING_INTERVAL), (F16(VOC_ALGORITHM_SAMPLING_INTERVAL) + tau_a))); - params->m_Adaptive_Lowpass___X3 = - ((fix16_mul((F16(1.) - a3), params->m_Adaptive_Lowpass___X3)) + (fix16_mul(a3, sample))); - return params->m_Adaptive_Lowpass___X3; + params->m_Adaptive_Lowpass_X3 = + ((fix16_mul((F16(1.) - a3), params->m_Adaptive_Lowpass_X3)) + (fix16_mul(a3, sample))); + return params->m_Adaptive_Lowpass_X3; } } // namespace sgp40 } // namespace esphome diff --git a/esphome/components/sgp40/sensirion_voc_algorithm.h b/esphome/components/sgp40/sensirion_voc_algorithm.h index 05431635ad..adef6b29e8 100644 --- a/esphome/components/sgp40/sensirion_voc_algorithm.h +++ b/esphome/components/sgp40/sensirion_voc_algorithm.h @@ -1,5 +1,5 @@ #pragma once -#include +#include namespace esphome { namespace sgp40 { @@ -52,31 +52,31 @@ struct VocAlgorithmParams { fix16_t mUptime; fix16_t mSraw; fix16_t mVoc_Index; - fix16_t m_Mean_Variance_Estimator__Gating_Max_Duration_Minutes; - bool m_Mean_Variance_Estimator___Initialized; - fix16_t m_Mean_Variance_Estimator___Mean; - fix16_t m_Mean_Variance_Estimator___Sraw_Offset; - fix16_t m_Mean_Variance_Estimator___Std; - fix16_t m_Mean_Variance_Estimator___Gamma; - fix16_t m_Mean_Variance_Estimator___Gamma_Initial_Mean; - fix16_t m_Mean_Variance_Estimator___Gamma_Initial_Variance; - fix16_t m_Mean_Variance_Estimator__Gamma_Mean; - fix16_t m_Mean_Variance_Estimator__Gamma_Variance; - fix16_t m_Mean_Variance_Estimator___Uptime_Gamma; - fix16_t m_Mean_Variance_Estimator___Uptime_Gating; - fix16_t m_Mean_Variance_Estimator___Gating_Duration_Minutes; - fix16_t m_Mean_Variance_Estimator___Sigmoid__L; - fix16_t m_Mean_Variance_Estimator___Sigmoid__K; - fix16_t m_Mean_Variance_Estimator___Sigmoid__X0; - fix16_t m_Mox_Model__Sraw_Std; - fix16_t m_Mox_Model__Sraw_Mean; - fix16_t m_Sigmoid_Scaled__Offset; - fix16_t m_Adaptive_Lowpass__A1; - fix16_t m_Adaptive_Lowpass__A2; - bool m_Adaptive_Lowpass___Initialized; - fix16_t m_Adaptive_Lowpass___X1; - fix16_t m_Adaptive_Lowpass___X2; - fix16_t m_Adaptive_Lowpass___X3; + fix16_t m_Mean_Variance_Estimator_Gating_Max_Duration_Minutes; + bool m_Mean_Variance_Estimator_Initialized; + fix16_t m_Mean_Variance_Estimator_Mean; + fix16_t m_Mean_Variance_Estimator_Sraw_Offset; + fix16_t m_Mean_Variance_Estimator_Std; + fix16_t m_Mean_Variance_Estimator_Gamma; + fix16_t m_Mean_Variance_Estimator_Gamma_Initial_Mean; + fix16_t m_Mean_Variance_Estimator_Gamma_Initial_Variance; + fix16_t m_Mean_Variance_Estimator_Gamma_Mean; + fix16_t m_Mean_Variance_Estimator_Gamma_Variance; + fix16_t m_Mean_Variance_Estimator_Uptime_Gamma; + fix16_t m_Mean_Variance_Estimator_Uptime_Gating; + fix16_t m_Mean_Variance_Estimator_Gating_Duration_Minutes; + fix16_t m_Mean_Variance_Estimator_Sigmoid_L; + fix16_t m_Mean_Variance_Estimator_Sigmoid_K; + fix16_t m_Mean_Variance_Estimator_Sigmoid_X0; + fix16_t m_Mox_Model_Sraw_Std; + fix16_t m_Mox_Model_Sraw_Mean; + fix16_t m_Sigmoid_Scaled_Offset; + fix16_t m_Adaptive_Lowpass_A1; + fix16_t m_Adaptive_Lowpass_A2; + bool m_Adaptive_Lowpass_Initialized; + fix16_t m_Adaptive_Lowpass_X1; + fix16_t m_Adaptive_Lowpass_X2; + fix16_t m_Adaptive_Lowpass_X3; }; /** diff --git a/esphome/components/sgp40/sgp40.cpp b/esphome/components/sgp40/sgp40.cpp index cfd9766aa9..3b634353c4 100644 --- a/esphome/components/sgp40/sgp40.cpp +++ b/esphome/components/sgp40/sgp40.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace sgp40 { -static const char *TAG = "sgp40"; +static const char *const TAG = "sgp40"; void SGP40Component::setup() { ESP_LOGCONFIG(TAG, "Setting up SGP40..."); diff --git a/esphome/components/sgp40/sgp40.h b/esphome/components/sgp40/sgp40.h index d448b5e45c..b9ea365169 100644 --- a/esphome/components/sgp40/sgp40.h +++ b/esphome/components/sgp40/sgp40.h @@ -31,10 +31,10 @@ static const uint16_t SGP40_CMD_SELF_TEST = 0x280e; // Shortest time interval of 3H for storing baseline values. // Prevents wear of the flash because of too many write operations -const long SHORTEST_BASELINE_STORE_INTERVAL = 10800; +const uint32_t SHORTEST_BASELINE_STORE_INTERVAL = 10800; // Store anyway if the baseline difference exceeds the max storage diff value -const long MAXIMUM_STORAGE_DIFF = 50; +const uint32_t MAXIMUM_STORAGE_DIFF = 50; class SGP40Component; @@ -65,7 +65,7 @@ class SGP40Component : public PollingComponent, public sensor::Sensor, public i2 uint8_t generate_crc_(const uint8_t *data, uint8_t datalen); uint16_t measure_raw_(); ESPPreferenceObject pref_; - long seconds_since_last_store_; + int32_t seconds_since_last_store_; SGP40Baselines baselines_storage_; VocAlgorithmParams voc_algorithm_params_; bool store_baseline_; diff --git a/esphome/components/sht3xd/sht3xd.cpp b/esphome/components/sht3xd/sht3xd.cpp index 559fdc21ab..0bf2cc1a81 100644 --- a/esphome/components/sht3xd/sht3xd.cpp +++ b/esphome/components/sht3xd/sht3xd.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace sht3xd { -static const char *TAG = "sht3xd"; +static const char *const TAG = "sht3xd"; static const uint16_t SHT3XD_COMMAND_READ_SERIAL_NUMBER = 0x3780; static const uint16_t SHT3XD_COMMAND_READ_STATUS = 0xF32D; diff --git a/esphome/components/sht4x/sht4x.cpp b/esphome/components/sht4x/sht4x.cpp index a4b315940d..6d7c917b57 100644 --- a/esphome/components/sht4x/sht4x.cpp +++ b/esphome/components/sht4x/sht4x.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace sht4x { -static const char *TAG = "sht4x"; +static const char *const TAG = "sht4x"; static const uint8_t MEASURECOMMANDS[] = {0xFD, 0xF6, 0xE0}; diff --git a/esphome/components/shtcx/shtcx.cpp b/esphome/components/shtcx/shtcx.cpp index d67031febf..848da8d7d7 100644 --- a/esphome/components/shtcx/shtcx.cpp +++ b/esphome/components/shtcx/shtcx.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace shtcx { -static const char *TAG = "shtcx"; +static const char *const TAG = "shtcx"; static const uint16_t SHTCX_COMMAND_SLEEP = 0xB098; static const uint16_t SHTCX_COMMAND_WAKEUP = 0x3517; diff --git a/esphome/components/shutdown/shutdown_switch.cpp b/esphome/components/shutdown/shutdown_switch.cpp index ce33cd187f..3cc8ba9e1b 100644 --- a/esphome/components/shutdown/shutdown_switch.cpp +++ b/esphome/components/shutdown/shutdown_switch.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace shutdown { -static const char *TAG = "shutdown.switch"; +static const char *const TAG = "shutdown.switch"; void ShutdownSwitch::dump_config() { LOG_SWITCH("", "Shutdown Switch", this); } void ShutdownSwitch::write_state(bool state) { diff --git a/esphome/components/sim800l/sim800l.cpp b/esphome/components/sim800l/sim800l.cpp index ac4c8fc2d2..e48b1ac9bd 100644 --- a/esphome/components/sim800l/sim800l.cpp +++ b/esphome/components/sim800l/sim800l.cpp @@ -1,11 +1,11 @@ #include "sim800l.h" #include "esphome/core/log.h" -#include +#include namespace esphome { namespace sim800l { -static const char* TAG = "sim800l"; +static const char *const TAG = "sim800l"; const char ASCII_CR = 0x0D; const char ASCII_LF = 0x0A; @@ -40,7 +40,7 @@ void Sim800LComponent::update() { } } -void Sim800LComponent::send_cmd_(std::string message) { +void Sim800LComponent::send_cmd_(const std::string &message) { ESP_LOGV(TAG, "S: %s - %d", message.c_str(), this->state_); this->watch_dog_ = 0; this->write_str(message.c_str()); @@ -268,7 +268,7 @@ void Sim800LComponent::loop() { } } -void Sim800LComponent::send_sms(std::string recipient, std::string message) { +void Sim800LComponent::send_sms(const std::string &recipient, const std::string &message) { ESP_LOGD(TAG, "Sending to %s: %s", recipient.c_str(), message.c_str()); this->recipient_ = recipient; this->outgoing_message_ = message; @@ -279,7 +279,7 @@ void Sim800LComponent::dump_config() { ESP_LOGCONFIG(TAG, "SIM800L:"); ESP_LOGCONFIG(TAG, " RSSI: %d dB", this->rssi_); } -void Sim800LComponent::dial(std::string recipient) { +void Sim800LComponent::dial(const std::string &recipient) { ESP_LOGD(TAG, "Dialing %s", recipient.c_str()); this->recipient_ = recipient; this->dial_pending_ = true; diff --git a/esphome/components/sim800l/sim800l.h b/esphome/components/sim800l/sim800l.h index f8ccf88977..fa9c392bfc 100644 --- a/esphome/components/sim800l/sim800l.h +++ b/esphome/components/sim800l/sim800l.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "esphome/core/component.h" #include "esphome/components/uart/uart.h" #include "esphome/core/automation.h" @@ -43,11 +45,11 @@ class Sim800LComponent : public uart::UARTDevice, public PollingComponent { void add_on_sms_received_callback(std::function callback) { this->callback_.add(std::move(callback)); } - void send_sms(std::string recipient, std::string message); - void dial(std::string recipient); + void send_sms(const std::string &recipient, const std::string &message); + void dial(const std::string &recipient); protected: - void send_cmd_(std::string); + void send_cmd_(const std::string &); void parse_cmd_(std::string); std::string sender_; @@ -72,7 +74,7 @@ class Sim800LReceivedMessageTrigger : public Trigger { public: explicit Sim800LReceivedMessageTrigger(Sim800LComponent *parent) { parent->add_on_sms_received_callback( - [this](std::string message, std::string sender) { this->trigger(message, sender); }); + [this](std::string message, std::string sender) { this->trigger(std::move(message), std::move(sender)); }); } }; diff --git a/esphome/components/slow_pwm/slow_pwm_output.cpp b/esphome/components/slow_pwm/slow_pwm_output.cpp index 04a0d86bf7..9b2589e735 100644 --- a/esphome/components/slow_pwm/slow_pwm_output.cpp +++ b/esphome/components/slow_pwm/slow_pwm_output.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace slow_pwm { -static const char *TAG = "output.slow_pwm"; +static const char *const TAG = "output.slow_pwm"; void SlowPWMOutput::setup() { this->pin_->setup(); @@ -12,7 +12,7 @@ void SlowPWMOutput::setup() { } void SlowPWMOutput::loop() { - unsigned long now = millis(); + uint32_t now = millis(); float scaled_state = this->state_ * this->period_; if (now - this->period_start_time_ > this->period_) { diff --git a/esphome/components/sm16716/sm16716.cpp b/esphome/components/sm16716/sm16716.cpp index bc8e4fc1f4..373fbd4766 100644 --- a/esphome/components/sm16716/sm16716.cpp +++ b/esphome/components/sm16716/sm16716.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace sm16716 { -static const char *TAG = "sm16716"; +static const char *const TAG = "sm16716"; void SM16716::setup() { ESP_LOGCONFIG(TAG, "Setting up SM16716OutputComponent..."); diff --git a/esphome/components/sm2135/sm2135.cpp b/esphome/components/sm2135/sm2135.cpp index 3991c220ec..8bd64972c9 100644 --- a/esphome/components/sm2135/sm2135.cpp +++ b/esphome/components/sm2135/sm2135.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace sm2135 { -static const char *TAG = "sm2135"; +static const char *const TAG = "sm2135"; static const uint8_t SM2135_ADDR_MC = 0xC0; // Max current register static const uint8_t SM2135_ADDR_CH = 0xC1; // RGB or CW channel select register diff --git a/esphome/components/sm300d2/sm300d2.cpp b/esphome/components/sm300d2/sm300d2.cpp index 25abc82f3c..34d80349f9 100644 --- a/esphome/components/sm300d2/sm300d2.cpp +++ b/esphome/components/sm300d2/sm300d2.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace sm300d2 { -static const char *TAG = "sm300d2"; +static const char *const TAG = "sm300d2"; static const uint8_t SM300D2_RESPONSE_LENGTH = 17; void SM300D2Sensor::update() { diff --git a/esphome/components/sn74hc595/sn74hc595.cpp b/esphome/components/sn74hc595/sn74hc595.cpp index d82c5d5036..596ebf755d 100644 --- a/esphome/components/sn74hc595/sn74hc595.cpp +++ b/esphome/components/sn74hc595/sn74hc595.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace sn74hc595 { -static const char *TAG = "sn74hc595"; +static const char *const TAG = "sn74hc595"; void SN74HC595Component::setup() { ESP_LOGCONFIG(TAG, "Setting up SN74HC595..."); diff --git a/esphome/components/sntp/sntp_component.cpp b/esphome/components/sntp/sntp_component.cpp index 4050609dd8..b667f3b1ce 100644 --- a/esphome/components/sntp/sntp_component.cpp +++ b/esphome/components/sntp/sntp_component.cpp @@ -11,7 +11,7 @@ namespace esphome { namespace sntp { -static const char *TAG = "sntp"; +static const char *const TAG = "sntp"; void SNTPComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up SNTP..."); diff --git a/esphome/components/speed/fan/speed_fan.cpp b/esphome/components/speed/fan/speed_fan.cpp index 0455fa200d..8c6ec54d4c 100644 --- a/esphome/components/speed/fan/speed_fan.cpp +++ b/esphome/components/speed/fan/speed_fan.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace speed { -static const char *TAG = "speed.fan"; +static const char *const TAG = "speed.fan"; void SpeedFan::dump_config() { ESP_LOGCONFIG(TAG, "Fan '%s':", this->fan_->get_name().c_str()); diff --git a/esphome/components/spi/spi.cpp b/esphome/components/spi/spi.cpp index d2850b8e7d..a5d7fba30a 100644 --- a/esphome/components/spi/spi.cpp +++ b/esphome/components/spi/spi.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace spi { -static const char *TAG = "spi"; +static const char *const TAG = "spi"; void ICACHE_RAM_ATTR HOT SPIComponent::disable() { if (this->hw_spi_ != nullptr) { diff --git a/esphome/components/sps30/sps30.cpp b/esphome/components/sps30/sps30.cpp index 181bf44189..30bd134626 100644 --- a/esphome/components/sps30/sps30.cpp +++ b/esphome/components/sps30/sps30.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace sps30 { -static const char *TAG = "sps30"; +static const char *const TAG = "sps30"; static const uint16_t SPS30_CMD_GET_ARTICLE_CODE = 0xD025; static const uint16_t SPS30_CMD_GET_SERIAL_NUMBER = 0xD033; diff --git a/esphome/components/ssd1306_base/ssd1306_base.cpp b/esphome/components/ssd1306_base/ssd1306_base.cpp index 1537d3b526..10e66df784 100644 --- a/esphome/components/ssd1306_base/ssd1306_base.cpp +++ b/esphome/components/ssd1306_base/ssd1306_base.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace ssd1306_base { -static const char *TAG = "ssd1306"; +static const char *const TAG = "ssd1306"; static const uint8_t BLACK = 0; static const uint8_t WHITE = 1; @@ -130,7 +130,7 @@ void SSD1306::update() { } void SSD1306::set_brightness(float brightness) { // validation - this->brightness_ = clamp(brightness, 0, 1); + this->brightness_ = clamp(brightness, 0.0F, 1.0F); // now write the new brightness level to the display this->command(SSD1306_COMMAND_SET_CONTRAST); this->command(int(SSD1306_MAX_CONTRAST * (this->brightness_))); diff --git a/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp b/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp index 8c535094ac..fce9796008 100644 --- a/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp +++ b/esphome/components/ssd1306_i2c/ssd1306_i2c.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace ssd1306_i2c { -static const char *TAG = "ssd1306_i2c"; +static const char *const TAG = "ssd1306_i2c"; void I2CSSD1306::setup() { ESP_LOGCONFIG(TAG, "Setting up I2C SSD1306..."); diff --git a/esphome/components/ssd1306_spi/ssd1306_spi.cpp b/esphome/components/ssd1306_spi/ssd1306_spi.cpp index d87f412f70..5ef25b8139 100644 --- a/esphome/components/ssd1306_spi/ssd1306_spi.cpp +++ b/esphome/components/ssd1306_spi/ssd1306_spi.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace ssd1306_spi { -static const char *TAG = "ssd1306_spi"; +static const char *const TAG = "ssd1306_spi"; void SPISSD1306::setup() { ESP_LOGCONFIG(TAG, "Setting up SPI SSD1306..."); diff --git a/esphome/components/ssd1322_base/ssd1322_base.cpp b/esphome/components/ssd1322_base/ssd1322_base.cpp index 703f53fa93..007f61d5c8 100644 --- a/esphome/components/ssd1322_base/ssd1322_base.cpp +++ b/esphome/components/ssd1322_base/ssd1322_base.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace ssd1322_base { -static const char *TAG = "ssd1322"; +static const char *const TAG = "ssd1322"; static const uint8_t SSD1322_MAX_CONTRAST = 255; static const uint8_t SSD1322_COLORMASK = 0x0f; @@ -126,7 +126,7 @@ void SSD1322::update() { this->display(); } void SSD1322::set_brightness(float brightness) { - this->brightness_ = clamp(brightness, 0, 1); + this->brightness_ = clamp(brightness, 0.0F, 1.0F); // now write the new brightness level to the display this->command(SSD1322_SETCONTRAST); this->data(int(SSD1322_MAX_CONTRAST * (this->brightness_))); diff --git a/esphome/components/ssd1322_spi/ssd1322_spi.cpp b/esphome/components/ssd1322_spi/ssd1322_spi.cpp index 561a2e7a71..50c46c4d02 100644 --- a/esphome/components/ssd1322_spi/ssd1322_spi.cpp +++ b/esphome/components/ssd1322_spi/ssd1322_spi.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace ssd1322_spi { -static const char *TAG = "ssd1322_spi"; +static const char *const TAG = "ssd1322_spi"; void SPISSD1322::setup() { ESP_LOGCONFIG(TAG, "Setting up SPI SSD1322..."); diff --git a/esphome/components/ssd1325_base/ssd1325_base.cpp b/esphome/components/ssd1325_base/ssd1325_base.cpp index 9818880505..1cca1853b1 100644 --- a/esphome/components/ssd1325_base/ssd1325_base.cpp +++ b/esphome/components/ssd1325_base/ssd1325_base.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace ssd1325_base { -static const char *TAG = "ssd1325"; +static const char *const TAG = "ssd1325"; static const uint8_t BLACK = 0; static const uint8_t WHITE = 15; diff --git a/esphome/components/ssd1325_spi/ssd1325_spi.cpp b/esphome/components/ssd1325_spi/ssd1325_spi.cpp index 9df05439ca..98f48b8538 100644 --- a/esphome/components/ssd1325_spi/ssd1325_spi.cpp +++ b/esphome/components/ssd1325_spi/ssd1325_spi.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace ssd1325_spi { -static const char *TAG = "ssd1325_spi"; +static const char *const TAG = "ssd1325_spi"; void SPISSD1325::setup() { ESP_LOGCONFIG(TAG, "Setting up SPI SSD1325..."); diff --git a/esphome/components/ssd1327_base/ssd1327_base.cpp b/esphome/components/ssd1327_base/ssd1327_base.cpp index 398712c083..ae94be87df 100644 --- a/esphome/components/ssd1327_base/ssd1327_base.cpp +++ b/esphome/components/ssd1327_base/ssd1327_base.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace ssd1327_base { -static const char *TAG = "ssd1327"; +static const char *const TAG = "ssd1327"; static const uint8_t SSD1327_MAX_CONTRAST = 127; static const uint8_t SSD1327_COLORMASK = 0x0f; @@ -100,7 +100,7 @@ void SSD1327::update() { } void SSD1327::set_brightness(float brightness) { // validation - this->brightness_ = clamp(brightness, 0, 1); + this->brightness_ = clamp(brightness, 0.0F, 1.0F); // now write the new brightness level to the display this->command(SSD1327_SETCONTRAST); this->command(int(SSD1327_MAX_CONTRAST * (this->brightness_))); diff --git a/esphome/components/ssd1327_i2c/ssd1327_i2c.cpp b/esphome/components/ssd1327_i2c/ssd1327_i2c.cpp index 16cada0ef2..2967d2f9c4 100644 --- a/esphome/components/ssd1327_i2c/ssd1327_i2c.cpp +++ b/esphome/components/ssd1327_i2c/ssd1327_i2c.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace ssd1327_i2c { -static const char *TAG = "ssd1327_i2c"; +static const char *const TAG = "ssd1327_i2c"; void I2CSSD1327::setup() { ESP_LOGCONFIG(TAG, "Setting up I2C SSD1327..."); diff --git a/esphome/components/ssd1327_spi/ssd1327_spi.cpp b/esphome/components/ssd1327_spi/ssd1327_spi.cpp index c10ce6e9c8..1dd2b73e66 100644 --- a/esphome/components/ssd1327_spi/ssd1327_spi.cpp +++ b/esphome/components/ssd1327_spi/ssd1327_spi.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace ssd1327_spi { -static const char *TAG = "ssd1327_spi"; +static const char *const TAG = "ssd1327_spi"; void SPISSD1327::setup() { ESP_LOGCONFIG(TAG, "Setting up SPI SSD1327..."); diff --git a/esphome/components/ssd1331_base/ssd1331_base.cpp b/esphome/components/ssd1331_base/ssd1331_base.cpp index 9db5581044..f25ef50075 100644 --- a/esphome/components/ssd1331_base/ssd1331_base.cpp +++ b/esphome/components/ssd1331_base/ssd1331_base.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace ssd1331_base { -static const char *TAG = "ssd1331"; +static const char *const TAG = "ssd1331"; static const uint16_t BLACK = 0; static const uint16_t WHITE = 0xffff; @@ -97,7 +97,7 @@ void SSD1331::update() { } void SSD1331::set_brightness(float brightness) { // validation - this->brightness_ = clamp(brightness, 0, 1); + this->brightness_ = clamp(brightness, 0.0F, 1.0F); // now write the new brightness level to the display this->command(SSD1331_CONTRASTA); // 0x81 this->command(int(SSD1331_MAX_CONTRASTA * (this->brightness_))); diff --git a/esphome/components/ssd1331_spi/ssd1331_spi.cpp b/esphome/components/ssd1331_spi/ssd1331_spi.cpp index f618c6d368..ff42c74b9f 100644 --- a/esphome/components/ssd1331_spi/ssd1331_spi.cpp +++ b/esphome/components/ssd1331_spi/ssd1331_spi.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace ssd1331_spi { -static const char *TAG = "ssd1331_spi"; +static const char *const TAG = "ssd1331_spi"; void SPISSD1331::setup() { ESP_LOGCONFIG(TAG, "Setting up SPI SSD1331..."); diff --git a/esphome/components/ssd1351_base/ssd1351_base.cpp b/esphome/components/ssd1351_base/ssd1351_base.cpp index 42407a13de..34f357e38a 100644 --- a/esphome/components/ssd1351_base/ssd1351_base.cpp +++ b/esphome/components/ssd1351_base/ssd1351_base.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace ssd1351_base { -static const char *TAG = "ssd1351"; +static const char *const TAG = "ssd1351"; static const uint16_t BLACK = 0; static const uint16_t WHITE = 0xffff; diff --git a/esphome/components/ssd1351_spi/ssd1351_spi.cpp b/esphome/components/ssd1351_spi/ssd1351_spi.cpp index 2839ef7a8e..9599c6e644 100644 --- a/esphome/components/ssd1351_spi/ssd1351_spi.cpp +++ b/esphome/components/ssd1351_spi/ssd1351_spi.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace ssd1351_spi { -static const char *TAG = "ssd1351_spi"; +static const char *const TAG = "ssd1351_spi"; void SPISSD1351::setup() { ESP_LOGCONFIG(TAG, "Setting up SPI SSD1351..."); diff --git a/esphome/components/st7735/st7735.cpp b/esphome/components/st7735/st7735.cpp index 201098f3cf..f329ef4620 100644 --- a/esphome/components/st7735/st7735.cpp +++ b/esphome/components/st7735/st7735.cpp @@ -1,486 +1,486 @@ -#include "st7735.h" -#include "esphome/core/log.h" -#include "esphome/core/helpers.h" - -namespace esphome { -namespace st7735 { - -static const uint8_t ST_CMD_DELAY = 0x80; // special signifier for command lists - -static const uint8_t ST77XX_NOP = 0x00; -static const uint8_t ST77XX_SWRESET = 0x01; -static const uint8_t ST77XX_RDDID = 0x04; -static const uint8_t ST77XX_RDDST = 0x09; - -static const uint8_t ST77XX_SLPIN = 0x10; -static const uint8_t ST77XX_SLPOUT = 0x11; -static const uint8_t ST77XX_PTLON = 0x12; -static const uint8_t ST77XX_NORON = 0x13; - -static const uint8_t ST77XX_INVOFF = 0x20; -static const uint8_t ST77XX_INVON = 0x21; -static const uint8_t ST77XX_DISPOFF = 0x28; -static const uint8_t ST77XX_DISPON = 0x29; -static const uint8_t ST77XX_CASET = 0x2A; -static const uint8_t ST77XX_RASET = 0x2B; -static const uint8_t ST77XX_RAMWR = 0x2C; -static const uint8_t ST77XX_RAMRD = 0x2E; - -static const uint8_t ST77XX_PTLAR = 0x30; -static const uint8_t ST77XX_TEOFF = 0x34; -static const uint8_t ST77XX_TEON = 0x35; -static const uint8_t ST77XX_MADCTL = 0x36; -static const uint8_t ST77XX_COLMOD = 0x3A; - -static const uint8_t ST77XX_MADCTL_MY = 0x80; -static const uint8_t ST77XX_MADCTL_MX = 0x40; -static const uint8_t ST77XX_MADCTL_MV = 0x20; -static const uint8_t ST77XX_MADCTL_ML = 0x10; -static const uint8_t ST77XX_MADCTL_RGB = 0x00; - -static const uint8_t ST77XX_RDID1 = 0xDA; -static const uint8_t ST77XX_RDID2 = 0xDB; -static const uint8_t ST77XX_RDID3 = 0xDC; -static const uint8_t ST77XX_RDID4 = 0xDD; - -// Some register settings -static const uint8_t ST7735_MADCTL_BGR = 0x08; - -static const uint8_t ST7735_MADCTL_MH = 0x04; - -static const uint8_t ST7735_FRMCTR1 = 0xB1; -static const uint8_t ST7735_FRMCTR2 = 0xB2; -static const uint8_t ST7735_FRMCTR3 = 0xB3; -static const uint8_t ST7735_INVCTR = 0xB4; -static const uint8_t ST7735_DISSET5 = 0xB6; - -static const uint8_t ST7735_PWCTR1 = 0xC0; -static const uint8_t ST7735_PWCTR2 = 0xC1; -static const uint8_t ST7735_PWCTR3 = 0xC2; -static const uint8_t ST7735_PWCTR4 = 0xC3; -static const uint8_t ST7735_PWCTR5 = 0xC4; -static const uint8_t ST7735_VMCTR1 = 0xC5; - -static const uint8_t ST7735_PWCTR6 = 0xFC; - -static const uint8_t ST7735_GMCTRP1 = 0xE0; -static const uint8_t ST7735_GMCTRN1 = 0xE1; - -// clang-format off -static const uint8_t PROGMEM - BCMD[] = { // Init commands for 7735B screens - 18, // 18 commands in list: - ST77XX_SWRESET, ST_CMD_DELAY, // 1: Software reset, no args, w/delay - 50, // 50 ms delay - ST77XX_SLPOUT, ST_CMD_DELAY, // 2: Out of sleep mode, no args, w/delay - 255, // 255 = max (500 ms) delay - ST77XX_COLMOD, 1+ST_CMD_DELAY, // 3: Set color mode, 1 arg + delay: - 0x05, // 16-bit color - 10, // 10 ms delay - ST7735_FRMCTR1, 3+ST_CMD_DELAY, // 4: Frame rate control, 3 args + delay: - 0x00, // fastest refresh - 0x06, // 6 lines front porch - 0x03, // 3 lines back porch - 10, // 10 ms delay - ST77XX_MADCTL, 1, // 5: Mem access ctl (directions), 1 arg: - 0x08, // Row/col addr, bottom-top refresh - ST7735_DISSET5, 2, // 6: Display settings #5, 2 args: - 0x15, // 1 clk cycle nonoverlap, 2 cycle gate - // rise, 3 cycle osc equalize - 0x02, // Fix on VTL - ST7735_INVCTR, 1, // 7: Display inversion control, 1 arg: - 0x0, // Line inversion - ST7735_PWCTR1, 2+ST_CMD_DELAY, // 8: Power control, 2 args + delay: - 0x02, // GVDD = 4.7V - 0x70, // 1.0uA - 10, // 10 ms delay - ST7735_PWCTR2, 1, // 9: Power control, 1 arg, no delay: - 0x05, // VGH = 14.7V, VGL = -7.35V - ST7735_PWCTR3, 2, // 10: Power control, 2 args, no delay: - 0x01, // Opamp current small - 0x02, // Boost frequency - ST7735_VMCTR1, 2+ST_CMD_DELAY, // 11: Power control, 2 args + delay: - 0x3C, // VCOMH = 4V - 0x38, // VCOML = -1.1V - 10, // 10 ms delay - ST7735_PWCTR6, 2, // 12: Power control, 2 args, no delay: - 0x11, 0x15, - ST7735_GMCTRP1,16, // 13: Gamma Adjustments (pos. polarity), 16 args + delay: - 0x09, 0x16, 0x09, 0x20, // (Not entirely necessary, but provides - 0x21, 0x1B, 0x13, 0x19, // accurate colors) - 0x17, 0x15, 0x1E, 0x2B, - 0x04, 0x05, 0x02, 0x0E, - ST7735_GMCTRN1,16+ST_CMD_DELAY, // 14: Gamma Adjustments (neg. polarity), 16 args + delay: - 0x0B, 0x14, 0x08, 0x1E, // (Not entirely necessary, but provides - 0x22, 0x1D, 0x18, 0x1E, // accurate colors) - 0x1B, 0x1A, 0x24, 0x2B, - 0x06, 0x06, 0x02, 0x0F, - 10, // 10 ms delay - ST77XX_CASET, 4, // 15: Column addr set, 4 args, no delay: - 0x00, 0x02, // XSTART = 2 - 0x00, 0x81, // XEND = 129 - ST77XX_RASET, 4, // 16: Row addr set, 4 args, no delay: - 0x00, 0x02, // XSTART = 1 - 0x00, 0x81, // XEND = 160 - ST77XX_NORON, ST_CMD_DELAY, // 17: Normal display on, no args, w/delay - 10, // 10 ms delay - ST77XX_DISPON, ST_CMD_DELAY, // 18: Main screen turn on, no args, delay - 255 }, // 255 = max (500 ms) delay - - RCMD1[] = { // 7735R init, part 1 (red or green tab) - 15, // 15 commands in list: - ST77XX_SWRESET, ST_CMD_DELAY, // 1: Software reset, 0 args, w/delay - 150, // 150 ms delay - ST77XX_SLPOUT, ST_CMD_DELAY, // 2: Out of sleep mode, 0 args, w/delay - 255, // 500 ms delay - ST7735_FRMCTR1, 3, // 3: Framerate ctrl - normal mode, 3 arg: - 0x01, 0x2C, 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D) - ST7735_FRMCTR2, 3, // 4: Framerate ctrl - idle mode, 3 args: - 0x01, 0x2C, 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D) - ST7735_FRMCTR3, 6, // 5: Framerate - partial mode, 6 args: - 0x01, 0x2C, 0x2D, // Dot inversion mode - 0x01, 0x2C, 0x2D, // Line inversion mode - ST7735_INVCTR, 1, // 6: Display inversion ctrl, 1 arg: - 0x07, // No inversion - ST7735_PWCTR1, 3, // 7: Power control, 3 args, no delay: - 0xA2, - 0x02, // -4.6V - 0x84, // AUTO mode - ST7735_PWCTR2, 1, // 8: Power control, 1 arg, no delay: - 0xC5, // VGH25=2.4C VGSEL=-10 VGH=3 * AVDD - ST7735_PWCTR3, 2, // 9: Power control, 2 args, no delay: - 0x0A, // Opamp current small - 0x00, // Boost frequency - ST7735_PWCTR4, 2, // 10: Power control, 2 args, no delay: - 0x8A, // BCLK/2, - 0x2A, // opamp current small & medium low - ST7735_PWCTR5, 2, // 11: Power control, 2 args, no delay: - 0x8A, 0xEE, - ST7735_VMCTR1, 1, // 12: Power control, 1 arg, no delay: - 0x0E, - ST77XX_INVOFF, 0, // 13: Don't invert display, no args - ST77XX_MADCTL, 1, // 14: Mem access ctl (directions), 1 arg: - 0xC8, // row/col addr, bottom-top refresh - ST77XX_COLMOD, 1, // 15: set color mode, 1 arg, no delay: - 0x05 }, // 16-bit color - - RCMD2GREEN[] = { // 7735R init, part 2 (green tab only) - 2, // 2 commands in list: - ST77XX_CASET, 4, // 1: Column addr set, 4 args, no delay: - 0x00, 0x02, // XSTART = 0 - 0x00, 0x7F+0x02, // XEND = 127 - ST77XX_RASET, 4, // 2: Row addr set, 4 args, no delay: - 0x00, 0x01, // XSTART = 0 - 0x00, 0x9F+0x01 }, // XEND = 159 - - RCMD2RED[] = { // 7735R init, part 2 (red tab only) - 2, // 2 commands in list: - ST77XX_CASET, 4, // 1: Column addr set, 4 args, no delay: - 0x00, 0x00, // XSTART = 0 - 0x00, 0x7F, // XEND = 127 - ST77XX_RASET, 4, // 2: Row addr set, 4 args, no delay: - 0x00, 0x00, // XSTART = 0 - 0x00, 0x9F }, // XEND = 159 - - RCMD2GREEN144[] = { // 7735R init, part 2 (green 1.44 tab) - 2, // 2 commands in list: - ST77XX_CASET, 4, // 1: Column addr set, 4 args, no delay: - 0x00, 0x00, // XSTART = 0 - 0x00, 0x7F, // XEND = 127 - ST77XX_RASET, 4, // 2: Row addr set, 4 args, no delay: - 0x00, 0x00, // XSTART = 0 - 0x00, 0x7F }, // XEND = 127 - - RCMD2GREEN160X80[] = { // 7735R init, part 2 (mini 160x80) - 2, // 2 commands in list: - ST77XX_CASET, 4, // 1: Column addr set, 4 args, no delay: - 0x00, 0x00, // XSTART = 0 - 0x00, 0x4F, // XEND = 79 - ST77XX_RASET, 4, // 2: Row addr set, 4 args, no delay: - 0x00, 0x00, // XSTART = 0 - 0x00, 0x9F }, // XEND = 159 - - RCMD3[] = { // 7735R init, part 3 (red or green tab) - 4, // 4 commands in list: - ST7735_GMCTRP1, 16 , // 1: Gamma Adjustments (pos. polarity), 16 args + delay: - 0x02, 0x1c, 0x07, 0x12, // (Not entirely necessary, but provides - 0x37, 0x32, 0x29, 0x2d, // accurate colors) - 0x29, 0x25, 0x2B, 0x39, - 0x00, 0x01, 0x03, 0x10, - ST7735_GMCTRN1, 16 , // 2: Gamma Adjustments (neg. polarity), 16 args + delay: - 0x03, 0x1d, 0x07, 0x06, // (Not entirely necessary, but provides - 0x2E, 0x2C, 0x29, 0x2D, // accurate colors) - 0x2E, 0x2E, 0x37, 0x3F, - 0x00, 0x00, 0x02, 0x10, - ST77XX_NORON, ST_CMD_DELAY, // 3: Normal display on, no args, w/delay - 10, // 10 ms delay - ST77XX_DISPON, ST_CMD_DELAY, // 4: Main screen turn on, no args w/delay - 100 }; // 100 ms delay - -// clang-format on -static const char *TAG = "st7735"; - -ST7735::ST7735(ST7735Model model, int width, int height, int colstart, int rowstart, boolean eightbitcolor, - boolean usebgr) { - model_ = model; - this->width_ = width; - this->height_ = height; - this->colstart_ = colstart; - this->rowstart_ = rowstart; - this->eightbitcolor_ = eightbitcolor; - this->usebgr_ = usebgr; -} - -void ST7735::setup() { - ESP_LOGCONFIG(TAG, "Setting up ST7735..."); - this->spi_setup(); - - this->dc_pin_->setup(); // OUTPUT - this->cs_->setup(); // OUTPUT - - this->dc_pin_->digital_write(true); - this->cs_->digital_write(true); - - this->init_reset_(); - delay(100); // NOLINT - - ESP_LOGD(TAG, " START"); - dump_config(); - ESP_LOGD(TAG, " END"); - - display_init_(RCMD1); - - if (this->model_ == INITR_GREENTAB) { - display_init_(RCMD2GREEN); - colstart_ == 0 ? colstart_ = 2 : colstart_; - rowstart_ == 0 ? rowstart_ = 1 : rowstart_; - } else if ((this->model_ == INITR_144GREENTAB) || (this->model_ == INITR_HALLOWING)) { - height_ == 0 ? height_ = ST7735_TFTHEIGHT_128 : height_; - width_ == 0 ? width_ = ST7735_TFTWIDTH_128 : width_; - display_init_(RCMD2GREEN144); - colstart_ == 0 ? colstart_ = 2 : colstart_; - rowstart_ == 0 ? rowstart_ = 3 : rowstart_; - } else if (this->model_ == INITR_MINI_160X80) { - height_ == 0 ? height_ = ST7735_TFTHEIGHT_160 : height_; - width_ == 0 ? width_ = ST7735_TFTWIDTH_80 : width_; - display_init_(RCMD2GREEN160X80); - colstart_ = 24; - rowstart_ = 0; // For default rotation 0 - } else { - // colstart, rowstart left at default '0' values - display_init_(RCMD2RED); - } - display_init_(RCMD3); - - uint8_t data = 0; - if (this->model_ != INITR_HALLOWING) { - uint8_t data = ST77XX_MADCTL_MX | ST77XX_MADCTL_MY; - } - if (this->usebgr_) { - data = data | ST7735_MADCTL_BGR; - } else { - data = data | ST77XX_MADCTL_RGB; - } - sendcommand_(ST77XX_MADCTL, &data, 1); - - this->init_internal_(this->get_buffer_length()); - memset(this->buffer_, 0x00, this->get_buffer_length()); -} - -void ST7735::update() { - this->do_update_(); - this->write_display_data_(); -} - -int ST7735::get_height_internal() { return height_; } - -int ST7735::get_width_internal() { return width_; } - -size_t ST7735::get_buffer_length() { - if (this->eightbitcolor_) { - return size_t(this->get_width_internal()) * size_t(this->get_height_internal()); - } - return size_t(this->get_width_internal()) * size_t(this->get_height_internal()) * 2; -} - -void HOT ST7735::draw_absolute_pixel_internal(int x, int y, Color color) { - if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) - return; - - if (this->eightbitcolor_) { - const uint32_t color332 = display::ColorUtil::color_to_332(color); - uint16_t pos = (x + y * this->get_width_internal()); - this->buffer_[pos] = color332; - } else { - const uint32_t color565 = display::ColorUtil::color_to_565(color); - uint16_t pos = (x + y * this->get_width_internal()) * 2; - this->buffer_[pos++] = (color565 >> 8) & 0xff; - this->buffer_[pos] = color565 & 0xff; - } -} - -void ST7735::init_reset_() { - if (this->reset_pin_ != nullptr) { - this->reset_pin_->setup(); - this->reset_pin_->digital_write(true); - delay(1); - // Trigger Reset - this->reset_pin_->digital_write(false); - delay(10); - // Wake up - this->reset_pin_->digital_write(true); - } -} -const char *ST7735::model_str_() { - switch (this->model_) { - case INITR_GREENTAB: - return "ST7735 GREENTAB"; - case INITR_REDTAB: - return "ST7735 REDTAB"; - case INITR_BLACKTAB: - return "ST7735 BLACKTAB"; - case INITR_MINI_160X80: - return "ST7735 MINI160x80"; - default: - return "Unknown"; - } -} - -void ST7735::display_init_(const uint8_t *addr) { - uint8_t num_commands, cmd, num_args; - uint16_t ms; - - num_commands = pgm_read_byte(addr++); // Number of commands to follow - while (num_commands--) { // For each command... - cmd = pgm_read_byte(addr++); // Read command - num_args = pgm_read_byte(addr++); // Number of args to follow - ms = num_args & ST_CMD_DELAY; // If hibit set, delay follows args - num_args &= ~ST_CMD_DELAY; // Mask out delay bit - this->sendcommand_(cmd, addr, num_args); - addr += num_args; - - if (ms) { - ms = pgm_read_byte(addr++); // Read post-command delay time (ms) - if (ms == 255) - ms = 500; // If 255, delay for 500 ms - delay(ms); - } - } -} - -void ST7735::dump_config() { - LOG_DISPLAY("", "ST7735", this); - ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_()); - LOG_PIN(" CS Pin: ", this->cs_); - LOG_PIN(" DC Pin: ", this->dc_pin_); - LOG_PIN(" Reset Pin: ", this->reset_pin_); - ESP_LOGD(TAG, " Buffer Size: %zu", this->get_buffer_length()); - ESP_LOGD(TAG, " Height: %d", this->height_); - ESP_LOGD(TAG, " Width: %d", this->width_); - ESP_LOGD(TAG, " ColStart: %d", this->colstart_); - ESP_LOGD(TAG, " RowStart: %d", this->rowstart_); - LOG_UPDATE_INTERVAL(this); -} - -void HOT ST7735::writecommand_(uint8_t value) { - this->enable(); - this->dc_pin_->digital_write(false); - this->write_byte(value); - this->dc_pin_->digital_write(true); - this->disable(); -} - -void HOT ST7735::writedata_(uint8_t value) { - this->dc_pin_->digital_write(true); - this->enable(); - this->write_byte(value); - this->disable(); -} - -void HOT ST7735::sendcommand_(uint8_t cmd, const uint8_t *data_bytes, uint8_t num_data_bytes) { - this->writecommand_(cmd); - this->senddata_(data_bytes, num_data_bytes); -} - -void HOT ST7735::senddata_(const uint8_t *data_bytes, uint8_t num_data_bytes) { - this->dc_pin_->digital_write(true); // pull DC high to indicate data - this->cs_->digital_write(false); - this->enable(); - for (uint8_t i = 0; i < num_data_bytes; i++) { - this->write_byte(pgm_read_byte(data_bytes++)); // write byte - SPI library - } - this->cs_->digital_write(true); - this->disable(); -} - -void HOT ST7735::write_display_data_() { - uint16_t offsetx = colstart_; - uint16_t offsety = rowstart_; - - uint16_t x1 = offsetx; - uint16_t x2 = x1 + get_width_internal() - 1; - uint16_t y1 = offsety; - uint16_t y2 = y1 + get_height_internal() - 1; - - this->enable(); - - // set column(x) address - this->dc_pin_->digital_write(false); - this->write_byte(ST77XX_CASET); - this->dc_pin_->digital_write(true); - this->spi_master_write_addr_(x1, x2); - - // set Page(y) address - this->dc_pin_->digital_write(false); - this->write_byte(ST77XX_RASET); - this->dc_pin_->digital_write(true); - this->spi_master_write_addr_(y1, y2); - - // Memory Write - this->dc_pin_->digital_write(false); - this->write_byte(ST77XX_RAMWR); - this->dc_pin_->digital_write(true); - - if (this->eightbitcolor_) { - for (int line = 0; line < this->get_buffer_length(); line = line + this->get_width_internal()) { - for (int index = 0; index < this->get_width_internal(); ++index) { - auto color332 = display::ColorUtil::to_color(this->buffer_[index + line], display::ColorOrder::COLOR_ORDER_RGB, - display::ColorBitness::COLOR_BITNESS_332, true); - - auto color = display::ColorUtil::color_to_565(color332); - - this->write_byte((color >> 8) & 0xff); - this->write_byte(color & 0xff); - } - } - } else { - this->write_array(this->buffer_, this->get_buffer_length()); - } - this->disable(); -} - -void ST7735::spi_master_write_addr_(uint16_t addr1, uint16_t addr2) { - static uint8_t BYTE[4]; - BYTE[0] = (addr1 >> 8) & 0xFF; - BYTE[1] = addr1 & 0xFF; - BYTE[2] = (addr2 >> 8) & 0xFF; - BYTE[3] = addr2 & 0xFF; - - this->dc_pin_->digital_write(true); - this->write_array(BYTE, 4); -} - -void ST7735::spi_master_write_color_(uint16_t color, uint16_t size) { - static uint8_t BYTE[1024]; - int index = 0; - for (int i = 0; i < size; i++) { - BYTE[index++] = (color >> 8) & 0xFF; - BYTE[index++] = color & 0xFF; - } - - this->dc_pin_->digital_write(true); - return write_array(BYTE, size * 2); -} - -} // namespace st7735 -} // namespace esphome +#include "st7735.h" +#include "esphome/core/log.h" +#include "esphome/core/helpers.h" + +namespace esphome { +namespace st7735 { + +static const uint8_t ST_CMD_DELAY = 0x80; // special signifier for command lists + +static const uint8_t ST77XX_NOP = 0x00; +static const uint8_t ST77XX_SWRESET = 0x01; +static const uint8_t ST77XX_RDDID = 0x04; +static const uint8_t ST77XX_RDDST = 0x09; + +static const uint8_t ST77XX_SLPIN = 0x10; +static const uint8_t ST77XX_SLPOUT = 0x11; +static const uint8_t ST77XX_PTLON = 0x12; +static const uint8_t ST77XX_NORON = 0x13; + +static const uint8_t ST77XX_INVOFF = 0x20; +static const uint8_t ST77XX_INVON = 0x21; +static const uint8_t ST77XX_DISPOFF = 0x28; +static const uint8_t ST77XX_DISPON = 0x29; +static const uint8_t ST77XX_CASET = 0x2A; +static const uint8_t ST77XX_RASET = 0x2B; +static const uint8_t ST77XX_RAMWR = 0x2C; +static const uint8_t ST77XX_RAMRD = 0x2E; + +static const uint8_t ST77XX_PTLAR = 0x30; +static const uint8_t ST77XX_TEOFF = 0x34; +static const uint8_t ST77XX_TEON = 0x35; +static const uint8_t ST77XX_MADCTL = 0x36; +static const uint8_t ST77XX_COLMOD = 0x3A; + +static const uint8_t ST77XX_MADCTL_MY = 0x80; +static const uint8_t ST77XX_MADCTL_MX = 0x40; +static const uint8_t ST77XX_MADCTL_MV = 0x20; +static const uint8_t ST77XX_MADCTL_ML = 0x10; +static const uint8_t ST77XX_MADCTL_RGB = 0x00; + +static const uint8_t ST77XX_RDID1 = 0xDA; +static const uint8_t ST77XX_RDID2 = 0xDB; +static const uint8_t ST77XX_RDID3 = 0xDC; +static const uint8_t ST77XX_RDID4 = 0xDD; + +// Some register settings +static const uint8_t ST7735_MADCTL_BGR = 0x08; + +static const uint8_t ST7735_MADCTL_MH = 0x04; + +static const uint8_t ST7735_FRMCTR1 = 0xB1; +static const uint8_t ST7735_FRMCTR2 = 0xB2; +static const uint8_t ST7735_FRMCTR3 = 0xB3; +static const uint8_t ST7735_INVCTR = 0xB4; +static const uint8_t ST7735_DISSET5 = 0xB6; + +static const uint8_t ST7735_PWCTR1 = 0xC0; +static const uint8_t ST7735_PWCTR2 = 0xC1; +static const uint8_t ST7735_PWCTR3 = 0xC2; +static const uint8_t ST7735_PWCTR4 = 0xC3; +static const uint8_t ST7735_PWCTR5 = 0xC4; +static const uint8_t ST7735_VMCTR1 = 0xC5; + +static const uint8_t ST7735_PWCTR6 = 0xFC; + +static const uint8_t ST7735_GMCTRP1 = 0xE0; +static const uint8_t ST7735_GMCTRN1 = 0xE1; + +// clang-format off +static const uint8_t PROGMEM + BCMD[] = { // Init commands for 7735B screens + 18, // 18 commands in list: + ST77XX_SWRESET, ST_CMD_DELAY, // 1: Software reset, no args, w/delay + 50, // 50 ms delay + ST77XX_SLPOUT, ST_CMD_DELAY, // 2: Out of sleep mode, no args, w/delay + 255, // 255 = max (500 ms) delay + ST77XX_COLMOD, 1+ST_CMD_DELAY, // 3: Set color mode, 1 arg + delay: + 0x05, // 16-bit color + 10, // 10 ms delay + ST7735_FRMCTR1, 3+ST_CMD_DELAY, // 4: Frame rate control, 3 args + delay: + 0x00, // fastest refresh + 0x06, // 6 lines front porch + 0x03, // 3 lines back porch + 10, // 10 ms delay + ST77XX_MADCTL, 1, // 5: Mem access ctl (directions), 1 arg: + 0x08, // Row/col addr, bottom-top refresh + ST7735_DISSET5, 2, // 6: Display settings #5, 2 args: + 0x15, // 1 clk cycle nonoverlap, 2 cycle gate + // rise, 3 cycle osc equalize + 0x02, // Fix on VTL + ST7735_INVCTR, 1, // 7: Display inversion control, 1 arg: + 0x0, // Line inversion + ST7735_PWCTR1, 2+ST_CMD_DELAY, // 8: Power control, 2 args + delay: + 0x02, // GVDD = 4.7V + 0x70, // 1.0uA + 10, // 10 ms delay + ST7735_PWCTR2, 1, // 9: Power control, 1 arg, no delay: + 0x05, // VGH = 14.7V, VGL = -7.35V + ST7735_PWCTR3, 2, // 10: Power control, 2 args, no delay: + 0x01, // Opamp current small + 0x02, // Boost frequency + ST7735_VMCTR1, 2+ST_CMD_DELAY, // 11: Power control, 2 args + delay: + 0x3C, // VCOMH = 4V + 0x38, // VCOML = -1.1V + 10, // 10 ms delay + ST7735_PWCTR6, 2, // 12: Power control, 2 args, no delay: + 0x11, 0x15, + ST7735_GMCTRP1,16, // 13: Gamma Adjustments (pos. polarity), 16 args + delay: + 0x09, 0x16, 0x09, 0x20, // (Not entirely necessary, but provides + 0x21, 0x1B, 0x13, 0x19, // accurate colors) + 0x17, 0x15, 0x1E, 0x2B, + 0x04, 0x05, 0x02, 0x0E, + ST7735_GMCTRN1,16+ST_CMD_DELAY, // 14: Gamma Adjustments (neg. polarity), 16 args + delay: + 0x0B, 0x14, 0x08, 0x1E, // (Not entirely necessary, but provides + 0x22, 0x1D, 0x18, 0x1E, // accurate colors) + 0x1B, 0x1A, 0x24, 0x2B, + 0x06, 0x06, 0x02, 0x0F, + 10, // 10 ms delay + ST77XX_CASET, 4, // 15: Column addr set, 4 args, no delay: + 0x00, 0x02, // XSTART = 2 + 0x00, 0x81, // XEND = 129 + ST77XX_RASET, 4, // 16: Row addr set, 4 args, no delay: + 0x00, 0x02, // XSTART = 1 + 0x00, 0x81, // XEND = 160 + ST77XX_NORON, ST_CMD_DELAY, // 17: Normal display on, no args, w/delay + 10, // 10 ms delay + ST77XX_DISPON, ST_CMD_DELAY, // 18: Main screen turn on, no args, delay + 255 }, // 255 = max (500 ms) delay + + RCMD1[] = { // 7735R init, part 1 (red or green tab) + 15, // 15 commands in list: + ST77XX_SWRESET, ST_CMD_DELAY, // 1: Software reset, 0 args, w/delay + 150, // 150 ms delay + ST77XX_SLPOUT, ST_CMD_DELAY, // 2: Out of sleep mode, 0 args, w/delay + 255, // 500 ms delay + ST7735_FRMCTR1, 3, // 3: Framerate ctrl - normal mode, 3 arg: + 0x01, 0x2C, 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D) + ST7735_FRMCTR2, 3, // 4: Framerate ctrl - idle mode, 3 args: + 0x01, 0x2C, 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D) + ST7735_FRMCTR3, 6, // 5: Framerate - partial mode, 6 args: + 0x01, 0x2C, 0x2D, // Dot inversion mode + 0x01, 0x2C, 0x2D, // Line inversion mode + ST7735_INVCTR, 1, // 6: Display inversion ctrl, 1 arg: + 0x07, // No inversion + ST7735_PWCTR1, 3, // 7: Power control, 3 args, no delay: + 0xA2, + 0x02, // -4.6V + 0x84, // AUTO mode + ST7735_PWCTR2, 1, // 8: Power control, 1 arg, no delay: + 0xC5, // VGH25=2.4C VGSEL=-10 VGH=3 * AVDD + ST7735_PWCTR3, 2, // 9: Power control, 2 args, no delay: + 0x0A, // Opamp current small + 0x00, // Boost frequency + ST7735_PWCTR4, 2, // 10: Power control, 2 args, no delay: + 0x8A, // BCLK/2, + 0x2A, // opamp current small & medium low + ST7735_PWCTR5, 2, // 11: Power control, 2 args, no delay: + 0x8A, 0xEE, + ST7735_VMCTR1, 1, // 12: Power control, 1 arg, no delay: + 0x0E, + ST77XX_INVOFF, 0, // 13: Don't invert display, no args + ST77XX_MADCTL, 1, // 14: Mem access ctl (directions), 1 arg: + 0xC8, // row/col addr, bottom-top refresh + ST77XX_COLMOD, 1, // 15: set color mode, 1 arg, no delay: + 0x05 }, // 16-bit color + + RCMD2GREEN[] = { // 7735R init, part 2 (green tab only) + 2, // 2 commands in list: + ST77XX_CASET, 4, // 1: Column addr set, 4 args, no delay: + 0x00, 0x02, // XSTART = 0 + 0x00, 0x7F+0x02, // XEND = 127 + ST77XX_RASET, 4, // 2: Row addr set, 4 args, no delay: + 0x00, 0x01, // XSTART = 0 + 0x00, 0x9F+0x01 }, // XEND = 159 + + RCMD2RED[] = { // 7735R init, part 2 (red tab only) + 2, // 2 commands in list: + ST77XX_CASET, 4, // 1: Column addr set, 4 args, no delay: + 0x00, 0x00, // XSTART = 0 + 0x00, 0x7F, // XEND = 127 + ST77XX_RASET, 4, // 2: Row addr set, 4 args, no delay: + 0x00, 0x00, // XSTART = 0 + 0x00, 0x9F }, // XEND = 159 + + RCMD2GREEN144[] = { // 7735R init, part 2 (green 1.44 tab) + 2, // 2 commands in list: + ST77XX_CASET, 4, // 1: Column addr set, 4 args, no delay: + 0x00, 0x00, // XSTART = 0 + 0x00, 0x7F, // XEND = 127 + ST77XX_RASET, 4, // 2: Row addr set, 4 args, no delay: + 0x00, 0x00, // XSTART = 0 + 0x00, 0x7F }, // XEND = 127 + + RCMD2GREEN160X80[] = { // 7735R init, part 2 (mini 160x80) + 2, // 2 commands in list: + ST77XX_CASET, 4, // 1: Column addr set, 4 args, no delay: + 0x00, 0x00, // XSTART = 0 + 0x00, 0x4F, // XEND = 79 + ST77XX_RASET, 4, // 2: Row addr set, 4 args, no delay: + 0x00, 0x00, // XSTART = 0 + 0x00, 0x9F }, // XEND = 159 + + RCMD3[] = { // 7735R init, part 3 (red or green tab) + 4, // 4 commands in list: + ST7735_GMCTRP1, 16 , // 1: Gamma Adjustments (pos. polarity), 16 args + delay: + 0x02, 0x1c, 0x07, 0x12, // (Not entirely necessary, but provides + 0x37, 0x32, 0x29, 0x2d, // accurate colors) + 0x29, 0x25, 0x2B, 0x39, + 0x00, 0x01, 0x03, 0x10, + ST7735_GMCTRN1, 16 , // 2: Gamma Adjustments (neg. polarity), 16 args + delay: + 0x03, 0x1d, 0x07, 0x06, // (Not entirely necessary, but provides + 0x2E, 0x2C, 0x29, 0x2D, // accurate colors) + 0x2E, 0x2E, 0x37, 0x3F, + 0x00, 0x00, 0x02, 0x10, + ST77XX_NORON, ST_CMD_DELAY, // 3: Normal display on, no args, w/delay + 10, // 10 ms delay + ST77XX_DISPON, ST_CMD_DELAY, // 4: Main screen turn on, no args w/delay + 100 }; // 100 ms delay + +// clang-format on +static const char *const TAG = "st7735"; + +ST7735::ST7735(ST7735Model model, int width, int height, int colstart, int rowstart, boolean eightbitcolor, + boolean usebgr) { + model_ = model; + this->width_ = width; + this->height_ = height; + this->colstart_ = colstart; + this->rowstart_ = rowstart; + this->eightbitcolor_ = eightbitcolor; + this->usebgr_ = usebgr; +} + +void ST7735::setup() { + ESP_LOGCONFIG(TAG, "Setting up ST7735..."); + this->spi_setup(); + + this->dc_pin_->setup(); // OUTPUT + this->cs_->setup(); // OUTPUT + + this->dc_pin_->digital_write(true); + this->cs_->digital_write(true); + + this->init_reset_(); + delay(100); // NOLINT + + ESP_LOGD(TAG, " START"); + dump_config(); + ESP_LOGD(TAG, " END"); + + display_init_(RCMD1); + + if (this->model_ == INITR_GREENTAB) { + display_init_(RCMD2GREEN); + colstart_ == 0 ? colstart_ = 2 : colstart_; + rowstart_ == 0 ? rowstart_ = 1 : rowstart_; + } else if ((this->model_ == INITR_144GREENTAB) || (this->model_ == INITR_HALLOWING)) { + height_ == 0 ? height_ = ST7735_TFTHEIGHT_128 : height_; + width_ == 0 ? width_ = ST7735_TFTWIDTH_128 : width_; + display_init_(RCMD2GREEN144); + colstart_ == 0 ? colstart_ = 2 : colstart_; + rowstart_ == 0 ? rowstart_ = 3 : rowstart_; + } else if (this->model_ == INITR_MINI_160X80) { + height_ == 0 ? height_ = ST7735_TFTHEIGHT_160 : height_; + width_ == 0 ? width_ = ST7735_TFTWIDTH_80 : width_; + display_init_(RCMD2GREEN160X80); + colstart_ = 24; + rowstart_ = 0; // For default rotation 0 + } else { + // colstart, rowstart left at default '0' values + display_init_(RCMD2RED); + } + display_init_(RCMD3); + + uint8_t data = 0; + if (this->model_ != INITR_HALLOWING) { + uint8_t data = ST77XX_MADCTL_MX | ST77XX_MADCTL_MY; + } + if (this->usebgr_) { + data = data | ST7735_MADCTL_BGR; + } else { + data = data | ST77XX_MADCTL_RGB; + } + sendcommand_(ST77XX_MADCTL, &data, 1); + + this->init_internal_(this->get_buffer_length()); + memset(this->buffer_, 0x00, this->get_buffer_length()); +} + +void ST7735::update() { + this->do_update_(); + this->write_display_data_(); +} + +int ST7735::get_height_internal() { return height_; } + +int ST7735::get_width_internal() { return width_; } + +size_t ST7735::get_buffer_length() { + if (this->eightbitcolor_) { + return size_t(this->get_width_internal()) * size_t(this->get_height_internal()); + } + return size_t(this->get_width_internal()) * size_t(this->get_height_internal()) * 2; +} + +void HOT ST7735::draw_absolute_pixel_internal(int x, int y, Color color) { + if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) + return; + + if (this->eightbitcolor_) { + const uint32_t color332 = display::ColorUtil::color_to_332(color); + uint16_t pos = (x + y * this->get_width_internal()); + this->buffer_[pos] = color332; + } else { + const uint32_t color565 = display::ColorUtil::color_to_565(color); + uint16_t pos = (x + y * this->get_width_internal()) * 2; + this->buffer_[pos++] = (color565 >> 8) & 0xff; + this->buffer_[pos] = color565 & 0xff; + } +} + +void ST7735::init_reset_() { + if (this->reset_pin_ != nullptr) { + this->reset_pin_->setup(); + this->reset_pin_->digital_write(true); + delay(1); + // Trigger Reset + this->reset_pin_->digital_write(false); + delay(10); + // Wake up + this->reset_pin_->digital_write(true); + } +} +const char *ST7735::model_str_() { + switch (this->model_) { + case INITR_GREENTAB: + return "ST7735 GREENTAB"; + case INITR_REDTAB: + return "ST7735 REDTAB"; + case INITR_BLACKTAB: + return "ST7735 BLACKTAB"; + case INITR_MINI_160X80: + return "ST7735 MINI160x80"; + default: + return "Unknown"; + } +} + +void ST7735::display_init_(const uint8_t *addr) { + uint8_t num_commands, cmd, num_args; + uint16_t ms; + + num_commands = pgm_read_byte(addr++); // Number of commands to follow + while (num_commands--) { // For each command... + cmd = pgm_read_byte(addr++); // Read command + num_args = pgm_read_byte(addr++); // Number of args to follow + ms = num_args & ST_CMD_DELAY; // If hibit set, delay follows args + num_args &= ~ST_CMD_DELAY; // Mask out delay bit + this->sendcommand_(cmd, addr, num_args); + addr += num_args; + + if (ms) { + ms = pgm_read_byte(addr++); // Read post-command delay time (ms) + if (ms == 255) + ms = 500; // If 255, delay for 500 ms + delay(ms); + } + } +} + +void ST7735::dump_config() { + LOG_DISPLAY("", "ST7735", this); + ESP_LOGCONFIG(TAG, " Model: %s", this->model_str_()); + LOG_PIN(" CS Pin: ", this->cs_); + LOG_PIN(" DC Pin: ", this->dc_pin_); + LOG_PIN(" Reset Pin: ", this->reset_pin_); + ESP_LOGD(TAG, " Buffer Size: %zu", this->get_buffer_length()); + ESP_LOGD(TAG, " Height: %d", this->height_); + ESP_LOGD(TAG, " Width: %d", this->width_); + ESP_LOGD(TAG, " ColStart: %d", this->colstart_); + ESP_LOGD(TAG, " RowStart: %d", this->rowstart_); + LOG_UPDATE_INTERVAL(this); +} + +void HOT ST7735::writecommand_(uint8_t value) { + this->enable(); + this->dc_pin_->digital_write(false); + this->write_byte(value); + this->dc_pin_->digital_write(true); + this->disable(); +} + +void HOT ST7735::writedata_(uint8_t value) { + this->dc_pin_->digital_write(true); + this->enable(); + this->write_byte(value); + this->disable(); +} + +void HOT ST7735::sendcommand_(uint8_t cmd, const uint8_t *data_bytes, uint8_t num_data_bytes) { + this->writecommand_(cmd); + this->senddata_(data_bytes, num_data_bytes); +} + +void HOT ST7735::senddata_(const uint8_t *data_bytes, uint8_t num_data_bytes) { + this->dc_pin_->digital_write(true); // pull DC high to indicate data + this->cs_->digital_write(false); + this->enable(); + for (uint8_t i = 0; i < num_data_bytes; i++) { + this->write_byte(pgm_read_byte(data_bytes++)); // write byte - SPI library + } + this->cs_->digital_write(true); + this->disable(); +} + +void HOT ST7735::write_display_data_() { + uint16_t offsetx = colstart_; + uint16_t offsety = rowstart_; + + uint16_t x1 = offsetx; + uint16_t x2 = x1 + get_width_internal() - 1; + uint16_t y1 = offsety; + uint16_t y2 = y1 + get_height_internal() - 1; + + this->enable(); + + // set column(x) address + this->dc_pin_->digital_write(false); + this->write_byte(ST77XX_CASET); + this->dc_pin_->digital_write(true); + this->spi_master_write_addr_(x1, x2); + + // set Page(y) address + this->dc_pin_->digital_write(false); + this->write_byte(ST77XX_RASET); + this->dc_pin_->digital_write(true); + this->spi_master_write_addr_(y1, y2); + + // Memory Write + this->dc_pin_->digital_write(false); + this->write_byte(ST77XX_RAMWR); + this->dc_pin_->digital_write(true); + + if (this->eightbitcolor_) { + for (int line = 0; line < this->get_buffer_length(); line = line + this->get_width_internal()) { + for (int index = 0; index < this->get_width_internal(); ++index) { + auto color332 = display::ColorUtil::to_color(this->buffer_[index + line], display::ColorOrder::COLOR_ORDER_RGB, + display::ColorBitness::COLOR_BITNESS_332, true); + + auto color = display::ColorUtil::color_to_565(color332); + + this->write_byte((color >> 8) & 0xff); + this->write_byte(color & 0xff); + } + } + } else { + this->write_array(this->buffer_, this->get_buffer_length()); + } + this->disable(); +} + +void ST7735::spi_master_write_addr_(uint16_t addr1, uint16_t addr2) { + static uint8_t BYTE[4]; + BYTE[0] = (addr1 >> 8) & 0xFF; + BYTE[1] = addr1 & 0xFF; + BYTE[2] = (addr2 >> 8) & 0xFF; + BYTE[3] = addr2 & 0xFF; + + this->dc_pin_->digital_write(true); + this->write_array(BYTE, 4); +} + +void ST7735::spi_master_write_color_(uint16_t color, uint16_t size) { + static uint8_t BYTE[1024]; + int index = 0; + for (int i = 0; i < size; i++) { + BYTE[index++] = (color >> 8) & 0xFF; + BYTE[index++] = color & 0xFF; + } + + this->dc_pin_->digital_write(true); + return write_array(BYTE, size * 2); +} + +} // namespace st7735 +} // namespace esphome diff --git a/esphome/components/st7789v/st7789v.cpp b/esphome/components/st7789v/st7789v.cpp index 3a6374263a..c84153970d 100644 --- a/esphome/components/st7789v/st7789v.cpp +++ b/esphome/components/st7789v/st7789v.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace st7789v { -static const char *TAG = "st7789v"; +static const char *const TAG = "st7789v"; void ST7789V::setup() { ESP_LOGCONFIG(TAG, "Setting up SPI ST7789V..."); diff --git a/esphome/components/status/status_binary_sensor.cpp b/esphome/components/status/status_binary_sensor.cpp index 90ac1faad7..152e3aff9d 100644 --- a/esphome/components/status/status_binary_sensor.cpp +++ b/esphome/components/status/status_binary_sensor.cpp @@ -13,7 +13,7 @@ namespace esphome { namespace status { -static const char *TAG = "status"; +static const char *const TAG = "status"; void StatusBinarySensor::loop() { bool status = network_is_connected(); diff --git a/esphome/components/status_led/status_led.cpp b/esphome/components/status_led/status_led.cpp index 472b3cd92f..d2d56cf05b 100644 --- a/esphome/components/status_led/status_led.cpp +++ b/esphome/components/status_led/status_led.cpp @@ -5,9 +5,9 @@ namespace esphome { namespace status_led { -static const char *TAG = "status_led"; +static const char *const TAG = "status_led"; -StatusLED *global_status_led = nullptr; +StatusLED *global_status_led = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) StatusLED::StatusLED(GPIOPin *pin) : pin_(pin) { global_status_led = this; } void StatusLED::pre_setup() { diff --git a/esphome/components/status_led/status_led.h b/esphome/components/status_led/status_led.h index 7da325e460..79f9f524a3 100644 --- a/esphome/components/status_led/status_led.h +++ b/esphome/components/status_led/status_led.h @@ -20,7 +20,7 @@ class StatusLED : public Component { GPIOPin *pin_; }; -extern StatusLED *global_status_led; +extern StatusLED *global_status_led; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace status_led } // namespace esphome diff --git a/esphome/components/stepper/__init__.py b/esphome/components/stepper/__init__.py index 41b1db7df2..54f6aa4205 100644 --- a/esphome/components/stepper/__init__.py +++ b/esphome/components/stepper/__init__.py @@ -21,6 +21,8 @@ Stepper = stepper_ns.class_("Stepper") SetTargetAction = stepper_ns.class_("SetTargetAction", automation.Action) ReportPositionAction = stepper_ns.class_("ReportPositionAction", automation.Action) SetSpeedAction = stepper_ns.class_("SetSpeedAction", automation.Action) +SetAccelerationAction = stepper_ns.class_("SetAccelerationAction", automation.Action) +SetDecelerationAction = stepper_ns.class_("SetDecelerationAction", automation.Action) def validate_acceleration(value): @@ -138,11 +140,47 @@ async def stepper_report_position_to_code(config, action_id, template_arg, args) async def stepper_set_speed_to_code(config, action_id, template_arg, args): paren = await cg.get_variable(config[CONF_ID]) var = cg.new_Pvariable(action_id, template_arg, paren) - template_ = await cg.templatable(config[CONF_SPEED], args, cg.int32) + template_ = await cg.templatable(config[CONF_SPEED], args, cg.float_) cg.add(var.set_speed(template_)) return var +@automation.register_action( + "stepper.set_acceleration", + SetAccelerationAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(Stepper), + cv.Required(CONF_ACCELERATION): cv.templatable(validate_acceleration), + } + ), +) +async def stepper_set_acceleration_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + template_ = await cg.templatable(config[CONF_ACCELERATION], args, cg.float_) + cg.add(var.set_acceleration(template_)) + return var + + +@automation.register_action( + "stepper.set_deceleration", + SetDecelerationAction, + cv.Schema( + { + cv.Required(CONF_ID): cv.use_id(Stepper), + cv.Required(CONF_DECELERATION): cv.templatable(validate_acceleration), + } + ), +) +async def stepper_set_deceleration_to_code(config, action_id, template_arg, args): + paren = await cg.get_variable(config[CONF_ID]) + var = cg.new_Pvariable(action_id, template_arg, paren) + template_ = await cg.templatable(config[CONF_DECELERATION], args, cg.float_) + cg.add(var.set_deceleration(template_)) + return var + + @coroutine_with_priority(100.0) async def to_code(config): cg.add_global(stepper_ns.using) diff --git a/esphome/components/stepper/stepper.cpp b/esphome/components/stepper/stepper.cpp index b966fb00cc..d7f6cc6dda 100644 --- a/esphome/components/stepper/stepper.cpp +++ b/esphome/components/stepper/stepper.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace stepper { -static const char *TAG = "stepper"; +static const char *const TAG = "stepper"; void Stepper::calculate_speed_(uint32_t now) { // delta t since last calculation in seconds diff --git a/esphome/components/stepper/stepper.h b/esphome/components/stepper/stepper.h index 33777dce83..560362e4d0 100644 --- a/esphome/components/stepper/stepper.h +++ b/esphome/components/stepper/stepper.h @@ -77,5 +77,35 @@ template class SetSpeedAction : public Action { Stepper *parent_; }; +template class SetAccelerationAction : public Action { + public: + explicit SetAccelerationAction(Stepper *parent) : parent_(parent) {} + + TEMPLATABLE_VALUE(float, acceleration); + + void play(Ts... x) override { + float acceleration = this->acceleration_.value(x...); + this->parent_->set_acceleration(acceleration); + } + + protected: + Stepper *parent_; +}; + +template class SetDecelerationAction : public Action { + public: + explicit SetDecelerationAction(Stepper *parent) : parent_(parent) {} + + TEMPLATABLE_VALUE(float, deceleration); + + void play(Ts... x) override { + float deceleration = this->deceleration_.value(x...); + this->parent_->set_deceleration(deceleration); + } + + protected: + Stepper *parent_; +}; + } // namespace stepper } // namespace esphome diff --git a/esphome/components/sts3x/sts3x.cpp b/esphome/components/sts3x/sts3x.cpp index 1a24a17caf..5d6d725a17 100644 --- a/esphome/components/sts3x/sts3x.cpp +++ b/esphome/components/sts3x/sts3x.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace sts3x { -static const char *TAG = "sts3x"; +static const char *const TAG = "sts3x"; static const uint16_t STS3X_COMMAND_READ_SERIAL_NUMBER = 0x3780; static const uint16_t STS3X_COMMAND_READ_STATUS = 0xF32D; diff --git a/esphome/components/sun/sensor/sun_sensor.cpp b/esphome/components/sun/sensor/sun_sensor.cpp index 63b7715287..6c90722c29 100644 --- a/esphome/components/sun/sensor/sun_sensor.cpp +++ b/esphome/components/sun/sensor/sun_sensor.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace sun { -static const char *TAG = "sun.sensor"; +static const char *const TAG = "sun.sensor"; void SunSensor::dump_config() { LOG_SENSOR("", "Sun Sensor", this); } diff --git a/esphome/components/sun/sun.cpp b/esphome/components/sun/sun.cpp index 0722e66e67..4fd034ce7d 100644 --- a/esphome/components/sun/sun.cpp +++ b/esphome/components/sun/sun.cpp @@ -16,7 +16,7 @@ namespace sun { using namespace esphome::sun::internal; -static const char *TAG = "sun"; +static const char *const TAG = "sun"; #undef PI #undef degrees diff --git a/esphome/components/sun/text_sensor/sun_text_sensor.cpp b/esphome/components/sun/text_sensor/sun_text_sensor.cpp index ee949584cc..c047b87fdd 100644 --- a/esphome/components/sun/text_sensor/sun_text_sensor.cpp +++ b/esphome/components/sun/text_sensor/sun_text_sensor.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace sun { -static const char *TAG = "sun.text_sensor"; +static const char *const TAG = "sun.text_sensor"; void SunTextSensor::dump_config() { LOG_TEXT_SENSOR("", "Sun Text Sensor", this); } diff --git a/esphome/components/switch/automation.cpp b/esphome/components/switch/automation.cpp index ba9bb7e05a..5989ae9ce3 100644 --- a/esphome/components/switch/automation.cpp +++ b/esphome/components/switch/automation.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace switch_ { -static const char *TAG = "switch.automation"; +static const char *const TAG = "switch.automation"; } // namespace switch_ } // namespace esphome diff --git a/esphome/components/switch/switch.cpp b/esphome/components/switch/switch.cpp index a6a25dd9dc..c96f9a40d0 100644 --- a/esphome/components/switch/switch.cpp +++ b/esphome/components/switch/switch.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace switch_ { -static const char *TAG = "switch"; +static const char *const TAG = "switch"; std::string Switch::icon() { return ""; } Switch::Switch(const std::string &name) : Nameable(name), state(false) {} diff --git a/esphome/components/switch/switch.h b/esphome/components/switch/switch.h index cd6cec429f..966119a29f 100644 --- a/esphome/components/switch/switch.h +++ b/esphome/components/switch/switch.h @@ -8,15 +8,15 @@ namespace esphome { namespace switch_ { #define LOG_SWITCH(prefix, type, obj) \ - if (obj != nullptr) { \ - ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, obj->get_name().c_str()); \ - if (!obj->get_icon().empty()) { \ - ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, obj->get_icon().c_str()); \ + if ((obj) != nullptr) { \ + ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \ + if (!(obj)->get_icon().empty()) { \ + ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \ } \ - if (obj->assumed_state()) { \ + if ((obj)->assumed_state()) { \ ESP_LOGCONFIG(TAG, "%s Assumed State: YES", prefix); \ } \ - if (obj->is_inverted()) { \ + if ((obj)->is_inverted()) { \ ESP_LOGCONFIG(TAG, "%s Inverted: YES", prefix); \ } \ } diff --git a/esphome/components/sx1509/output/sx1509_float_output.cpp b/esphome/components/sx1509/output/sx1509_float_output.cpp index 7ff1bbb61b..c68f8f9ded 100644 --- a/esphome/components/sx1509/output/sx1509_float_output.cpp +++ b/esphome/components/sx1509/output/sx1509_float_output.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace sx1509 { -static const char *TAG = "sx1509_float_channel"; +static const char *const TAG = "sx1509_float_channel"; void SX1509FloatOutputChannel::write_state(float state) { const uint16_t max_duty = 255; diff --git a/esphome/components/sx1509/sx1509.cpp b/esphome/components/sx1509/sx1509.cpp index 0d6ffbb9b8..14d9ad7a61 100644 --- a/esphome/components/sx1509/sx1509.cpp +++ b/esphome/components/sx1509/sx1509.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace sx1509 { -static const char *TAG = "sx1509"; +static const char *const TAG = "sx1509"; void SX1509Component::setup() { ESP_LOGCONFIG(TAG, "Setting up SX1509Component..."); diff --git a/esphome/components/sx1509/sx1509_gpio_pin.cpp b/esphome/components/sx1509/sx1509_gpio_pin.cpp index 1d1c87b4e6..ac55ac1ca5 100644 --- a/esphome/components/sx1509/sx1509_gpio_pin.cpp +++ b/esphome/components/sx1509/sx1509_gpio_pin.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace sx1509 { -static const char *TAG = "sx1509_gpio_pin"; +static const char *const TAG = "sx1509_gpio_pin"; void SX1509GPIOPin::setup() { ESP_LOGD(TAG, "setup pin %d", this->pin_); diff --git a/esphome/components/tca9548a/tca9548a.cpp b/esphome/components/tca9548a/tca9548a.cpp index 0df60d6dd2..472b8b6673 100644 --- a/esphome/components/tca9548a/tca9548a.cpp +++ b/esphome/components/tca9548a/tca9548a.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace tca9548a { -static const char *TAG = "tca9548a"; +static const char *const TAG = "tca9548a"; void TCA9548AComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up TCA9548A..."); diff --git a/esphome/components/tcl112/tcl112.cpp b/esphome/components/tcl112/tcl112.cpp index 6921bbd3c0..5b938ba0c3 100644 --- a/esphome/components/tcl112/tcl112.cpp +++ b/esphome/components/tcl112/tcl112.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace tcl112 { -static const char *TAG = "tcl112.climate"; +static const char *const TAG = "tcl112.climate"; const uint16_t TCL112_STATE_LENGTH = 14; const uint16_t TCL112_BITS = TCL112_STATE_LENGTH * 8; diff --git a/esphome/components/tcs34725/tcs34725.cpp b/esphome/components/tcs34725/tcs34725.cpp index 49aff5b162..52548262c1 100644 --- a/esphome/components/tcs34725/tcs34725.cpp +++ b/esphome/components/tcs34725/tcs34725.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace tcs34725 { -static const char *TAG = "tcs34725"; +static const char *const TAG = "tcs34725"; static const uint8_t TCS34725_ADDRESS = 0x29; static const uint8_t TCS34725_COMMAND_BIT = 0x80; diff --git a/esphome/components/teleinfo/sensor/teleinfo_sensor.cpp b/esphome/components/teleinfo/sensor/teleinfo_sensor.cpp index c00bf8f441..661c149c09 100644 --- a/esphome/components/teleinfo/sensor/teleinfo_sensor.cpp +++ b/esphome/components/teleinfo/sensor/teleinfo_sensor.cpp @@ -3,9 +3,9 @@ namespace esphome { namespace teleinfo { -static const char *TAG = "teleinfo_sensor"; +static const char *const TAG = "teleinfo_sensor"; TeleInfoSensor::TeleInfoSensor(const char *tag) { this->tag = std::string(tag); } -void TeleInfoSensor::publish_val(std::string val) { +void TeleInfoSensor::publish_val(const std::string &val) { auto newval = parse_float(val); publish_state(*newval); } diff --git a/esphome/components/teleinfo/sensor/teleinfo_sensor.h b/esphome/components/teleinfo/sensor/teleinfo_sensor.h index de46cbfd1a..56781166ab 100644 --- a/esphome/components/teleinfo/sensor/teleinfo_sensor.h +++ b/esphome/components/teleinfo/sensor/teleinfo_sensor.h @@ -8,7 +8,7 @@ namespace teleinfo { class TeleInfoSensor : public TeleInfoListener, public sensor::Sensor, public Component { public: TeleInfoSensor(const char *tag); - void publish_val(std::string val) override; + void publish_val(const std::string &val) override; void dump_config() override; }; diff --git a/esphome/components/teleinfo/teleinfo.cpp b/esphome/components/teleinfo/teleinfo.cpp index bd233c9595..8240615cc5 100644 --- a/esphome/components/teleinfo/teleinfo.cpp +++ b/esphome/components/teleinfo/teleinfo.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace teleinfo { -static const char *TAG = "teleinfo"; +static const char *const TAG = "teleinfo"; /* Helpers */ static int get_field(char *dest, char *buf_start, char *buf_end, int sep) { @@ -120,7 +120,7 @@ void TeleInfo::loop() { } if (!check_crc_(buf_finger, grp_end)) - break; + continue; /* Get tag */ field_len = get_field(tag_, buf_finger, grp_end, separator_); @@ -148,7 +148,7 @@ void TeleInfo::loop() { break; } } -void TeleInfo::publish_value_(std::string tag, std::string val) { +void TeleInfo::publish_value_(const std::string &tag, const std::string &val) { for (auto element : teleinfo_listeners_) { if (tag != element->tag) continue; diff --git a/esphome/components/teleinfo/teleinfo.h b/esphome/components/teleinfo/teleinfo.h index 3932c4758e..f10024691e 100644 --- a/esphome/components/teleinfo/teleinfo.h +++ b/esphome/components/teleinfo/teleinfo.h @@ -16,7 +16,7 @@ static const uint16_t MAX_BUF_SIZE = 1024; class TeleInfoListener { public: std::string tag; - virtual void publish_val(std::string val){}; + virtual void publish_val(const std::string &val){}; }; class TeleInfo : public PollingComponent, public uart::UARTDevice { public: @@ -44,7 +44,7 @@ class TeleInfo : public PollingComponent, public uart::UARTDevice { } state_{OFF}; bool read_chars_until_(bool drop, uint8_t c); bool check_crc_(const char *grp, const char *grp_end); - void publish_value_(std::string tag, std::string val); + void publish_value_(const std::string &tag, const std::string &val); }; } // namespace teleinfo } // namespace esphome diff --git a/esphome/components/teleinfo/text_sensor/teleinfo_text_sensor.cpp b/esphome/components/teleinfo/text_sensor/teleinfo_text_sensor.cpp index ac48aeabbd..1adbd9ce13 100644 --- a/esphome/components/teleinfo/text_sensor/teleinfo_text_sensor.cpp +++ b/esphome/components/teleinfo/text_sensor/teleinfo_text_sensor.cpp @@ -3,9 +3,9 @@ namespace esphome { namespace teleinfo { -static const char *TAG = "teleinfo_text_sensor"; +static const char *const TAG = "teleinfo_text_sensor"; TeleInfoTextSensor::TeleInfoTextSensor(const char *tag) { this->tag = std::string(tag); } -void TeleInfoTextSensor::publish_val(std::string val) { publish_state(val); } +void TeleInfoTextSensor::publish_val(const std::string &val) { publish_state(val); } void TeleInfoTextSensor::dump_config() { LOG_TEXT_SENSOR(" ", tag.c_str(), this); } } // namespace teleinfo } // namespace esphome diff --git a/esphome/components/teleinfo/text_sensor/teleinfo_text_sensor.h b/esphome/components/teleinfo/text_sensor/teleinfo_text_sensor.h index be355151ce..5a7dc9d1a7 100644 --- a/esphome/components/teleinfo/text_sensor/teleinfo_text_sensor.h +++ b/esphome/components/teleinfo/text_sensor/teleinfo_text_sensor.h @@ -6,7 +6,7 @@ namespace teleinfo { class TeleInfoTextSensor : public TeleInfoListener, public text_sensor::TextSensor, public Component { public: TeleInfoTextSensor(const char *tag); - void publish_val(std::string val) override; + void publish_val(const std::string &val) override; void dump_config() override; }; } // namespace teleinfo diff --git a/esphome/components/template/binary_sensor/template_binary_sensor.cpp b/esphome/components/template/binary_sensor/template_binary_sensor.cpp index a4199faa9a..66ff4be4c4 100644 --- a/esphome/components/template/binary_sensor/template_binary_sensor.cpp +++ b/esphome/components/template/binary_sensor/template_binary_sensor.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace template_ { -static const char *TAG = "template.binary_sensor"; +static const char *const TAG = "template.binary_sensor"; void TemplateBinarySensor::loop() { if (!this->f_.has_value()) diff --git a/esphome/components/template/cover/template_cover.cpp b/esphome/components/template/cover/template_cover.cpp index 147f76af7d..47c651e643 100644 --- a/esphome/components/template/cover/template_cover.cpp +++ b/esphome/components/template/cover/template_cover.cpp @@ -6,7 +6,7 @@ namespace template_ { using namespace esphome::cover; -static const char *TAG = "template.cover"; +static const char *const TAG = "template.cover"; TemplateCover::TemplateCover() : open_trigger_(new Trigger<>()), diff --git a/esphome/components/template/number/__init__.py b/esphome/components/template/number/__init__.py new file mode 100644 index 0000000000..22bbaacc15 --- /dev/null +++ b/esphome/components/template/number/__init__.py @@ -0,0 +1,88 @@ +from esphome import automation +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import number +from esphome.const import ( + CONF_ID, + CONF_INITIAL_VALUE, + CONF_LAMBDA, + CONF_MAX_VALUE, + CONF_MIN_VALUE, + CONF_OPTIMISTIC, + CONF_RESTORE_VALUE, + CONF_STEP, +) +from .. import template_ns + +TemplateNumber = template_ns.class_( + "TemplateNumber", number.Number, cg.PollingComponent +) + +CONF_SET_ACTION = "set_action" + + +def validate_min_max(config): + if config[CONF_MAX_VALUE] <= config[CONF_MIN_VALUE]: + raise cv.Invalid("max_value must be greater than min_value") + return config + + +def validate(config): + if CONF_LAMBDA in config: + if CONF_OPTIMISTIC in config: + raise cv.Invalid("optimistic cannot be used with lambda") + if CONF_INITIAL_VALUE in config: + raise cv.Invalid("initial_value cannot be used with lambda") + if CONF_RESTORE_VALUE in config: + raise cv.Invalid("restore_value cannot be used with lambda") + return config + + +CONFIG_SCHEMA = cv.All( + number.NUMBER_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TemplateNumber), + cv.Required(CONF_MAX_VALUE): cv.float_, + cv.Required(CONF_MIN_VALUE): cv.float_, + cv.Required(CONF_STEP): cv.positive_float, + cv.Optional(CONF_LAMBDA): cv.returning_lambda, + cv.Optional(CONF_OPTIMISTIC): cv.boolean, + cv.Optional(CONF_SET_ACTION): automation.validate_automation(single=True), + cv.Optional(CONF_INITIAL_VALUE): cv.float_, + cv.Optional(CONF_RESTORE_VALUE): cv.boolean, + } + ).extend(cv.polling_component_schema("60s")), + validate_min_max, + validate, +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await number.register_number( + var, + config, + min_value=config[CONF_MIN_VALUE], + max_value=config[CONF_MAX_VALUE], + step=config[CONF_STEP], + ) + + if CONF_LAMBDA in config: + template_ = await cg.process_lambda( + config[CONF_LAMBDA], [], return_type=cg.optional.template(float) + ) + cg.add(var.set_template(template_)) + + else: + if CONF_OPTIMISTIC in config: + cg.add(var.set_optimistic(config[CONF_OPTIMISTIC])) + if CONF_INITIAL_VALUE in config: + cg.add(var.set_initial_value(config[CONF_INITIAL_VALUE])) + if CONF_RESTORE_VALUE in config: + cg.add(var.set_restore_value(config[CONF_RESTORE_VALUE])) + + if CONF_SET_ACTION in config: + await automation.build_automation( + var.get_set_trigger(), [(float, "x")], config[CONF_SET_ACTION] + ) diff --git a/esphome/components/template/number/template_number.cpp b/esphome/components/template/number/template_number.cpp new file mode 100644 index 0000000000..eb9b17b976 --- /dev/null +++ b/esphome/components/template/number/template_number.cpp @@ -0,0 +1,55 @@ +#include "template_number.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace template_ { + +static const char *const TAG = "template.number"; + +void TemplateNumber::setup() { + if (this->f_.has_value()) + return; + + float value; + if (!this->restore_value_) { + value = this->initial_value_; + } else { + this->pref_ = global_preferences.make_preference(this->get_object_id_hash()); + if (!this->pref_.load(&value)) { + if (!isnan(this->initial_value_)) + value = this->initial_value_; + else + value = this->traits.get_min_value(); + } + } + this->publish_state(value); +} + +void TemplateNumber::update() { + if (!this->f_.has_value()) + return; + + auto val = (*this->f_)(); + if (!val.has_value()) + return; + + this->publish_state(*val); +} + +void TemplateNumber::control(float value) { + this->set_trigger_->trigger(value); + + if (this->optimistic_) + this->publish_state(value); + + if (this->restore_value_) + this->pref_.save(&value); +} +void TemplateNumber::dump_config() { + LOG_NUMBER("", "Template Number", this); + ESP_LOGCONFIG(TAG, " Optimistic: %s", YESNO(this->optimistic_)); + LOG_UPDATE_INTERVAL(this); +} + +} // namespace template_ +} // namespace esphome diff --git a/esphome/components/template/number/template_number.h b/esphome/components/template/number/template_number.h new file mode 100644 index 0000000000..9a82e44339 --- /dev/null +++ b/esphome/components/template/number/template_number.h @@ -0,0 +1,37 @@ +#pragma once + +#include "esphome/components/number/number.h" +#include "esphome/core/automation.h" +#include "esphome/core/component.h" +#include "esphome/core/preferences.h" + +namespace esphome { +namespace template_ { + +class TemplateNumber : public number::Number, public PollingComponent { + public: + void set_template(std::function()> &&f) { this->f_ = f; } + + void setup() override; + void update() override; + void dump_config() override; + float get_setup_priority() const override { return setup_priority::HARDWARE; } + + Trigger *get_set_trigger() const { return set_trigger_; } + void set_optimistic(bool optimistic) { optimistic_ = optimistic; } + void set_initial_value(float initial_value) { initial_value_ = initial_value; } + void set_restore_value(bool restore_value) { this->restore_value_ = restore_value; } + + protected: + void control(float value) override; + bool optimistic_{false}; + float initial_value_{NAN}; + bool restore_value_{false}; + Trigger *set_trigger_ = new Trigger(); + optional()>> f_; + + ESPPreferenceObject pref_; +}; + +} // namespace template_ +} // namespace esphome diff --git a/esphome/components/template/sensor/template_sensor.cpp b/esphome/components/template/sensor/template_sensor.cpp index e2d36f13a0..9324cb5dea 100644 --- a/esphome/components/template/sensor/template_sensor.cpp +++ b/esphome/components/template/sensor/template_sensor.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace template_ { -static const char *TAG = "template.sensor"; +static const char *const TAG = "template.sensor"; void TemplateSensor::update() { if (!this->f_.has_value()) diff --git a/esphome/components/template/switch/template_switch.cpp b/esphome/components/template/switch/template_switch.cpp index d9f95e203c..b3e545d3e9 100644 --- a/esphome/components/template/switch/template_switch.cpp +++ b/esphome/components/template/switch/template_switch.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace template_ { -static const char *TAG = "template.switch"; +static const char *const TAG = "template.switch"; TemplateSwitch::TemplateSwitch() : turn_on_trigger_(new Trigger<>()), turn_off_trigger_(new Trigger<>()) {} diff --git a/esphome/components/template/text_sensor/template_text_sensor.cpp b/esphome/components/template/text_sensor/template_text_sensor.cpp index e06c70d95e..885ad47bbf 100644 --- a/esphome/components/template/text_sensor/template_text_sensor.cpp +++ b/esphome/components/template/text_sensor/template_text_sensor.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace template_ { -static const char *TAG = "template.text_sensor"; +static const char *const TAG = "template.text_sensor"; void TemplateTextSensor::update() { if (!this->f_.has_value()) diff --git a/esphome/components/text_sensor/automation.h b/esphome/components/text_sensor/automation.h index 6810d10b13..d82fd27c1f 100644 --- a/esphome/components/text_sensor/automation.h +++ b/esphome/components/text_sensor/automation.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "esphome/core/component.h" #include "esphome/core/automation.h" #include "esphome/components/text_sensor/text_sensor.h" @@ -10,7 +12,7 @@ namespace text_sensor { class TextSensorStateTrigger : public Trigger { public: explicit TextSensorStateTrigger(TextSensor *parent) { - parent->add_on_state_callback([this](std::string value) { this->trigger(value); }); + parent->add_on_state_callback([this](std::string value) { this->trigger(std::move(value)); }); } }; diff --git a/esphome/components/text_sensor/text_sensor.cpp b/esphome/components/text_sensor/text_sensor.cpp index d4f5ef434a..8738860d55 100644 --- a/esphome/components/text_sensor/text_sensor.cpp +++ b/esphome/components/text_sensor/text_sensor.cpp @@ -4,12 +4,12 @@ namespace esphome { namespace text_sensor { -static const char *TAG = "text_sensor"; +static const char *const TAG = "text_sensor"; TextSensor::TextSensor() : TextSensor("") {} TextSensor::TextSensor(const std::string &name) : Nameable(name) {} -void TextSensor::publish_state(std::string state) { +void TextSensor::publish_state(const std::string &state) { this->state = state; this->has_state_ = true; ESP_LOGD(TAG, "'%s': Sending state '%s'", this->name_.c_str(), state.c_str()); diff --git a/esphome/components/text_sensor/text_sensor.h b/esphome/components/text_sensor/text_sensor.h index 85c2b644a0..5c6e5be51a 100644 --- a/esphome/components/text_sensor/text_sensor.h +++ b/esphome/components/text_sensor/text_sensor.h @@ -7,13 +7,13 @@ namespace esphome { namespace text_sensor { #define LOG_TEXT_SENSOR(prefix, type, obj) \ - if (obj != nullptr) { \ - ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, obj->get_name().c_str()); \ - if (!obj->get_icon().empty()) { \ - ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, obj->get_icon().c_str()); \ + if ((obj) != nullptr) { \ + ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \ + if (!(obj)->get_icon().empty()) { \ + ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \ } \ - if (!obj->unique_id().empty()) { \ - ESP_LOGV(TAG, "%s Unique ID: '%s'", prefix, obj->unique_id().c_str()); \ + if (!(obj)->unique_id().empty()) { \ + ESP_LOGV(TAG, "%s Unique ID: '%s'", prefix, (obj)->unique_id().c_str()); \ } \ } @@ -22,7 +22,7 @@ class TextSensor : public Nameable { explicit TextSensor(); explicit TextSensor(const std::string &name); - void publish_state(std::string state); + void publish_state(const std::string &state); void set_icon(const std::string &icon); diff --git a/esphome/components/thermostat/thermostat_climate.cpp b/esphome/components/thermostat/thermostat_climate.cpp index 65dd398197..305db66f16 100644 --- a/esphome/components/thermostat/thermostat_climate.cpp +++ b/esphome/components/thermostat/thermostat_climate.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace thermostat { -static const char *TAG = "thermostat.climate"; +static const char *const TAG = "thermostat.climate"; void ThermostatClimate::setup() { this->sensor_->add_on_state_callback([this](float state) { diff --git a/esphome/components/time/automation.cpp b/esphome/components/time/automation.cpp index 8e41fba0da..6d34459fea 100644 --- a/esphome/components/time/automation.cpp +++ b/esphome/components/time/automation.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace time { -static const char *TAG = "automation"; +static const char *const TAG = "automation"; void CronTrigger::add_second(uint8_t second) { this->seconds_[second] = true; } void CronTrigger::add_minute(uint8_t minute) { this->minutes_[minute] = true; } diff --git a/esphome/components/time/real_time_clock.cpp b/esphome/components/time/real_time_clock.cpp index 44ff505ecc..c2a93b5191 100644 --- a/esphome/components/time/real_time_clock.cpp +++ b/esphome/components/time/real_time_clock.cpp @@ -4,12 +4,12 @@ #ifdef ARDUINO_ARCH_ESP8266 #include "sys/time.h" #endif -#include "errno.h" +#include namespace esphome { namespace time { -static const char *TAG = "time"; +static const char *const TAG = "time"; RealTimeClock::RealTimeClock() = default; void RealTimeClock::call_setup() { diff --git a/esphome/components/time/real_time_clock.h b/esphome/components/time/real_time_clock.h index c591482729..0c6fa6f3a0 100644 --- a/esphome/components/time/real_time_clock.h +++ b/esphome/components/time/real_time_clock.h @@ -1,11 +1,11 @@ #pragma once +#include "esphome/core/automation.h" #include "esphome/core/component.h" #include "esphome/core/helpers.h" -#include "esphome/core/automation.h" -#include -#include #include +#include +#include namespace esphome { namespace time { @@ -32,11 +32,8 @@ struct ESPTime { uint16_t year; /// daylight saving time flag bool is_dst; - union { - ESPDEPRECATED(".time is deprecated, use .timestamp instead") time_t time; - /// unix epoch time (seconds since UTC Midnight January 1, 1970) - time_t timestamp; - }; + /// unix epoch time (seconds since UTC Midnight January 1, 1970) + time_t timestamp; /** Convert this ESPTime struct to a null-terminated c string buffer as specified by the format argument. * Up to buffer_len bytes are written. diff --git a/esphome/components/time_based/time_based_cover.cpp b/esphome/components/time_based/time_based_cover.cpp index 1aa3c2471a..60a33aa82a 100644 --- a/esphome/components/time_based/time_based_cover.cpp +++ b/esphome/components/time_based/time_based_cover.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace time_based { -static const char *TAG = "time_based.cover"; +static const char *const TAG = "time_based.cover"; using namespace esphome::cover; @@ -115,13 +115,13 @@ void TimeBasedCover::start_direction_(CoverOperation dir) { this->current_operation = dir; - this->stop_prev_trigger_(); - trig->trigger(); - this->prev_command_trigger_ = trig; - const uint32_t now = millis(); this->start_dir_time_ = now; this->last_recompute_time_ = now; + + this->stop_prev_trigger_(); + trig->trigger(); + this->prev_command_trigger_ = trig; } void TimeBasedCover::recompute_position_() { if (this->current_operation == COVER_OPERATION_IDLE) diff --git a/esphome/components/tlc59208f/tlc59208f_output.cpp b/esphome/components/tlc59208f/tlc59208f_output.cpp index 6e65ff4e76..784a0947ed 100644 --- a/esphome/components/tlc59208f/tlc59208f_output.cpp +++ b/esphome/components/tlc59208f/tlc59208f_output.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace tlc59208f { -static const char *TAG = "tlc59208f"; +static const char *const TAG = "tlc59208f"; // * marks register defaults // 0*: Register auto increment disabled, 1: Register auto increment enabled diff --git a/esphome/components/tm1637/tm1637.cpp b/esphome/components/tm1637/tm1637.cpp index df904ad9ab..1f1c0fd301 100644 --- a/esphome/components/tm1637/tm1637.cpp +++ b/esphome/components/tm1637/tm1637.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace tm1637 { -const char* TAG = "display.tm1637"; +static const char *const TAG = "display.tm1637"; const uint8_t TM1637_I2C_COMM1 = 0x40; const uint8_t TM1637_I2C_COMM2 = 0xC0; const uint8_t TM1637_I2C_COMM3 = 0x80; @@ -136,7 +136,7 @@ void TM1637Display::dump_config() { } void TM1637Display::update() { - for (uint8_t& i : this->buffer_) + for (uint8_t &i : this->buffer_) i = 0; if (this->writer_.has_value()) (*this->writer_)(*this); @@ -227,7 +227,7 @@ bool TM1637Display::send_byte_(uint8_t b) { return ack; } -uint8_t TM1637Display::print(uint8_t start_pos, const char* str) { +uint8_t TM1637Display::print(uint8_t start_pos, const char *str) { ESP_LOGV(TAG, "Print at %d: %s", start_pos, str); uint8_t pos = start_pos; for (; *str != '\0'; str++) { @@ -263,8 +263,8 @@ uint8_t TM1637Display::print(uint8_t start_pos, const char* str) { } return pos - start_pos; } -uint8_t TM1637Display::print(const char* str) { return this->print(0, str); } -uint8_t TM1637Display::printf(uint8_t pos, const char* format, ...) { +uint8_t TM1637Display::print(const char *str) { return this->print(0, str); } +uint8_t TM1637Display::printf(uint8_t pos, const char *format, ...) { va_list arg; va_start(arg, format); char buffer[64]; @@ -274,7 +274,7 @@ uint8_t TM1637Display::printf(uint8_t pos, const char* format, ...) { return this->print(pos, buffer); return 0; } -uint8_t TM1637Display::printf(const char* format, ...) { +uint8_t TM1637Display::printf(const char *format, ...) { va_list arg; va_start(arg, format); char buffer[64]; @@ -286,14 +286,14 @@ uint8_t TM1637Display::printf(const char* format, ...) { } #ifdef USE_TIME -uint8_t TM1637Display::strftime(uint8_t pos, const char* format, time::ESPTime time) { +uint8_t TM1637Display::strftime(uint8_t pos, const char *format, time::ESPTime time) { char buffer[64]; size_t ret = time.strftime(buffer, sizeof(buffer), format); if (ret > 0) return this->print(pos, buffer); return 0; } -uint8_t TM1637Display::strftime(const char* format, time::ESPTime time) { return this->strftime(0, format, time); } +uint8_t TM1637Display::strftime(const char *format, time::ESPTime time) { return this->strftime(0, format, time); } #endif } // namespace tm1637 diff --git a/esphome/components/tm1651/tm1651.cpp b/esphome/components/tm1651/tm1651.cpp index 0417706327..bb37e9f4c9 100644 --- a/esphome/components/tm1651/tm1651.cpp +++ b/esphome/components/tm1651/tm1651.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace tm1651 { -static const char *TAG = "tm1651.display"; +static const char *const TAG = "tm1651.display"; static const uint8_t MAX_INPUT_LEVEL_PERCENT = 100; static const uint8_t TM1651_MAX_LEVEL = 7; diff --git a/esphome/components/tmp102/tmp102.cpp b/esphome/components/tmp102/tmp102.cpp index 204c4f0805..7b3dcad4aa 100644 --- a/esphome/components/tmp102/tmp102.cpp +++ b/esphome/components/tmp102/tmp102.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace tmp102 { -static const char *TAG = "tmp102"; +static const char *const TAG = "tmp102"; static const uint8_t TMP102_ADDRESS = 0x48; static const uint8_t TMP102_REGISTER_TEMPERATURE = 0x00; diff --git a/esphome/components/tmp117/tmp117.cpp b/esphome/components/tmp117/tmp117.cpp index 9040d9bfed..f719ea93b6 100644 --- a/esphome/components/tmp117/tmp117.cpp +++ b/esphome/components/tmp117/tmp117.cpp @@ -7,7 +7,7 @@ namespace esphome { namespace tmp117 { -static const char *TAG = "tmp117"; +static const char *const TAG = "tmp117"; void TMP117Component::update() { int16_t data; diff --git a/esphome/components/tof10120/tof10120_sensor.cpp b/esphome/components/tof10120/tof10120_sensor.cpp index 4e2732bb08..cabfdad41d 100644 --- a/esphome/components/tof10120/tof10120_sensor.cpp +++ b/esphome/components/tof10120/tof10120_sensor.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace tof10120 { -static const char *TAG = "tof10120"; +static const char *const TAG = "tof10120"; static const uint8_t TOF10120_READ_DISTANCE_CMD[] = {0x00}; static const uint8_t TOF10120_DEFAULT_DELAY = 30; diff --git a/esphome/components/toshiba/toshiba.cpp b/esphome/components/toshiba/toshiba.cpp index b932516edf..c08ae898b5 100644 --- a/esphome/components/toshiba/toshiba.cpp +++ b/esphome/components/toshiba/toshiba.cpp @@ -36,7 +36,7 @@ const uint8_t TOSHIBA_POWER_ECO = 0x03; const uint8_t TOSHIBA_MOTION_SWING = 0x04; const uint8_t TOSHIBA_MOTION_FIX = 0x00; -static const char *TAG = "toshiba.climate"; +static const char *const TAG = "toshiba.climate"; void ToshibaClimate::transmit_state() { uint8_t message[16] = {0}; diff --git a/esphome/components/total_daily_energy/total_daily_energy.cpp b/esphome/components/total_daily_energy/total_daily_energy.cpp index 1816c46844..8c5ef8c137 100644 --- a/esphome/components/total_daily_energy/total_daily_energy.cpp +++ b/esphome/components/total_daily_energy/total_daily_energy.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace total_daily_energy { -static const char *TAG = "total_daily_energy"; +static const char *const TAG = "total_daily_energy"; void TotalDailyEnergy::setup() { this->pref_ = global_preferences.make_preference(this->get_object_id_hash()); diff --git a/esphome/components/tsl2561/tsl2561.cpp b/esphome/components/tsl2561/tsl2561.cpp index ffd39a54b0..e80e9220d8 100644 --- a/esphome/components/tsl2561/tsl2561.cpp +++ b/esphome/components/tsl2561/tsl2561.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace tsl2561 { -static const char *TAG = "tsl2561"; +static const char *const TAG = "tsl2561"; static const uint8_t TSL2561_COMMAND_BIT = 0x80; static const uint8_t TSL2561_WORD_BIT = 0x20; diff --git a/esphome/components/ttp229_bsf/ttp229_bsf.cpp b/esphome/components/ttp229_bsf/ttp229_bsf.cpp index 9b5c3c67de..93c947d28a 100644 --- a/esphome/components/ttp229_bsf/ttp229_bsf.cpp +++ b/esphome/components/ttp229_bsf/ttp229_bsf.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace ttp229_bsf { -static const char *TAG = "ttp229_bsf"; +static const char *const TAG = "ttp229_bsf"; void TTP229BSFComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up ttp229_bsf... "); diff --git a/esphome/components/ttp229_lsf/ttp229_lsf.cpp b/esphome/components/ttp229_lsf/ttp229_lsf.cpp index 2bd5f8556d..6e3e68ea7a 100644 --- a/esphome/components/ttp229_lsf/ttp229_lsf.cpp +++ b/esphome/components/ttp229_lsf/ttp229_lsf.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace ttp229_lsf { -static const char *TAG = "ttp229_lsf"; +static const char *const TAG = "ttp229_lsf"; void TTP229LSFComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up ttp229..."); diff --git a/esphome/components/tuya/binary_sensor/tuya_binary_sensor.cpp b/esphome/components/tuya/binary_sensor/tuya_binary_sensor.cpp index 6b2954a6a0..edfbb2ac60 100644 --- a/esphome/components/tuya/binary_sensor/tuya_binary_sensor.cpp +++ b/esphome/components/tuya/binary_sensor/tuya_binary_sensor.cpp @@ -4,10 +4,10 @@ namespace esphome { namespace tuya { -static const char *TAG = "tuya.binary_sensor"; +static const char *const TAG = "tuya.binary_sensor"; void TuyaBinarySensor::setup() { - this->parent_->register_listener(this->sensor_id_, [this](TuyaDatapoint datapoint) { + this->parent_->register_listener(this->sensor_id_, [this](const TuyaDatapoint &datapoint) { ESP_LOGV(TAG, "MCU reported binary sensor %u is: %s", datapoint.id, ONOFF(datapoint.value_bool)); this->publish_state(datapoint.value_bool); }); diff --git a/esphome/components/tuya/climate/tuya_climate.cpp b/esphome/components/tuya/climate/tuya_climate.cpp index fbd25ee03a..5f214d3bfe 100644 --- a/esphome/components/tuya/climate/tuya_climate.cpp +++ b/esphome/components/tuya/climate/tuya_climate.cpp @@ -4,11 +4,11 @@ namespace esphome { namespace tuya { -static const char *TAG = "tuya.climate"; +static const char *const TAG = "tuya.climate"; void TuyaClimate::setup() { if (this->switch_id_.has_value()) { - this->parent_->register_listener(*this->switch_id_, [this](TuyaDatapoint datapoint) { + this->parent_->register_listener(*this->switch_id_, [this](const TuyaDatapoint &datapoint) { ESP_LOGV(TAG, "MCU reported switch is: %s", ONOFF(datapoint.value_bool)); this->mode = climate::CLIMATE_MODE_OFF; if (datapoint.value_bool) { @@ -25,7 +25,7 @@ void TuyaClimate::setup() { }); } if (this->active_state_id_.has_value()) { - this->parent_->register_listener(*this->active_state_id_, [this](TuyaDatapoint datapoint) { + this->parent_->register_listener(*this->active_state_id_, [this](const TuyaDatapoint &datapoint) { ESP_LOGV(TAG, "MCU reported active state is: %u", datapoint.value_enum); this->active_state_ = datapoint.value_enum; this->compute_state_(); @@ -33,7 +33,7 @@ void TuyaClimate::setup() { }); } if (this->target_temperature_id_.has_value()) { - this->parent_->register_listener(*this->target_temperature_id_, [this](TuyaDatapoint datapoint) { + this->parent_->register_listener(*this->target_temperature_id_, [this](const TuyaDatapoint &datapoint) { this->target_temperature = datapoint.value_int * this->target_temperature_multiplier_; ESP_LOGV(TAG, "MCU reported target temperature is: %.1f", this->target_temperature); this->compute_state_(); @@ -41,7 +41,7 @@ void TuyaClimate::setup() { }); } if (this->current_temperature_id_.has_value()) { - this->parent_->register_listener(*this->current_temperature_id_, [this](TuyaDatapoint datapoint) { + this->parent_->register_listener(*this->current_temperature_id_, [this](const TuyaDatapoint &datapoint) { this->current_temperature = datapoint.value_int * this->current_temperature_multiplier_; ESP_LOGV(TAG, "MCU reported current temperature is: %.1f", this->current_temperature); this->compute_state_(); diff --git a/esphome/components/tuya/fan/tuya_fan.cpp b/esphome/components/tuya/fan/tuya_fan.cpp index b2326034a1..ed92713e52 100644 --- a/esphome/components/tuya/fan/tuya_fan.cpp +++ b/esphome/components/tuya/fan/tuya_fan.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace tuya { -static const char *TAG = "tuya.fan"; +static const char *const TAG = "tuya.fan"; void TuyaFan::setup() { auto traits = fan::FanTraits(this->oscillation_id_.has_value(), this->speed_id_.has_value(), @@ -13,7 +13,7 @@ void TuyaFan::setup() { this->fan_->set_traits(traits); if (this->speed_id_.has_value()) { - this->parent_->register_listener(*this->speed_id_, [this](TuyaDatapoint datapoint) { + this->parent_->register_listener(*this->speed_id_, [this](const TuyaDatapoint &datapoint) { ESP_LOGV(TAG, "MCU reported speed of: %d", datapoint.value_enum); auto call = this->fan_->make_call(); if (datapoint.value_enum < this->speed_count_) @@ -24,7 +24,7 @@ void TuyaFan::setup() { }); } if (this->switch_id_.has_value()) { - this->parent_->register_listener(*this->switch_id_, [this](TuyaDatapoint datapoint) { + this->parent_->register_listener(*this->switch_id_, [this](const TuyaDatapoint &datapoint) { ESP_LOGV(TAG, "MCU reported switch is: %s", ONOFF(datapoint.value_bool)); auto call = this->fan_->make_call(); call.set_state(datapoint.value_bool); @@ -32,7 +32,7 @@ void TuyaFan::setup() { }); } if (this->oscillation_id_.has_value()) { - this->parent_->register_listener(*this->oscillation_id_, [this](TuyaDatapoint datapoint) { + this->parent_->register_listener(*this->oscillation_id_, [this](const TuyaDatapoint &datapoint) { ESP_LOGV(TAG, "MCU reported oscillation is: %s", ONOFF(datapoint.value_bool)); auto call = this->fan_->make_call(); call.set_oscillating(datapoint.value_bool); @@ -40,7 +40,7 @@ void TuyaFan::setup() { }); } if (this->direction_id_.has_value()) { - this->parent_->register_listener(*this->direction_id_, [this](TuyaDatapoint datapoint) { + this->parent_->register_listener(*this->direction_id_, [this](const TuyaDatapoint &datapoint) { auto call = this->fan_->make_call(); call.set_direction(datapoint.value_bool ? fan::FAN_DIRECTION_REVERSE : fan::FAN_DIRECTION_FORWARD); call.perform(); @@ -80,7 +80,7 @@ void TuyaFan::write_state() { } if (this->speed_id_.has_value()) { ESP_LOGV(TAG, "Setting speed: %d", this->fan_->speed); - this->parent_->set_datapoint_value(*this->speed_id_, this->fan_->speed); + this->parent_->set_datapoint_value(*this->speed_id_, this->fan_->speed - 1); } } diff --git a/esphome/components/tuya/light/tuya_light.cpp b/esphome/components/tuya/light/tuya_light.cpp index 4befad401e..d7e3561328 100644 --- a/esphome/components/tuya/light/tuya_light.cpp +++ b/esphome/components/tuya/light/tuya_light.cpp @@ -4,11 +4,11 @@ namespace esphome { namespace tuya { -static const char *TAG = "tuya.light"; +static const char *const TAG = "tuya.light"; void TuyaLight::setup() { if (this->color_temperature_id_.has_value()) { - this->parent_->register_listener(*this->color_temperature_id_, [this](TuyaDatapoint datapoint) { + this->parent_->register_listener(*this->color_temperature_id_, [this](const TuyaDatapoint &datapoint) { auto call = this->state_->make_call(); call.set_color_temperature(this->cold_white_temperature_ + (this->warm_white_temperature_ - this->cold_white_temperature_) * @@ -17,14 +17,14 @@ void TuyaLight::setup() { }); } if (this->dimmer_id_.has_value()) { - this->parent_->register_listener(*this->dimmer_id_, [this](TuyaDatapoint datapoint) { + this->parent_->register_listener(*this->dimmer_id_, [this](const TuyaDatapoint &datapoint) { auto call = this->state_->make_call(); call.set_brightness(float(datapoint.value_uint) / this->max_value_); call.perform(); }); } if (switch_id_.has_value()) { - this->parent_->register_listener(*this->switch_id_, [this](TuyaDatapoint datapoint) { + this->parent_->register_listener(*this->switch_id_, [this](const TuyaDatapoint &datapoint) { auto call = this->state_->make_call(); call.set_state(datapoint.value_bool); call.perform(); diff --git a/esphome/components/tuya/sensor/tuya_sensor.cpp b/esphome/components/tuya/sensor/tuya_sensor.cpp index 483552950f..1e39c1bc35 100644 --- a/esphome/components/tuya/sensor/tuya_sensor.cpp +++ b/esphome/components/tuya/sensor/tuya_sensor.cpp @@ -4,10 +4,10 @@ namespace esphome { namespace tuya { -static const char *TAG = "tuya.sensor"; +static const char *const TAG = "tuya.sensor"; void TuyaSensor::setup() { - this->parent_->register_listener(this->sensor_id_, [this](TuyaDatapoint datapoint) { + this->parent_->register_listener(this->sensor_id_, [this](const TuyaDatapoint &datapoint) { if (datapoint.type == TuyaDatapointType::BOOLEAN) { ESP_LOGV(TAG, "MCU reported sensor %u is: %s", datapoint.id, ONOFF(datapoint.value_bool)); this->publish_state(datapoint.value_bool); diff --git a/esphome/components/tuya/switch/tuya_switch.cpp b/esphome/components/tuya/switch/tuya_switch.cpp index c735b32341..8cd09fb01c 100644 --- a/esphome/components/tuya/switch/tuya_switch.cpp +++ b/esphome/components/tuya/switch/tuya_switch.cpp @@ -4,10 +4,10 @@ namespace esphome { namespace tuya { -static const char *TAG = "tuya.switch"; +static const char *const TAG = "tuya.switch"; void TuyaSwitch::setup() { - this->parent_->register_listener(this->switch_id_, [this](TuyaDatapoint datapoint) { + this->parent_->register_listener(this->switch_id_, [this](const TuyaDatapoint &datapoint) { ESP_LOGV(TAG, "MCU reported switch %u is: %s", this->switch_id_, ONOFF(datapoint.value_bool)); this->publish_state(datapoint.value_bool); }); diff --git a/esphome/components/tuya/tuya.cpp b/esphome/components/tuya/tuya.cpp index d1f8b8c9e2..d9a0a9932a 100644 --- a/esphome/components/tuya/tuya.cpp +++ b/esphome/components/tuya/tuya.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace tuya { -static const char *TAG = "tuya"; +static const char *const TAG = "tuya"; static const int COMMAND_DELAY = 50; void Tuya::setup() { @@ -232,7 +232,7 @@ void Tuya::handle_datapoint_(const uint8_t *buffer, size_t len) { const uint8_t *data = buffer + 4; size_t data_len = len - 4; if (data_size != data_len) { - ESP_LOGW(TAG, "Datapoint %u is not expected size", datapoint.id); + ESP_LOGW(TAG, "Datapoint %u is not expected size (%zu != %zu)", datapoint.id, data_size, data_len); return; } datapoint.len = data_len; @@ -339,7 +339,7 @@ void Tuya::process_command_queue_() { } } -void Tuya::send_command_(TuyaCommand command) { +void Tuya::send_command_(const TuyaCommand &command) { command_queue_.push_back(command); process_command_queue_(); } @@ -427,7 +427,7 @@ void Tuya::set_datapoint_value(uint8_t datapoint_id, uint32_t value) { this->send_datapoint_command_(datapoint->id, datapoint->type, data); } -void Tuya::set_datapoint_value(uint8_t datapoint_id, std::string value) { +void Tuya::set_datapoint_value(uint8_t datapoint_id, const std::string &value) { ESP_LOGD(TAG, "Setting datapoint %u to %s", datapoint_id, value.c_str()); optional datapoint = this->get_datapoint_(datapoint_id); if (!datapoint.has_value()) { diff --git a/esphome/components/tuya/tuya.h b/esphome/components/tuya/tuya.h index 42848b4914..4ca4f56366 100644 --- a/esphome/components/tuya/tuya.h +++ b/esphome/components/tuya/tuya.h @@ -76,7 +76,7 @@ class Tuya : public Component, public uart::UARTDevice { void dump_config() override; void register_listener(uint8_t datapoint_id, const std::function &func); void set_datapoint_value(uint8_t datapoint_id, uint32_t value); - void set_datapoint_value(uint8_t datapoint_id, std::string value); + void set_datapoint_value(uint8_t datapoint_id, const std::string &value); #ifdef USE_TIME void set_time_id(time::RealTimeClock *time_id) { this->time_id_ = time_id; } #endif @@ -93,7 +93,7 @@ class Tuya : public Component, public uart::UARTDevice { void handle_command_(uint8_t command, uint8_t version, const uint8_t *buffer, size_t len); void send_raw_command_(TuyaCommand command); void process_command_queue_(); - void send_command_(TuyaCommand command); + void send_command_(const TuyaCommand &command); void send_empty_command_(TuyaCommandType command); void send_datapoint_command_(uint8_t datapoint_id, TuyaDatapointType datapoint_type, std::vector data); void send_wifi_status_(); diff --git a/esphome/components/tx20/tx20.cpp b/esphome/components/tx20/tx20.cpp index f3dafda288..f48e29521c 100644 --- a/esphome/components/tx20/tx20.cpp +++ b/esphome/components/tx20/tx20.cpp @@ -5,12 +5,12 @@ namespace esphome { namespace tx20 { -static const char *TAG = "tx20"; +static const char *const TAG = "tx20"; static const uint8_t MAX_BUFFER_SIZE = 41; static const uint16_t TX20_MAX_TIME = MAX_BUFFER_SIZE * 1200 + 5000; static const uint16_t TX20_BIT_TIME = 1200; -static const char *DIRECTIONS[] = {"N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", - "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"}; +static const char *const DIRECTIONS[] = {"N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", + "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"}; void Tx20Component::setup() { ESP_LOGCONFIG(TAG, "Setting up Tx20"); diff --git a/esphome/components/uart/switch/uart_switch.cpp b/esphome/components/uart/switch/uart_switch.cpp index a1ebb6dbb9..ffed08c731 100644 --- a/esphome/components/uart/switch/uart_switch.cpp +++ b/esphome/components/uart/switch/uart_switch.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace uart { -static const char *TAG = "uart.switch"; +static const char *const TAG = "uart.switch"; void UARTSwitch::loop() { if (this->state && this->send_every_) { diff --git a/esphome/components/uart/uart.cpp b/esphome/components/uart/uart.cpp index 1cfdb38a90..08e3395a7a 100644 --- a/esphome/components/uart/uart.cpp +++ b/esphome/components/uart/uart.cpp @@ -11,7 +11,7 @@ namespace esphome { namespace uart { -static const char *TAG = "uart"; +static const char *const TAG = "uart"; size_t UARTComponent::write(uint8_t data) { this->write_byte(data); diff --git a/esphome/components/uart/uart.h b/esphome/components/uart/uart.h index 6dd62070da..cd54290c73 100644 --- a/esphome/components/uart/uart.h +++ b/esphome/components/uart/uart.h @@ -56,6 +56,7 @@ class ESP8266SoftwareSerial { class UARTComponent : public Component, public Stream { public: void set_baud_rate(uint32_t baud_rate) { baud_rate_ = baud_rate; } + uint32_t get_baud_rate() const { return baud_rate_; } uint32_t get_config(); diff --git a/esphome/components/uart/uart_esp32.cpp b/esphome/components/uart/uart_esp32.cpp index 920fe660be..89de4c0cc1 100644 --- a/esphome/components/uart/uart_esp32.cpp +++ b/esphome/components/uart/uart_esp32.cpp @@ -7,7 +7,7 @@ namespace esphome { namespace uart { -static const char *TAG = "uart_esp32"; +static const char *const TAG = "uart_esp32"; uint8_t next_uart_num = 1; static const uint32_t UART_PARITY_EVEN = 0 << 0; diff --git a/esphome/components/uart/uart_esp8266.cpp b/esphome/components/uart/uart_esp8266.cpp index edb530f537..6f7d4c1f8b 100644 --- a/esphome/components/uart/uart_esp8266.cpp +++ b/esphome/components/uart/uart_esp8266.cpp @@ -8,7 +8,7 @@ namespace esphome { namespace uart { -static const char *TAG = "uart_esp8266"; +static const char *const TAG = "uart_esp8266"; uint32_t UARTComponent::get_config() { uint32_t config = 0; diff --git a/esphome/components/uln2003/uln2003.cpp b/esphome/components/uln2003/uln2003.cpp index 9bf34490ec..1af9806906 100644 --- a/esphome/components/uln2003/uln2003.cpp +++ b/esphome/components/uln2003/uln2003.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace uln2003 { -static const char *TAG = "uln2003.stepper"; +static const char *const TAG = "uln2003.stepper"; void ULN2003::setup() { this->pin_a_->setup(); diff --git a/esphome/components/ultrasonic/sensor.py b/esphome/components/ultrasonic/sensor.py index 77b08b3324..d5f2cef05f 100644 --- a/esphome/components/ultrasonic/sensor.py +++ b/esphome/components/ultrasonic/sensor.py @@ -37,13 +37,6 @@ CONFIG_SCHEMA = ( cv.Optional( CONF_PULSE_TIME, default="10us" ): cv.positive_time_period_microseconds, - cv.Optional("timeout_meter"): cv.invalid( - "The timeout_meter option has been renamed " "to 'timeout' in 1.12." - ), - cv.Optional("timeout_time"): cv.invalid( - "The timeout_time option has been removed. Please " - "use 'timeout' in 1.12." - ), } ) .extend(cv.polling_component_schema("60s")) diff --git a/esphome/components/ultrasonic/ultrasonic_sensor.cpp b/esphome/components/ultrasonic/ultrasonic_sensor.cpp index c573c21863..e53cd7cf7a 100644 --- a/esphome/components/ultrasonic/ultrasonic_sensor.cpp +++ b/esphome/components/ultrasonic/ultrasonic_sensor.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace ultrasonic { -static const char *TAG = "ultrasonic.sensor"; +static const char *const TAG = "ultrasonic.sensor"; void UltrasonicSensorComponent::setup() { ESP_LOGCONFIG(TAG, "Setting up Ultrasonic Sensor..."); diff --git a/esphome/components/uptime/uptime_sensor.cpp b/esphome/components/uptime/uptime_sensor.cpp index 5d117ab61d..755795ad53 100644 --- a/esphome/components/uptime/uptime_sensor.cpp +++ b/esphome/components/uptime/uptime_sensor.cpp @@ -5,7 +5,7 @@ namespace esphome { namespace uptime { -static const char *TAG = "uptime.sensor"; +static const char *const TAG = "uptime.sensor"; void UptimeSensor::update() { const uint32_t ms = millis(); diff --git a/esphome/components/version/version_text_sensor.cpp b/esphome/components/version/version_text_sensor.cpp index fa8e6a9d01..5b2437ab62 100644 --- a/esphome/components/version/version_text_sensor.cpp +++ b/esphome/components/version/version_text_sensor.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace version { -static const char *TAG = "version.text_sensor"; +static const char *const TAG = "version.text_sensor"; void VersionTextSensor::setup() { if (this->hide_timestamp_) { diff --git a/esphome/components/vl53l0x/vl53l0x_sensor.cpp b/esphome/components/vl53l0x/vl53l0x_sensor.cpp index 1926f7d113..65a4ec72bb 100644 --- a/esphome/components/vl53l0x/vl53l0x_sensor.cpp +++ b/esphome/components/vl53l0x/vl53l0x_sensor.cpp @@ -13,9 +13,10 @@ namespace esphome { namespace vl53l0x { -static const char *TAG = "vl53l0x"; -std::list VL53L0XSensor::vl53_sensors; -bool VL53L0XSensor::enable_pin_setup_complete = false; +static const char *const TAG = "vl53l0x"; + +std::list VL53L0XSensor::vl53_sensors; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +bool VL53L0XSensor::enable_pin_setup_complete = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) VL53L0XSensor::VL53L0XSensor() { VL53L0XSensor::vl53_sensors.push_back(this); } diff --git a/esphome/components/vl53l0x/vl53l0x_sensor.h b/esphome/components/vl53l0x/vl53l0x_sensor.h index 2662b768ae..0c37df67a2 100644 --- a/esphome/components/vl53l0x/vl53l0x_sensor.h +++ b/esphome/components/vl53l0x/vl53l0x_sensor.h @@ -264,8 +264,8 @@ class VL53L0XSensor : public sensor::Sensor, public PollingComponent, public i2c uint16_t timeout_start_us_; uint16_t timeout_us_{}; - static std::list vl53_sensors; - static bool enable_pin_setup_complete; + static std::list vl53_sensors; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) + static bool enable_pin_setup_complete; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) }; } // namespace vl53l0x diff --git a/esphome/components/waveshare_epaper/waveshare_epaper.cpp b/esphome/components/waveshare_epaper/waveshare_epaper.cpp index daaa77b7fa..4518dd60df 100644 --- a/esphome/components/waveshare_epaper/waveshare_epaper.cpp +++ b/esphome/components/waveshare_epaper/waveshare_epaper.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace waveshare_epaper { -static const char *TAG = "waveshare_epaper"; +static const char *const TAG = "waveshare_epaper"; static const uint8_t LUT_SIZE_WAVESHARE = 30; diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index fbb215ee17..b775d44211 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -19,7 +19,7 @@ namespace esphome { namespace web_server { -static const char *TAG = "web_server"; +static const char *const TAG = "web_server"; void write_row(AsyncResponseStream *stream, Nameable *obj, const std::string &klass, const std::string &action) { if (obj->is_internal()) @@ -119,6 +119,12 @@ void WebServer::setup() { if (!obj->is_internal()) client->send(this->cover_json(obj).c_str(), "state"); #endif + +#ifdef USE_NUMBER + for (auto *obj : App.get_numbers()) + if (!obj->is_internal()) + client->send(this->number_json(obj, obj->state).c_str(), "state"); +#endif }); #ifdef USE_LOGGER @@ -196,6 +202,11 @@ void WebServer::handle_index_request(AsyncWebServerRequest *request) { write_row(stream, obj, "cover", ""); #endif +#ifdef USE_NUMBER + for (auto *obj : App.get_numbers()) + write_row(stream, obj, "number", ""); +#endif + stream->print(F("

See ESPHome Web API for " "REST API documentation.

" "

OTA Update

events_.send(this->sensor_json(obj, state).c_str(), "state"); } -void WebServer::handle_sensor_request(AsyncWebServerRequest *request, UrlMatch match) { +void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (sensor::Sensor *obj : App.get_sensors()) { if (obj->is_internal()) continue; @@ -267,10 +278,10 @@ std::string WebServer::sensor_json(sensor::Sensor *obj, float value) { #endif #ifdef USE_TEXT_SENSOR -void WebServer::on_text_sensor_update(text_sensor::TextSensor *obj, std::string state) { +void WebServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) { this->events_.send(this->text_sensor_json(obj, state).c_str(), "state"); } -void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, UrlMatch match) { +void WebServer::handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (text_sensor::TextSensor *obj : App.get_text_sensors()) { if (obj->is_internal()) continue; @@ -302,7 +313,7 @@ std::string WebServer::switch_json(switch_::Switch *obj, bool value) { root["value"] = value; }); } -void WebServer::handle_switch_request(AsyncWebServerRequest *request, UrlMatch match) { +void WebServer::handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (switch_::Switch *obj : App.get_switches()) { if (obj->is_internal()) continue; @@ -343,7 +354,7 @@ std::string WebServer::binary_sensor_json(binary_sensor::BinarySensor *obj, bool root["value"] = value; }); } -void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, UrlMatch match) { +void WebServer::handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (binary_sensor::BinarySensor *obj : App.get_binary_sensors()) { if (obj->is_internal()) continue; @@ -387,7 +398,7 @@ std::string WebServer::fan_json(fan::FanState *obj) { root["oscillation"] = obj->oscillating; }); } -void WebServer::handle_fan_request(AsyncWebServerRequest *request, UrlMatch match) { +void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (fan::FanState *obj : App.get_fans()) { if (obj->is_internal()) continue; @@ -453,7 +464,7 @@ void WebServer::on_light_update(light::LightState *obj) { return; this->events_.send(this->light_json(obj).c_str(), "state"); } -void WebServer::handle_light_request(AsyncWebServerRequest *request, UrlMatch match) { +void WebServer::handle_light_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (light::LightState *obj : App.get_lights()) { if (obj->is_internal()) continue; @@ -528,7 +539,7 @@ void WebServer::on_cover_update(cover::Cover *obj) { return; this->events_.send(this->cover_json(obj).c_str(), "state"); } -void WebServer::handle_cover_request(AsyncWebServerRequest *request, UrlMatch match) { +void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMatch &match) { for (cover::Cover *obj : App.get_covers()) { if (obj->is_internal()) continue; @@ -584,6 +595,33 @@ std::string WebServer::cover_json(cover::Cover *obj) { } #endif +#ifdef USE_NUMBER +void WebServer::on_number_update(number::Number *obj, float state) { + this->events_.send(this->number_json(obj, state).c_str(), "state"); +} +void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match) { + for (auto *obj : App.get_numbers()) { + if (obj->is_internal()) + continue; + if (obj->get_object_id() != match.id) + continue; + std::string data = this->number_json(obj, obj->state); + request->send(200, "text/json", data.c_str()); + return; + } + request->send(404); +} +std::string WebServer::number_json(number::Number *obj, float value) { + return json::build_json([obj, value](JsonObject &root) { + root["id"] = "number-" + obj->get_object_id(); + char buffer[64]; + snprintf(buffer, sizeof(buffer), "%f", value); + root["state"] = buffer; + root["value"] = value; + }); +} +#endif + bool WebServer::canHandle(AsyncWebServerRequest *request) { if (request->url() == "/") return true; @@ -636,6 +674,11 @@ bool WebServer::canHandle(AsyncWebServerRequest *request) { return true; #endif +#ifdef USE_NUMBER + if (request->method() == HTTP_GET && match.domain == "number") + return true; +#endif + return false; } void WebServer::handleRequest(AsyncWebServerRequest *request) { @@ -711,6 +754,13 @@ void WebServer::handleRequest(AsyncWebServerRequest *request) { return; } #endif + +#ifdef USE_NUMBER + if (match.domain == "number") { + this->handle_number_request(request, match); + return; + } +#endif } bool WebServer::isRequestHandlerTrivial() { return false; } diff --git a/esphome/components/web_server/web_server.h b/esphome/components/web_server/web_server.h index b3bf2ef7f7..4789c6e1c0 100644 --- a/esphome/components/web_server/web_server.h +++ b/esphome/components/web_server/web_server.h @@ -88,7 +88,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { #ifdef USE_SENSOR void on_sensor_update(sensor::Sensor *obj, float state) override; /// Handle a sensor request under '/sensor/'. - void handle_sensor_request(AsyncWebServerRequest *request, UrlMatch match); + void handle_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match); /// Dump the sensor state with its value as a JSON string. std::string sensor_json(sensor::Sensor *obj, float value); @@ -98,7 +98,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { void on_switch_update(switch_::Switch *obj, bool state) override; /// Handle a switch request under '/switch//'. - void handle_switch_request(AsyncWebServerRequest *request, UrlMatch match); + void handle_switch_request(AsyncWebServerRequest *request, const UrlMatch &match); /// Dump the switch state with its value as a JSON string. std::string switch_json(switch_::Switch *obj, bool value); @@ -108,7 +108,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { void on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) override; /// Handle a binary sensor request under '/binary_sensor/'. - void handle_binary_sensor_request(AsyncWebServerRequest *request, UrlMatch match); + void handle_binary_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match); /// Dump the binary sensor state with its value as a JSON string. std::string binary_sensor_json(binary_sensor::BinarySensor *obj, bool value); @@ -118,7 +118,7 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { void on_fan_update(fan::FanState *obj) override; /// Handle a fan request under '/fan//'. - void handle_fan_request(AsyncWebServerRequest *request, UrlMatch match); + void handle_fan_request(AsyncWebServerRequest *request, const UrlMatch &match); /// Dump the fan state as a JSON string. std::string fan_json(fan::FanState *obj); @@ -128,17 +128,17 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { void on_light_update(light::LightState *obj) override; /// Handle a light request under '/light//'. - void handle_light_request(AsyncWebServerRequest *request, UrlMatch match); + void handle_light_request(AsyncWebServerRequest *request, const UrlMatch &match); /// Dump the light state as a JSON string. std::string light_json(light::LightState *obj); #endif #ifdef USE_TEXT_SENSOR - void on_text_sensor_update(text_sensor::TextSensor *obj, std::string state) override; + void on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) override; /// Handle a text sensor request under '/text_sensor/'. - void handle_text_sensor_request(AsyncWebServerRequest *request, UrlMatch match); + void handle_text_sensor_request(AsyncWebServerRequest *request, const UrlMatch &match); /// Dump the text sensor state with its value as a JSON string. std::string text_sensor_json(text_sensor::TextSensor *obj, const std::string &value); @@ -148,12 +148,21 @@ class WebServer : public Controller, public Component, public AsyncWebHandler { void on_cover_update(cover::Cover *obj) override; /// Handle a cover request under '/cover//'. - void handle_cover_request(AsyncWebServerRequest *request, UrlMatch match); + void handle_cover_request(AsyncWebServerRequest *request, const UrlMatch &match); /// Dump the cover state as a JSON string. std::string cover_json(cover::Cover *obj); #endif +#ifdef USE_NUMBER + void on_number_update(number::Number *obj, float state) override; + /// Handle a number request under '/number/'. + void handle_number_request(AsyncWebServerRequest *request, const UrlMatch &match); + + /// Dump the number state with its value as a JSON string. + std::string number_json(number::Number *obj, float value); +#endif + /// Override the web handler's canHandle method. bool canHandle(AsyncWebServerRequest *request) override; /// Override the web handler's handleRequest method. diff --git a/esphome/components/web_server_base/web_server_base.cpp b/esphome/components/web_server_base/web_server_base.cpp index b7548504e3..85711704b9 100644 --- a/esphome/components/web_server_base/web_server_base.cpp +++ b/esphome/components/web_server_base/web_server_base.cpp @@ -13,7 +13,7 @@ namespace esphome { namespace web_server_base { -static const char *TAG = "web_server_base"; +static const char *const TAG = "web_server_base"; void report_ota_error() { StreamString ss; diff --git a/esphome/components/whirlpool/whirlpool.cpp b/esphome/components/whirlpool/whirlpool.cpp index 5a1e025a38..d705b42a8c 100644 --- a/esphome/components/whirlpool/whirlpool.cpp +++ b/esphome/components/whirlpool/whirlpool.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace whirlpool { -static const char *TAG = "whirlpool.climate"; +static const char *const TAG = "whirlpool.climate"; const uint16_t WHIRLPOOL_HEADER_MARK = 9000; const uint16_t WHIRLPOOL_HEADER_SPACE = 4494; diff --git a/esphome/components/wifi/__init__.py b/esphome/components/wifi/__init__.py index 5a81d6a8f5..d066570cc8 100644 --- a/esphome/components/wifi/__init__.py +++ b/esphome/components/wifi/__init__.py @@ -205,6 +205,7 @@ def _validate(config): config[CONF_NETWORKS] = cv.ensure_list(WIFI_NETWORK_STA)(network) if (CONF_NETWORKS not in config) and (CONF_AP not in config): + config = config.copy() config[CONF_NETWORKS] = [] if config.get(CONF_FAST_CONNECT, False): @@ -248,9 +249,6 @@ CONFIG_SCHEMA = cv.All( cv.SplitDefault(CONF_OUTPUT_POWER, esp8266=20.0): cv.All( cv.decibel, cv.float_range(min=10.0, max=20.5) ), - cv.Optional("hostname"): cv.invalid( - "The hostname option has been removed in 1.11.0" - ), } ), _validate, diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 7d6cb1347e..e99cd0e1b1 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -29,7 +29,7 @@ namespace esphome { namespace wifi { -static const char *TAG = "wifi"; +static const char *const TAG = "wifi"; float WiFiComponent::get_setup_priority() const { return setup_priority::WIFI; } @@ -164,7 +164,7 @@ void WiFiComponent::loop() { WiFiComponent::WiFiComponent() { global_wifi_component = this; } -bool WiFiComponent::has_ap() const { return !this->ap_.get_ssid().empty(); } +bool WiFiComponent::has_ap() const { return this->has_ap_; } bool WiFiComponent::has_sta() const { return !this->sta_.empty(); } void WiFiComponent::set_fast_connect(bool fast_connect) { this->fast_connect_ = fast_connect; } IPAddress WiFiComponent::get_ip_address() { @@ -187,6 +187,18 @@ void WiFiComponent::setup_ap_config_() { if (this->ap_setup_) return; + if (this->ap_.get_ssid().empty()) { + std::string name = App.get_name(); + if (name.length() > 32) { + if (App.is_name_add_mac_suffix_enabled()) { + name.erase(name.begin() + 25, name.end() - 7); // Remove characters between 25 and the mac address + } else { + name = name.substr(0, 32); + } + } + this->ap_.set_ssid(name); + } + ESP_LOGCONFIG(TAG, "Setting up AP..."); ESP_LOGCONFIG(TAG, " AP SSID: '%s'", this->ap_.get_ssid().c_str()); @@ -212,7 +224,10 @@ void WiFiComponent::setup_ap_config_() { float WiFiComponent::get_loop_priority() const { return 10.0f; // before other loop components } -void WiFiComponent::set_ap(const WiFiAP &ap) { this->ap_ = ap; } +void WiFiComponent::set_ap(const WiFiAP &ap) { + this->ap_ = ap; + this->has_ap_ = true; +} void WiFiComponent::add_sta(const WiFiAP &ap) { this->sta_.push_back(ap); } void WiFiComponent::set_sta(const WiFiAP &ap) { this->clear_sta(); @@ -221,8 +236,8 @@ void WiFiComponent::set_sta(const WiFiAP &ap) { void WiFiComponent::clear_sta() { this->sta_.clear(); } void WiFiComponent::save_wifi_sta(const std::string &ssid, const std::string &password) { SavedWifiSettings save{}; - strcpy(save.ssid, ssid.c_str()); - strcpy(save.password, password.c_str()); + strncpy(save.ssid, ssid.c_str(), sizeof(save.ssid)); + strncpy(save.password, password.c_str(), sizeof(save.password)); this->pref_.save(&save); WiFiAP sta{}; @@ -627,7 +642,7 @@ void WiFiAP::set_password(const std::string &password) { this->password_ = passw void WiFiAP::set_eap(optional eap_auth) { this->eap_ = eap_auth; } #endif void WiFiAP::set_channel(optional channel) { this->channel_ = channel; } -void WiFiAP::set_manual_ip(optional manual_ip) { this->manual_ip_ = manual_ip; } +void WiFiAP::set_manual_ip(optional manual_ip) { this->manual_ip_ = std::move(manual_ip); } void WiFiAP::set_hidden(bool hidden) { this->hidden_ = hidden; } const std::string &WiFiAP::get_ssid() const { return this->ssid_; } const optional &WiFiAP::get_bssid() const { return this->bssid_; } @@ -639,9 +654,14 @@ const optional &WiFiAP::get_channel() const { return this->channel_; } const optional &WiFiAP::get_manual_ip() const { return this->manual_ip_; } bool WiFiAP::get_hidden() const { return this->hidden_; } -WiFiScanResult::WiFiScanResult(const bssid_t &bssid, const std::string &ssid, uint8_t channel, int8_t rssi, - bool with_auth, bool is_hidden) - : bssid_(bssid), ssid_(ssid), channel_(channel), rssi_(rssi), with_auth_(with_auth), is_hidden_(is_hidden) {} +WiFiScanResult::WiFiScanResult(const bssid_t &bssid, std::string ssid, uint8_t channel, int8_t rssi, bool with_auth, + bool is_hidden) + : bssid_(bssid), + ssid_(std::move(ssid)), + channel_(channel), + rssi_(rssi), + with_auth_(with_auth), + is_hidden_(is_hidden) {} bool WiFiScanResult::matches(const WiFiAP &config) { if (config.get_hidden()) { // User configured a hidden network, only match actually hidden networks @@ -688,7 +708,7 @@ int8_t WiFiScanResult::get_rssi() const { return this->rssi_; } bool WiFiScanResult::get_with_auth() const { return this->with_auth_; } bool WiFiScanResult::get_is_hidden() const { return this->is_hidden_; } -WiFiComponent *global_wifi_component; +WiFiComponent *global_wifi_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace wifi } // namespace esphome diff --git a/esphome/components/wifi/wifi_component.h b/esphome/components/wifi/wifi_component.h index 753455ddb1..f698e09d93 100644 --- a/esphome/components/wifi/wifi_component.h +++ b/esphome/components/wifi/wifi_component.h @@ -115,8 +115,7 @@ class WiFiAP { class WiFiScanResult { public: - WiFiScanResult(const bssid_t &bssid, const std::string &ssid, uint8_t channel, int8_t rssi, bool with_auth, - bool is_hidden); + WiFiScanResult(const bssid_t &bssid, std::string ssid, uint8_t channel, int8_t rssi, bool with_auth, bool is_hidden); bool matches(const WiFiAP &config); @@ -251,7 +250,7 @@ class WiFiComponent : public Component { bool wifi_sta_ip_config_(optional manual_ip); IPAddress wifi_sta_ip_(); bool wifi_apply_hostname_(); - bool wifi_sta_connect_(WiFiAP ap); + bool wifi_sta_connect_(const WiFiAP &ap); void wifi_pre_setup_(); wl_status_t wifi_sta_status_(); bool wifi_scan_start_(); @@ -283,6 +282,7 @@ class WiFiComponent : public Component { WiFiAP selected_ap_; bool fast_connect_{false}; + bool has_ap_{false}; WiFiAP ap_; WiFiComponentState state_{WIFI_COMPONENT_STATE_OFF}; uint32_t action_started_; @@ -300,7 +300,7 @@ class WiFiComponent : public Component { bool has_saved_wifi_settings_{false}; }; -extern WiFiComponent *global_wifi_component; +extern WiFiComponent *global_wifi_component; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) template class WiFiConnectedCondition : public Condition { public: diff --git a/esphome/components/wifi/wifi_component_esp32.cpp b/esphome/components/wifi/wifi_component_esp32.cpp index cf65ed3725..c51c17d60c 100644 --- a/esphome/components/wifi/wifi_component_esp32.cpp +++ b/esphome/components/wifi/wifi_component_esp32.cpp @@ -21,7 +21,7 @@ namespace esphome { namespace wifi { -static const char *TAG = "wifi_esp32"; +static const char *const TAG = "wifi_esp32"; bool WiFiComponent::wifi_mode_(optional sta, optional ap) { uint8_t current_mode = WiFi.getMode(); @@ -149,7 +149,7 @@ bool WiFiComponent::wifi_apply_hostname_() { } return true; } -bool WiFiComponent::wifi_sta_connect_(WiFiAP ap) { +bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { // enable STA if (!this->wifi_mode_(true, {})) return false; diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index dee3d5a4a5..2f6c32aec6 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -10,6 +10,10 @@ #include #endif +#ifdef WIFI_IS_OFF_AT_BOOT // Identifies ESP8266 Arduino 3.0.0 +#define ARDUINO_ESP8266_RELEASE_3 +#endif + extern "C" { #include "lwip/err.h" #include "lwip/dns.h" @@ -18,6 +22,12 @@ extern "C" { #if LWIP_IPV6 #include "lwip/netif.h" // struct netif #endif +#ifdef ARDUINO_ESP8266_RELEASE_3 +#include "LwipDhcpServer.h" +#define wifi_softap_set_dhcps_lease(lease) dhcpSoftAP.set_dhcps_lease(lease) +#define wifi_softap_set_dhcps_lease_time(time) dhcpSoftAP.set_dhcps_lease_time(time) +#define wifi_softap_set_dhcps_offer_option(offer, mode) dhcpSoftAP.set_dhcps_offer_option(offer, mode) +#endif } #include "esphome/core/helpers.h" @@ -29,7 +39,7 @@ extern "C" { namespace esphome { namespace wifi { -static const char *TAG = "wifi_esp8266"; +static const char *const TAG = "wifi_esp8266"; bool WiFiComponent::wifi_mode_(optional sta, optional ap) { uint8_t current_mode = wifi_get_opmode(); @@ -200,7 +210,7 @@ bool WiFiComponent::wifi_apply_hostname_() { return ret; } -bool WiFiComponent::wifi_sta_connect_(WiFiAP ap) { +bool WiFiComponent::wifi_sta_connect_(const WiFiAP &ap) { // enable STA if (!this->wifi_mode_(true, {})) return false; @@ -209,8 +219,8 @@ bool WiFiComponent::wifi_sta_connect_(WiFiAP ap) { struct station_config conf {}; memset(&conf, 0, sizeof(conf)); - strcpy(reinterpret_cast(conf.ssid), ap.get_ssid().c_str()); - strcpy(reinterpret_cast(conf.password), ap.get_password().c_str()); + strncpy(reinterpret_cast(conf.ssid), ap.get_ssid().c_str(), sizeof(conf.ssid)); + strncpy(reinterpret_cast(conf.password), ap.get_password().c_str(), sizeof(conf.password)); if (ap.get_bssid().has_value()) { conf.bssid_set = 1; @@ -649,6 +659,10 @@ bool WiFiComponent::wifi_ap_ip_config_(optional manual_ip) { return false; } +#ifdef ARDUINO_ESP8266_RELEASE_3 + dhcpSoftAP.begin(&info); +#endif + struct dhcps_lease lease {}; IPAddress start_address = info.ip.addr; start_address[3] += 99; @@ -688,7 +702,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { return false; struct softap_config conf {}; - strcpy(reinterpret_cast(conf.ssid), ap.get_ssid().c_str()); + strncpy(reinterpret_cast(conf.ssid), ap.get_ssid().c_str(), sizeof(conf.ssid)); conf.ssid_len = static_cast(ap.get_ssid().size()); conf.channel = ap.get_channel().value_or(1); conf.ssid_hidden = ap.get_hidden(); @@ -700,7 +714,7 @@ bool WiFiComponent::wifi_start_ap_(const WiFiAP &ap) { *conf.password = 0; } else { conf.authmode = AUTH_WPA2_PSK; - strcpy(reinterpret_cast(conf.password), ap.get_password().c_str()); + strncpy(reinterpret_cast(conf.password), ap.get_password().c_str(), sizeof(conf.password)); } ETS_UART_INTR_DISABLE(); diff --git a/esphome/components/wifi_info/wifi_info_text_sensor.cpp b/esphome/components/wifi_info/wifi_info_text_sensor.cpp index 08a69998fb..92e5d93a5a 100644 --- a/esphome/components/wifi_info/wifi_info_text_sensor.cpp +++ b/esphome/components/wifi_info/wifi_info_text_sensor.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace wifi_info { -static const char *TAG = "wifi_info"; +static const char *const TAG = "wifi_info"; void IPAddressWiFiInfo::dump_config() { LOG_TEXT_SENSOR("", "WifiInfo IPAddress", this); } void SSIDWiFiInfo::dump_config() { LOG_TEXT_SENSOR("", "WifiInfo SSID", this); } diff --git a/esphome/components/wifi_signal/wifi_signal_sensor.cpp b/esphome/components/wifi_signal/wifi_signal_sensor.cpp index 7b2f010c07..ba22138e2a 100644 --- a/esphome/components/wifi_signal/wifi_signal_sensor.cpp +++ b/esphome/components/wifi_signal/wifi_signal_sensor.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace wifi_signal { -static const char *TAG = "wifi_signal.sensor"; +static const char *const TAG = "wifi_signal.sensor"; void WiFiSignalSensor::dump_config() { LOG_SENSOR("", "WiFi Signal", this); } diff --git a/esphome/components/wled/wled_light_effect.cpp b/esphome/components/wled/wled_light_effect.cpp index f3d8fbd082..afff956c9c 100644 --- a/esphome/components/wled/wled_light_effect.cpp +++ b/esphome/components/wled/wled_light_effect.cpp @@ -19,7 +19,7 @@ enum Protocol { WLED_NOTIFIER = 0, WARLS = 1, DRGB = 2, DRGBW = 3, DNRGB = 4 }; const int DEFAULT_BLANK_TIME = 1000; -static const char *TAG = "wled_light_effect"; +static const char *const TAG = "wled_light_effect"; WLEDLightEffect::WLEDLightEffect(const std::string &name) : AddressableLightEffect(name) {} diff --git a/esphome/components/xiaomi_ble/xiaomi_ble.cpp b/esphome/components/xiaomi_ble/xiaomi_ble.cpp index 5806ceb72b..c736a236a1 100644 --- a/esphome/components/xiaomi_ble/xiaomi_ble.cpp +++ b/esphome/components/xiaomi_ble/xiaomi_ble.cpp @@ -10,7 +10,7 @@ namespace esphome { namespace xiaomi_ble { -static const char *TAG = "xiaomi_ble"; +static const char *const TAG = "xiaomi_ble"; bool parse_xiaomi_value(uint8_t value_type, const uint8_t *data, uint8_t value_length, XiaomiParseResult &result) { // motion detection, 1 byte, 8-bit unsigned integer diff --git a/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp b/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp index d701e8ee6d..c6e7a3f962 100644 --- a/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp +++ b/esphome/components/xiaomi_cgd1/xiaomi_cgd1.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace xiaomi_cgd1 { -static const char *TAG = "xiaomi_cgd1"; +static const char *const TAG = "xiaomi_cgd1"; void XiaomiCGD1::dump_config() { ESP_LOGCONFIG(TAG, "Xiaomi CGD1"); diff --git a/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp b/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp index cd18c12afc..2ba2ac4c0a 100644 --- a/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp +++ b/esphome/components/xiaomi_cgdk2/xiaomi_cgdk2.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace xiaomi_cgdk2 { -static const char *TAG = "xiaomi_cgdk2"; +static const char *const TAG = "xiaomi_cgdk2"; void XiaomiCGDK2::dump_config() { ESP_LOGCONFIG(TAG, "Xiaomi CGDK2"); diff --git a/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp b/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp index 86725956e0..86192fb028 100644 --- a/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp +++ b/esphome/components/xiaomi_cgg1/xiaomi_cgg1.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace xiaomi_cgg1 { -static const char *TAG = "xiaomi_cgg1"; +static const char *const TAG = "xiaomi_cgg1"; void XiaomiCGG1::dump_config() { ESP_LOGCONFIG(TAG, "Xiaomi CGG1"); diff --git a/esphome/components/xiaomi_gcls002/xiaomi_gcls002.cpp b/esphome/components/xiaomi_gcls002/xiaomi_gcls002.cpp index 24156f98ac..5f7d67b85a 100644 --- a/esphome/components/xiaomi_gcls002/xiaomi_gcls002.cpp +++ b/esphome/components/xiaomi_gcls002/xiaomi_gcls002.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace xiaomi_gcls002 { -static const char *TAG = "xiaomi_gcls002"; +static const char *const TAG = "xiaomi_gcls002"; void XiaomiGCLS002::dump_config() { ESP_LOGCONFIG(TAG, "Xiaomi GCLS002"); diff --git a/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.cpp b/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.cpp index 18e2b2439f..103201d511 100644 --- a/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.cpp +++ b/esphome/components/xiaomi_hhccjcy01/xiaomi_hhccjcy01.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace xiaomi_hhccjcy01 { -static const char *TAG = "xiaomi_hhccjcy01"; +static const char *const TAG = "xiaomi_hhccjcy01"; void XiaomiHHCCJCY01::dump_config() { ESP_LOGCONFIG(TAG, "Xiaomi HHCCJCY01"); diff --git a/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.cpp b/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.cpp index 2b5ad3a826..efc83cb6cc 100644 --- a/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.cpp +++ b/esphome/components/xiaomi_hhccpot002/xiaomi_hhccpot002.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace xiaomi_hhccpot002 { -static const char *TAG = "xiaomi_hhccpot002"; +static const char *const TAG = "xiaomi_hhccpot002"; void XiaomiHHCCPOT002 ::dump_config() { ESP_LOGCONFIG(TAG, "Xiaomi HHCCPOT002"); diff --git a/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.cpp b/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.cpp index 3e7090509b..c88a3c3b61 100644 --- a/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.cpp +++ b/esphome/components/xiaomi_jqjcy01ym/xiaomi_jqjcy01ym.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace xiaomi_jqjcy01ym { -static const char *TAG = "xiaomi_jqjcy01ym"; +static const char *const TAG = "xiaomi_jqjcy01ym"; void XiaomiJQJCY01YM::dump_config() { ESP_LOGCONFIG(TAG, "Xiaomi JQJCY01YM"); diff --git a/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.cpp b/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.cpp index 12590960e4..6b8ecdeaff 100644 --- a/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.cpp +++ b/esphome/components/xiaomi_lywsd02/xiaomi_lywsd02.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace xiaomi_lywsd02 { -static const char *TAG = "xiaomi_lywsd02"; +static const char *const TAG = "xiaomi_lywsd02"; void XiaomiLYWSD02::dump_config() { ESP_LOGCONFIG(TAG, "Xiaomi LYWSD02"); diff --git a/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp b/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp index e9cc99358b..f0a2cee8d4 100644 --- a/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp +++ b/esphome/components/xiaomi_lywsd03mmc/xiaomi_lywsd03mmc.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace xiaomi_lywsd03mmc { -static const char *TAG = "xiaomi_lywsd03mmc"; +static const char *const TAG = "xiaomi_lywsd03mmc"; void XiaomiLYWSD03MMC::dump_config() { ESP_LOGCONFIG(TAG, "Xiaomi LYWSD03MMC"); diff --git a/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.cpp b/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.cpp index 035ac8c906..39bcd9df03 100644 --- a/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.cpp +++ b/esphome/components/xiaomi_lywsdcgq/xiaomi_lywsdcgq.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace xiaomi_lywsdcgq { -static const char *TAG = "xiaomi_lywsdcgq"; +static const char *const TAG = "xiaomi_lywsdcgq"; void XiaomiLYWSDCGQ::dump_config() { ESP_LOGCONFIG(TAG, "Xiaomi LYWSDCGQ"); diff --git a/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp index a41df3e6a1..e93a4b91ae 100644 --- a/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp +++ b/esphome/components/xiaomi_mhoc401/xiaomi_mhoc401.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace xiaomi_mhoc401 { -static const char *TAG = "xiaomi_mhoc401"; +static const char *const TAG = "xiaomi_mhoc401"; void XiaomiMHOC401::dump_config() { ESP_LOGCONFIG(TAG, "Xiaomi MHOC401"); diff --git a/esphome/components/xiaomi_miflora/sensor.py b/esphome/components/xiaomi_miflora/sensor.py deleted file mode 100644 index 0a0b3ff63f..0000000000 --- a/esphome/components/xiaomi_miflora/sensor.py +++ /dev/null @@ -1,3 +0,0 @@ -import esphome.config_validation as cv - -CONFIG_SCHEMA = cv.invalid("This sensor has been renamed to xiaomi_hhccjcy01") diff --git a/esphome/components/xiaomi_mijia/sensor.py b/esphome/components/xiaomi_mijia/sensor.py deleted file mode 100644 index 597d8d1bce..0000000000 --- a/esphome/components/xiaomi_mijia/sensor.py +++ /dev/null @@ -1,3 +0,0 @@ -import esphome.config_validation as cv - -CONFIG_SCHEMA = cv.invalid("This sensor has been renamed to xiaomi_lywsdcgq") diff --git a/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp b/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp index 441bca6270..78464da6e3 100644 --- a/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp +++ b/esphome/components/xiaomi_miscale/xiaomi_miscale.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace xiaomi_miscale { -static const char *TAG = "xiaomi_miscale"; +static const char *const TAG = "xiaomi_miscale"; void XiaomiMiscale::dump_config() { ESP_LOGCONFIG(TAG, "Xiaomi Miscale"); diff --git a/esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp b/esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp index 2bc4656cb9..cc63e46573 100644 --- a/esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp +++ b/esphome/components/xiaomi_miscale2/xiaomi_miscale2.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace xiaomi_miscale2 { -static const char *TAG = "xiaomi_miscale2"; +static const char *const TAG = "xiaomi_miscale2"; void XiaomiMiscale2::dump_config() { ESP_LOGCONFIG(TAG, "Xiaomi Miscale2"); diff --git a/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp b/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp index c7a2a75348..739543ddb6 100644 --- a/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp +++ b/esphome/components/xiaomi_mjyd02yla/xiaomi_mjyd02yla.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace xiaomi_mjyd02yla { -static const char *TAG = "xiaomi_mjyd02yla"; +static const char *const TAG = "xiaomi_mjyd02yla"; void XiaomiMJYD02YLA::dump_config() { ESP_LOGCONFIG(TAG, "Xiaomi MJYD02YL-A"); diff --git a/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.cpp b/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.cpp index 45337f330e..c1eff2e066 100644 --- a/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.cpp +++ b/esphome/components/xiaomi_mue4094rt/xiaomi_mue4094rt.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace xiaomi_mue4094rt { -static const char *TAG = "xiaomi_mue4094rt"; +static const char *const TAG = "xiaomi_mue4094rt"; void XiaomiMUE4094RT::dump_config() { ESP_LOGCONFIG(TAG, "Xiaomi MUE4094RT"); diff --git a/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.cpp b/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.cpp index 6ecdf8f4f4..d6e2ebe5b2 100644 --- a/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.cpp +++ b/esphome/components/xiaomi_wx08zm/xiaomi_wx08zm.cpp @@ -6,7 +6,7 @@ namespace esphome { namespace xiaomi_wx08zm { -static const char *TAG = "xiaomi_wx08zm"; +static const char *const TAG = "xiaomi_wx08zm"; void XiaomiWX08ZM::dump_config() { ESP_LOGCONFIG(TAG, "Xiaomi WX08ZM"); diff --git a/esphome/components/xpt2046/xpt2046.cpp b/esphome/components/xpt2046/xpt2046.cpp index 3e22e82859..aaadeea52e 100644 --- a/esphome/components/xpt2046/xpt2046.cpp +++ b/esphome/components/xpt2046/xpt2046.cpp @@ -7,7 +7,7 @@ namespace esphome { namespace xpt2046 { -static const char *TAG = "xpt2046"; +static const char *const TAG = "xpt2046"; void XPT2046Component::setup() { if (this->irq_pin_ != nullptr) { @@ -36,7 +36,7 @@ void XPT2046Component::loop() { void XPT2046Component::update() { int16_t data[6]; bool touch = false; - unsigned long now = millis(); + uint32_t now = millis(); this->z_raw = 0; diff --git a/esphome/components/xpt2046/xpt2046.h b/esphome/components/xpt2046/xpt2046.h index 7fd80c3228..e7270f7d7d 100644 --- a/esphome/components/xpt2046/xpt2046.h +++ b/esphome/components/xpt2046/xpt2046.h @@ -111,7 +111,7 @@ class XPT2046Component : public PollingComponent, bool swap_x_y_; uint32_t report_millis_; - unsigned long last_pos_ms_{0}; + uint32_t last_pos_ms_{0}; GPIOPin *irq_pin_{nullptr}; bool last_irq_{true}; diff --git a/esphome/components/yashima/yashima.cpp b/esphome/components/yashima/yashima.cpp index 1505f4ead2..8d588127b0 100644 --- a/esphome/components/yashima/yashima.cpp +++ b/esphome/components/yashima/yashima.cpp @@ -4,7 +4,7 @@ namespace esphome { namespace yashima { -static const char *TAG = "yashima.climate"; +static const char *const TAG = "yashima.climate"; const uint16_t YASHIMA_STATE_LENGTH = 9; const uint16_t YASHIMA_BITS = YASHIMA_STATE_LENGTH * 8; diff --git a/esphome/components/zyaura/zyaura.cpp b/esphome/components/zyaura/zyaura.cpp index 3b1a2a5069..989791f098 100644 --- a/esphome/components/zyaura/zyaura.cpp +++ b/esphome/components/zyaura/zyaura.cpp @@ -4,9 +4,9 @@ namespace esphome { namespace zyaura { -static const char *TAG = "zyaura"; +static const char *const TAG = "zyaura"; -bool ICACHE_RAM_ATTR ZaDataProcessor::decode(unsigned long ms, bool data) { +bool ICACHE_RAM_ATTR ZaDataProcessor::decode(uint32_t ms, bool data) { // check if a new message has started, based on time since previous bit if ((ms - this->prev_ms_) > ZA_MAX_MS) { this->num_bits_ = 0; diff --git a/esphome/components/zyaura/zyaura.h b/esphome/components/zyaura/zyaura.h index fd26947e28..eed0c55c35 100644 --- a/esphome/components/zyaura/zyaura.h +++ b/esphome/components/zyaura/zyaura.h @@ -31,13 +31,13 @@ struct ZaMessage { class ZaDataProcessor { public: - bool decode(unsigned long ms, bool data); + bool decode(uint32_t ms, bool data); ZaMessage *message = new ZaMessage; protected: uint8_t buffer_[ZA_MSG_LEN]; int num_bits_ = 0; - unsigned long prev_ms_; + uint32_t prev_ms_; }; class ZaSensorStore { diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 7292cc3af5..aad147dbc9 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1018,9 +1018,11 @@ def requires_component(comp): uint8_t = int_range(min=0, max=255) uint16_t = int_range(min=0, max=65535) uint32_t = int_range(min=0, max=4294967295) +uint64_t = int_range(min=0, max=18446744073709551615) hex_uint8_t = hex_int_range(min=0, max=255) hex_uint16_t = hex_int_range(min=0, max=65535) hex_uint32_t = hex_int_range(min=0, max=4294967295) +hex_uint64_t = hex_int_range(min=0, max=18446744073709551615) i2c_address = hex_uint8_t diff --git a/esphome/const.py b/esphome/const.py index 02d0491cfe..48790cf16a 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -1,10 +1,6 @@ """Constants used by esphome.""" -MAJOR_VERSION = 1 -MINOR_VERSION = 19 -PATCH_VERSION = "4" -__short_version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}" -__version__ = f"{__short_version__}.{PATCH_VERSION}" +__version__ = "1.20.0" ESP_PLATFORM_ESP32 = "ESP32" ESP_PLATFORM_ESP8266 = "ESP8266" @@ -28,6 +24,7 @@ ARDUINO_VERSION_ESP32 = { # See also https://github.com/platformio/platform-espressif8266/releases ARDUINO_VERSION_ESP8266 = { "dev": "https://github.com/platformio/platform-espressif8266.git", + "3.0.0": "platformio/espressif8266@3.0.0", "2.7.4": "platformio/espressif8266@2.6.2", "2.7.3": "platformio/espressif8266@2.6.1", "2.7.2": "platformio/espressif8266@2.6.0", @@ -76,6 +73,7 @@ CONF_AVAILABILITY = "availability" CONF_AWAY = "away" CONF_AWAY_CONFIG = "away_config" CONF_BACKLIGHT_PIN = "backlight_pin" +CONF_BASELINE = "baseline" CONF_BATTERY_LEVEL = "battery_level" CONF_BATTERY_VOLTAGE = "battery_voltage" CONF_BAUD_RATE = "baud_rate" @@ -120,6 +118,7 @@ CONF_CODE = "code" CONF_COLD_WHITE = "cold_white" CONF_COLD_WHITE_COLOR_TEMPERATURE = "cold_white_color_temperature" CONF_COLOR = "color" +CONF_COLOR_BRIGHTNESS = "color_brightness" CONF_COLOR_CORRECT = "color_correct" CONF_COLOR_TEMPERATURE = "color_temperature" CONF_COLORS = "colors" @@ -187,6 +186,7 @@ CONF_DUMP = "dump" CONF_DURATION = "duration" CONF_EAP = "eap" CONF_ECHO_PIN = "echo_pin" +CONF_ECO2 = "eco2" CONF_EFFECT = "effect" CONF_EFFECTS = "effects" CONF_ELSE = "else" @@ -297,6 +297,7 @@ CONF_KEY = "key" CONF_LAMBDA = "lambda" CONF_LAST_CONFIDENCE = "last_confidence" CONF_LAST_FINGER_ID = "last_finger_id" +CONF_LAST_RESET_TYPE = "last_reset_type" CONF_LATITUDE = "latitude" CONF_LENGTH = "length" CONF_LEVEL = "level" @@ -497,7 +498,6 @@ CONF_ROTATION = "rotation" CONF_RS_PIN = "rs_pin" CONF_RTD_NOMINAL_RESISTANCE = "rtd_nominal_resistance" CONF_RTD_WIRES = "rtd_wires" -CONF_RUN_CYCLES = "run_cycles" CONF_RUN_DURATION = "run_duration" CONF_RW_PIN = "rw_pin" CONF_RX_BUFFER_SIZE = "rx_buffer_size" @@ -551,6 +551,7 @@ CONF_STATE_CLASS = "state_class" CONF_STATE_TOPIC = "state_topic" CONF_STATIC_IP = "static_ip" CONF_STATUS = "status" +CONF_STEP = "step" CONF_STEP_MODE = "step_mode" CONF_STEP_PIN = "step_pin" CONF_STOP = "stop" @@ -785,3 +786,10 @@ STATE_CLASS_NONE = "" # The state represents a measurement in present time STATE_CLASS_MEASUREMENT = "measurement" + +# This sensor does not support resetting. ie, it is not accumulative +LAST_RESET_TYPE_NONE = "" +# This sensor is expected to never reset its value +LAST_RESET_TYPE_NEVER = "never" +# This sensor may reset and Home Assistant will watch for this +LAST_RESET_TYPE_AUTO = "auto" diff --git a/esphome/core/application.cpp b/esphome/core/application.cpp index 99726b6248..17a2725de5 100644 --- a/esphome/core/application.cpp +++ b/esphome/core/application.cpp @@ -9,7 +9,7 @@ namespace esphome { -static const char *TAG = "app"; +static const char *const TAG = "app"; void Application::register_component_(Component *comp) { if (comp == nullptr) { @@ -161,6 +161,6 @@ void Application::calculate_looping_components_() { } } -Application App; +Application App; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace esphome diff --git a/esphome/core/application.h b/esphome/core/application.h index 97c99bd4f9..e065552a74 100644 --- a/esphome/core/application.h +++ b/esphome/core/application.h @@ -32,6 +32,9 @@ #ifdef USE_COVER #include "esphome/components/cover/cover.h" #endif +#ifdef USE_NUMBER +#include "esphome/components/number/number.h" +#endif namespace esphome { @@ -82,6 +85,10 @@ class Application { void register_light(light::LightState *light) { this->lights_.push_back(light); } #endif +#ifdef USE_NUMBER + void register_number(number::Number *number) { this->numbers_.push_back(number); } +#endif + /// Register the component in this Application instance. template C *register_component(C *c) { static_assert(std::is_base_of::value, "Only Component subclasses can be registered"); @@ -208,6 +215,15 @@ class Application { return nullptr; } #endif +#ifdef USE_NUMBER + const std::vector &get_numbers() { return this->numbers_; } + number::Number *get_number_by_key(uint32_t key, bool include_internal = false) { + for (auto *obj : this->numbers_) + if (obj->get_object_id_hash() == key && (include_internal || !obj->is_internal())) + return obj; + return nullptr; + } +#endif Scheduler scheduler; @@ -245,6 +261,9 @@ class Application { #ifdef USE_LIGHT std::vector lights_{}; #endif +#ifdef USE_NUMBER + std::vector numbers_{}; +#endif std::string name_; std::string compilation_time_; @@ -256,6 +275,6 @@ class Application { }; /// Global storage of Application pointer - only one Application can exist. -extern Application App; +extern Application App; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace esphome diff --git a/esphome/core/color.h b/esphome/core/color.h index d2f225dc3a..6e8c769d10 100644 --- a/esphome/core/color.h +++ b/esphome/core/color.h @@ -44,7 +44,7 @@ struct Color { w((colorcode >> 24) & 0xFF) {} inline bool is_on() ALWAYS_INLINE { return this->raw_32 != 0; } - inline Color &operator=(const Color &rhs) ALWAYS_INLINE { + inline Color &operator=(const Color &rhs) ALWAYS_INLINE { // NOLINT this->r = rhs.r; this->g = rhs.g; this->b = rhs.b; diff --git a/esphome/core/component.cpp b/esphome/core/component.cpp index c571e5e23f..09c91fbb0c 100644 --- a/esphome/core/component.cpp +++ b/esphome/core/component.cpp @@ -1,12 +1,14 @@ #include "esphome/core/component.h" -#include "esphome/core/helpers.h" -#include "esphome/core/esphal.h" -#include "esphome/core/log.h" + #include "esphome/core/application.h" +#include "esphome/core/esphal.h" +#include "esphome/core/helpers.h" +#include "esphome/core/log.h" +#include namespace esphome { -static const char *TAG = "component"; +static const char *const TAG = "component"; namespace setup_priority { @@ -34,7 +36,7 @@ const uint32_t STATUS_LED_OK = 0x0000; const uint32_t STATUS_LED_WARNING = 0x0100; const uint32_t STATUS_LED_ERROR = 0x0200; -uint32_t global_state = 0; +uint32_t global_state = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) float Component::get_loop_priority() const { return 0.0f; } @@ -173,7 +175,7 @@ void Nameable::set_name(const std::string &name) { this->name_ = name; this->calc_object_id_(); } -Nameable::Nameable(const std::string &name) : name_(name) { this->calc_object_id_(); } +Nameable::Nameable(std::string name) : name_(std::move(name)) { this->calc_object_id_(); } const std::string &Nameable::get_object_id() { return this->object_id_; } bool Nameable::is_internal() const { return this->internal_; } diff --git a/esphome/core/component.h b/esphome/core/component.h index ad3619386b..001620fe4a 100644 --- a/esphome/core/component.h +++ b/esphome/core/component.h @@ -246,7 +246,7 @@ class PollingComponent : public Component { class Nameable { public: Nameable() : Nameable("") {} - explicit Nameable(const std::string &name); + explicit Nameable(std::string name); const std::string &get_name() const; void set_name(const std::string &name); /// Get the sanitized name of this nameable as an ID. Caching it internally. diff --git a/esphome/core/config.py b/esphome/core/config.py index fd4b7088cc..9475225f4d 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -77,7 +77,7 @@ PLATFORMIO_ESP8266_LUT = { # recommended version as otherwise a bunch of devices could be bricked # * The docker images need to be updated to ship the new recommended version, in order not # to DDoS platformio servers. - # Update this file: https://github.com/esphome/esphome-docker-base/blob/master/platformio.ini + # Update this file: https://github.com/esphome/esphome-docker-base/blob/main/platformio.ini "RECOMMENDED": ARDUINO_VERSION_ESP8266["2.7.4"], "LATEST": "espressif8266", "DEV": ARDUINO_VERSION_ESP8266["dev"], @@ -201,11 +201,6 @@ CONFIG_SCHEMA = cv.Schema( cv.Required(CONF_VERSION): cv.string_strict, } ), - cv.Optional("esphome_core_version"): cv.invalid( - "The esphome_core_version option has been " - "removed in 1.13 - the esphome core source " - "files are now bundled with ESPHome." - ), } ) diff --git a/esphome/core/controller.cpp b/esphome/core/controller.cpp index bd68d777ff..305fe93532 100644 --- a/esphome/core/controller.cpp +++ b/esphome/core/controller.cpp @@ -44,7 +44,7 @@ void Controller::setup_controller() { #ifdef USE_TEXT_SENSOR for (auto *obj : App.get_text_sensors()) { if (!obj->is_internal()) - obj->add_on_state_callback([this, obj](std::string state) { this->on_text_sensor_update(obj, state); }); + obj->add_on_state_callback([this, obj](const std::string &state) { this->on_text_sensor_update(obj, state); }); } #endif #ifdef USE_CLIMATE @@ -53,6 +53,12 @@ void Controller::setup_controller() { obj->add_on_state_callback([this, obj]() { this->on_climate_update(obj); }); } #endif +#ifdef USE_NUMBER + for (auto *obj : App.get_numbers()) { + if (!obj->is_internal()) + obj->add_on_state_callback([this, obj](float state) { this->on_number_update(obj, state); }); + } +#endif } } // namespace esphome diff --git a/esphome/core/controller.h b/esphome/core/controller.h index fa7d1f2ef0..746658075f 100644 --- a/esphome/core/controller.h +++ b/esphome/core/controller.h @@ -25,6 +25,9 @@ #ifdef USE_CLIMATE #include "esphome/components/climate/climate.h" #endif +#ifdef USE_NUMBER +#include "esphome/components/number/number.h" +#endif namespace esphome { @@ -50,11 +53,14 @@ class Controller { virtual void on_cover_update(cover::Cover *obj){}; #endif #ifdef USE_TEXT_SENSOR - virtual void on_text_sensor_update(text_sensor::TextSensor *obj, std::string state){}; + virtual void on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state){}; #endif #ifdef USE_CLIMATE virtual void on_climate_update(climate::Climate *obj){}; #endif +#ifdef USE_NUMBER + virtual void on_number_update(number::Number *obj, float state){}; +#endif }; } // namespace esphome diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 90562510b9..cac03fc703 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -13,6 +13,7 @@ #define USE_COVER #define USE_LIGHT #define USE_CLIMATE +#define USE_NUMBER #define USE_MQTT #define USE_POWER_SUPPLY #define USE_HOMEASSISTANT_TIME diff --git a/esphome/core/esphal.cpp b/esphome/core/esphal.cpp index 30a1e9c49f..d7adc8dbf1 100644 --- a/esphome/core/esphal.cpp +++ b/esphome/core/esphal.cpp @@ -18,7 +18,7 @@ void ICACHE_RAM_ATTR __detachInterrupt(uint8_t pin); // NOLINT namespace esphome { -static const char *TAG = "esphal"; +static const char *const TAG = "esphal"; GPIOPin::GPIOPin(uint8_t pin, uint8_t mode, bool inverted) : pin_(pin), diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index db26e62781..a6cf8b779c 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -13,7 +13,7 @@ namespace esphome { -static const char *TAG = "helpers"; +static const char *const TAG = "helpers"; std::string get_mac_address() { char tmp[20]; @@ -55,7 +55,7 @@ double random_double() { return random_uint32() / double(UINT32_MAX); } float random_float() { return float(random_double()); } -static uint32_t fast_random_seed = 0; +static uint32_t fast_random_seed = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) void fast_random_set_seed(uint32_t seed) { fast_random_seed = seed; } uint32_t fast_random_32() { @@ -105,7 +105,7 @@ std::string truncate_string(const std::string &s, size_t length) { } std::string value_accuracy_to_string(float value, int8_t accuracy_decimals) { - auto multiplier = float(pow10(accuracy_decimals)); + auto multiplier = float(powf(10.0f, accuracy_decimals)); float value_rounded = roundf(value * multiplier) / multiplier; char tmp[32]; // should be enough, but we should maybe improve this at some point. dtostrf(value_rounded, 0, uint8_t(std::max(0, int(accuracy_decimals))), tmp); @@ -123,8 +123,8 @@ std::string uint32_to_string(uint32_t num) { snprintf(buffer, sizeof(buffer), "%04X%04X", address16[1], address16[0]); return std::string(buffer); } -static char *global_json_build_buffer = nullptr; -static size_t global_json_build_buffer_size = 0; +static char *global_json_build_buffer = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +static size_t global_json_build_buffer_size = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) void reserve_global_json_build_buffer(size_t required_size) { if (global_json_build_buffer_size == 0 || global_json_build_buffer_size < required_size) { @@ -154,7 +154,7 @@ ParseOnOffState parse_on_off(const char *str, const char *on, const char *off) { return PARSE_NONE; } -const char *HOSTNAME_CHARACTER_ALLOWLIST = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"; +const char *const HOSTNAME_CHARACTER_ALLOWLIST = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"; uint8_t crc8(uint8_t *data, uint8_t len) { uint8_t crc = 0; @@ -201,27 +201,27 @@ std::string to_string(int val) { sprintf(buf, "%d", val); return buf; } -std::string to_string(long val) { +std::string to_string(long val) { // NOLINT char buf[64]; sprintf(buf, "%ld", val); return buf; } -std::string to_string(long long val) { +std::string to_string(long long val) { // NOLINT char buf[64]; sprintf(buf, "%lld", val); return buf; } -std::string to_string(unsigned val) { +std::string to_string(unsigned val) { // NOLINT char buf[64]; sprintf(buf, "%u", val); return buf; } -std::string to_string(unsigned long val) { +std::string to_string(unsigned long val) { // NOLINT char buf[64]; sprintf(buf, "%lu", val); return buf; } -std::string to_string(unsigned long long val) { +std::string to_string(unsigned long long val) { // NOLINT char buf[64]; sprintf(buf, "%llu", val); return buf; @@ -271,7 +271,7 @@ template uint32_t reverse_bits(uint32_t x) { return uint32_t(reverse_bits_16(x & 0xFFFF) << 16) | uint32_t(reverse_bits_16(x >> 16)); } -static int high_freq_num_requests = 0; +static int high_freq_num_requests = 0; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) void HighFrequencyLoopRequester::start() { if (this->started_) @@ -287,13 +287,16 @@ void HighFrequencyLoopRequester::stop() { } bool HighFrequencyLoopRequester::is_high_frequency() { return high_freq_num_requests > 0; } -float clamp(float val, float min, float max) { +template T clamp(const T val, const T min, const T max) { if (val < min) return min; if (val > max) return max; return val; } +template float clamp(float, float, float); +template int clamp(int, int, int); + float lerp(float completion, float start, float end) { return start + (end - start) * completion; } bool str_startswith(const std::string &full, const std::string &start) { return full.rfind(start, 0) == 0; } diff --git a/esphome/core/helpers.h b/esphome/core/helpers.h index d49415ca6b..808f96d4b8 100644 --- a/esphome/core/helpers.h +++ b/esphome/core/helpers.h @@ -28,7 +28,7 @@ namespace esphome { /// The characters that are allowed in a hostname. -extern const char *HOSTNAME_CHARACTER_ALLOWLIST; +extern const char *const HOSTNAME_CHARACTER_ALLOWLIST; /// Gets the MAC address as a string, this can be used as way to identify this ESP. std::string get_mac_address(); @@ -37,11 +37,11 @@ std::string get_mac_address_pretty(); std::string to_string(const std::string &val); std::string to_string(int val); -std::string to_string(long val); -std::string to_string(long long val); -std::string to_string(unsigned val); -std::string to_string(unsigned long val); -std::string to_string(unsigned long long val); +std::string to_string(long val); // NOLINT +std::string to_string(long long val); // NOLINT +std::string to_string(unsigned val); // NOLINT +std::string to_string(unsigned long val); // NOLINT +std::string to_string(unsigned long long val); // NOLINT std::string to_string(float val); std::string to_string(double val); std::string to_string(long double val); @@ -80,7 +80,7 @@ class HighFrequencyLoopRequester { * @param max The maximum value. * @return val clamped in between min and max. */ -float clamp(float val, float min, float max); +template T clamp(T val, T min, T max); /** Linearly interpolate between end start and end by completion. * diff --git a/esphome/core/optional.h b/esphome/core/optional.h index f6b050f5f8..5b96781e63 100644 --- a/esphome/core/optional.h +++ b/esphome/core/optional.h @@ -16,6 +16,8 @@ // // Modified by Otto Winter on 18.05.18 +#include + namespace esphome { // type for nullopt @@ -42,7 +44,7 @@ template class optional { // NOLINT optional(nullopt_t) {} - optional(T const &arg) : has_value_(true), value_(arg) {} + optional(T const &arg) : has_value_(true), value_(arg) {} // NOLINT template optional(optional const &other) : has_value_(other.has_value()), value_(other.value()) {} diff --git a/esphome/core/preferences.cpp b/esphome/core/preferences.cpp index 2d58fc9efb..68030a2d59 100644 --- a/esphome/core/preferences.cpp +++ b/esphome/core/preferences.cpp @@ -15,7 +15,7 @@ extern "C" { namespace esphome { -static const char *TAG = "preferences"; +static const char *const TAG = "preferences"; ESPPreferenceObject::ESPPreferenceObject() : offset_(0), length_words_(0), type_(0), data_(nullptr) {} ESPPreferenceObject::ESPPreferenceObject(size_t offset, size_t length, uint32_t type) @@ -73,7 +73,7 @@ static inline bool esp_rtc_user_mem_read(uint32_t index, uint32_t *dest) { return true; } -static bool esp8266_flash_dirty = false; +static bool esp8266_flash_dirty = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) static inline bool esp_rtc_user_mem_write(uint32_t index, uint32_t value) { if (index >= ESP_RTC_USER_MEM_SIZE_WORDS) { @@ -88,7 +88,7 @@ static inline bool esp_rtc_user_mem_write(uint32_t index, uint32_t value) { return true; } -extern "C" uint32_t _SPIFFS_end; +extern "C" uint32_t _SPIFFS_end; // NOLINT static const uint32_t get_esp8266_flash_sector() { union { @@ -303,6 +303,6 @@ uint32_t ESPPreferenceObject::calculate_crc_() const { } bool ESPPreferenceObject::is_initialized() const { return this->data_ != nullptr; } -ESPPreferences global_preferences; +ESPPreferences global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) } // namespace esphome diff --git a/esphome/core/preferences.h b/esphome/core/preferences.h index bfea4c2336..43d0f023e3 100644 --- a/esphome/core/preferences.h +++ b/esphome/core/preferences.h @@ -39,14 +39,14 @@ class ESPPreferenceObject { #ifdef ARDUINO_ARCH_ESP8266 #ifdef USE_ESP8266_PREFERENCES_FLASH -static bool DEFAULT_IN_FLASH = true; +static const bool DEFAULT_IN_FLASH = true; #else -static bool DEFAULT_IN_FLASH = false; +static const bool DEFAULT_IN_FLASH = false; #endif #endif #ifdef ARDUINO_ARCH_ESP32 -static bool DEFAULT_IN_FLASH = true; +static const bool DEFAULT_IN_FLASH = true; #endif class ESPPreferences { @@ -83,7 +83,7 @@ class ESPPreferences { #endif }; -extern ESPPreferences global_preferences; +extern ESPPreferences global_preferences; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) template ESPPreferenceObject ESPPreferences::make_preference(uint32_t type, bool in_flash) { return this->make_preference((sizeof(T) + 3) / 4, type, in_flash); diff --git a/esphome/core/scheduler.cpp b/esphome/core/scheduler.cpp index 9be39f4938..410c68052f 100644 --- a/esphome/core/scheduler.cpp +++ b/esphome/core/scheduler.cpp @@ -5,7 +5,7 @@ namespace esphome { -static const char *TAG = "scheduler"; +static const char *const TAG = "scheduler"; static const uint32_t SCHEDULER_DONT_RUN = 4294967295UL; static const uint32_t MAX_LOGICALLY_DELETED_ITEMS = 10; diff --git a/esphome/core/util.cpp b/esphome/core/util.cpp index 19b8ab89e9..67e575e1c5 100644 --- a/esphome/core/util.cpp +++ b/esphome/core/util.cpp @@ -66,7 +66,7 @@ bool mqtt_is_connected() { bool remote_is_connected() { return api_is_connected() || mqtt_is_connected(); } #if defined(ARDUINO_ARCH_ESP8266) && defined(USE_MDNS) -bool mdns_setup; +static bool mdns_setup; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) #endif #ifndef WEBSERVER_PORT @@ -80,7 +80,7 @@ void network_setup_mdns(IPAddress address, int interface) { // see https://github.com/esp8266/Arduino/issues/6114 if (interface == 1) return; - MDNS.begin(App.get_name().c_str(), address); + MDNS.begin(App.get_name().c_str(), std::move(address)); mdns_setup = true; #endif #ifdef ARDUINO_ARCH_ESP32 diff --git a/esphome/core/util.h b/esphome/core/util.h index 180c7aaefa..2d58eff893 100644 --- a/esphome/core/util.h +++ b/esphome/core/util.h @@ -5,11 +5,6 @@ namespace esphome { -/// Macro for IDF version comparision -#ifndef ESP_IDF_VERSION_VAL -#define ESP_IDF_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch)) -#endif - /// Return whether the node is connected to the network (through wifi, eth, ...) bool network_is_connected(); /// Get the active network hostname diff --git a/esphome/cpp_types.py b/esphome/cpp_types.py index 3036249a03..ee606ec7b3 100644 --- a/esphome/cpp_types.py +++ b/esphome/cpp_types.py @@ -13,6 +13,7 @@ std_vector = std_ns.class_("vector") uint8 = global_ns.namespace("uint8_t") uint16 = global_ns.namespace("uint16_t") uint32 = global_ns.namespace("uint32_t") +uint64 = global_ns.namespace("uint64_t") int32 = global_ns.namespace("int32_t") const_char_ptr = global_ns.namespace("const char *") NAN = global_ns.namespace("NAN") diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index 00b12199c0..66f72bfc00 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -782,10 +782,9 @@ def make_app(debug=get_bool_env(ENV_DEV)): class StaticFileHandler(tornado.web.StaticFileHandler): def set_extra_headers(self, path): - if debug: - self.set_header( - "Cache-Control", "no-store, no-cache, must-revalidate, max-age=0" - ) + self.set_header( + "Cache-Control", "no-store, no-cache, must-revalidate, max-age=0" + ) app_settings = { "debug": debug, diff --git a/esphome/pins.py b/esphome/pins.py index fef77f3946..6356ae9bd0 100644 --- a/esphome/pins.py +++ b/esphome/pins.py @@ -538,7 +538,7 @@ ESP32_BOARD_PINS = { "iotbusio": {}, "iotbusproteus": {}, "lolin32": {"LED": 5}, - "lolin32-lite": {"LED": 22}, + "lolin32_lite": {"LED": 22}, "lolin_d32": {"LED": 5, "_VBAT": 35}, "lolin_d32_pro": {"LED": 5, "_VBAT": 35}, "lopy": { diff --git a/requirements.txt b/requirements.txt index cc9059f0d7..ab54828194 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ PyYAML==5.4.1 paho-mqtt==1.5.1 colorama==0.4.4 tornado==6.1 -protobuf==3.17.0 +protobuf==3.17.3 tzlocal==2.1 pytz==2021.1 pyserial==3.5 @@ -11,4 +11,4 @@ ifaddr==0.1.7 platformio==5.1.1 esptool==2.8 click==7.1.2 -esphome-dashboard==20210623.0 +esphome-dashboard==20210719.0 diff --git a/requirements_test.txt b/requirements_test.txt index 15593a8e12..f9566d5adc 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,13 +1,13 @@ pylint==2.8.2 flake8==3.9.2 -black==21.5b1 +black==21.6b0 pexpect==4.8.0 pre-commit # Unit tests pytest==6.2.4 -pytest-cov==2.11.1 -pytest-mock==3.5.1 -pytest-asyncio==0.14.0 +pytest-cov==2.12.1 +pytest-mock==3.6.1 +pytest-asyncio==0.15.1 asyncmock==0.4.2 -hypothesis==5.21.0 +hypothesis==5.49.0 diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index 05ad94a69d..56c3e8ccc8 100755 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -822,7 +822,7 @@ cpp += """\ namespace esphome { namespace api { -static const char *TAG = "api.service"; +static const char *const TAG = "api.service"; """ diff --git a/script/bump-docker-base-version.py b/script/bump-docker-base-version.py deleted file mode 100755 index 765a330ce4..0000000000 --- a/script/bump-docker-base-version.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import re -import sys - - -def sub(path, pattern, repl, expected_count=1): - with open(path) as fh: - content = fh.read() - content, count = re.subn(pattern, repl, content, flags=re.MULTILINE) - if expected_count is not None: - assert count == expected_count, f"Pattern {pattern} replacement failed!" - with open(path, "wt") as fh: - fh.write(content) - - -def write_version(version: str): - for p in [ - ".github/workflows/ci-docker.yml", - ".github/workflows/release-dev.yml", - ".github/workflows/release.yml", - ]: - sub(p, r'base_version=".*"', f'base_version="{version}"') - - sub( - "docker/Dockerfile", - r"ARG BUILD_FROM=esphome/esphome-base-amd64:.*", - f"ARG BUILD_FROM=esphome/esphome-base-amd64:{version}", - ) - sub( - "docker/Dockerfile.dev", - r"FROM esphome/esphome-base-amd64:.*", - f"FROM esphome/esphome-base-amd64:{version}", - ) - sub( - "docker/Dockerfile.lint", - r"FROM esphome/esphome-lint-base:.*", - f"FROM esphome/esphome-lint-base:{version}", - ) - - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument("new_version", type=str) - args = parser.parse_args() - - version = args.new_version - print(f"Bumping to {version}") - write_version(version) - return 0 - - -if __name__ == "__main__": - sys.exit(main() or 0) diff --git a/script/bump-version.py b/script/bump-version.py index b7b048eb22..1f034344f9 100755 --- a/script/bump-version.py +++ b/script/bump-version.py @@ -50,16 +50,10 @@ def sub(path, pattern, repl, expected_count=1): def write_version(version: Version): - sub( - "esphome/const.py", r"^MAJOR_VERSION = \d+$", f"MAJOR_VERSION = {version.major}" - ) - sub( - "esphome/const.py", r"^MINOR_VERSION = \d+$", f"MINOR_VERSION = {version.minor}" - ) sub( "esphome/const.py", - r"^PATCH_VERSION = .*$", - f'PATCH_VERSION = "{version.full_patch}"', + r"^__version__ = .*$", + f'__version__ = "{version}"', ) diff --git a/script/ci-custom.py b/script/ci-custom.py index 4ec7c664a4..d79e5b5e2f 100755 --- a/script/ci-custom.py +++ b/script/ci-custom.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +from helpers import git_ls_files, filter_changed import codecs import collections import fnmatch @@ -12,7 +13,6 @@ import functools import argparse sys.path.append(os.path.dirname(__file__)) -from helpers import git_ls_files, filter_changed def find_all(a_str, sub): @@ -559,8 +559,10 @@ def lint_inclusive_language(fname, match): "esphome/components/display/display_buffer.h", "esphome/components/i2c/i2c.h", "esphome/components/mqtt/mqtt_component.h", + "esphome/components/number/number.h", "esphome/components/output/binary_output.h", "esphome/components/output/float_output.h", + "esphome/components/nextion/nextion_base.h", "esphome/components/sensor/sensor.h", "esphome/components/stepper/stepper.h", "esphome/components/switch/switch.h", diff --git a/script/devcontainer-post-create b/script/devcontainer-post-create new file mode 100755 index 0000000000..4b4dc5b6c9 --- /dev/null +++ b/script/devcontainer-post-create @@ -0,0 +1,18 @@ +#!/bin/bash + +set -e +# set -x + +mkdir -p config +script/setup + +cpp_json=.vscode/c_cpp_properties.json +if [ ! -f $cpp_json ]; then + echo "Initializing PlatformIO..." + pio init --ide vscode --silent + sed -i "/\\/workspaces\/esphome\/include/d" $cpp_json +else + echo "Cpp environment already configured. To reconfigure it you could run one the following commands:" + echo " pio init --ide vscode -e livingroom8266" + echo " pio init --ide vscode -e livingroom32" +fi diff --git a/script/helpers.py b/script/helpers.py index 5b1b7ba918..1a4402aa1d 100644 --- a/script/helpers.py +++ b/script/helpers.py @@ -40,7 +40,7 @@ def build_all_include(): def build_compile_commands(): gcc_flags_json = os.path.join(root_path, ".gcc-flags.json") if not os.path.isfile(gcc_flags_json): - print("Could not find {} file which is required for clang-tidy.") + print("Could not find {} file which is required for clang-tidy.".format(gcc_flags_json)) print( 'Please run "pio init --ide atom" in the root esphome folder to generate that file.' ) diff --git a/tests/test1.yaml b/tests/test1.yaml index 08e1d63534..f9fe7d106d 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -197,6 +197,24 @@ ota: port: 3286 reboot_timeout: 2min num_attempts: 5 + on_state_change: + then: + lambda: >- + ESP_LOGD("ota", "State %d", state); + on_begin: + then: + logger.log: "OTA begin" + on_progress: + then: + lambda: >- + ESP_LOGD("ota", "Got progress %f", x); + on_end: + then: + logger.log: "OTA end" + on_error: + then: + lambda: >- + ESP_LOGD("ota", "Got error code %d", x); logger: baud_rate: 0 @@ -514,6 +532,7 @@ sensor: voltage_divider: 2351 change_mode_every: 16 initial_mode: VOLTAGE + model: hlw8012 - platform: total_daily_energy power_id: hlw8012_power name: 'HLW8012 Total Daily Energy' @@ -1036,10 +1055,6 @@ binary_sensor: pin: GPIO27 threshold: 1000 id: btn_left - - platform: nextion - page_id: 0 - component_id: 2 - name: 'Nextion Component 2 Touch' - platform: template name: 'Garage Door Open' id: garage_door @@ -1525,6 +1540,26 @@ climate: name: Toshiba Climate - platform: hitachi_ac344 name: Hitachi Climate + - platform: midea_ac + visual: + min_temperature: 18 °C + max_temperature: 25 °C + temperature_step: 0.1 °C + name: 'Electrolux EACS' + beeper: true + outdoor_temperature: + name: 'Temp' + power_usage: + name: 'Power' + humidity_setpoint: + name: 'Hum' + - platform: anova + name: Anova cooker + ble_client_id: ble_blah + +midea_dongle: + uart_id: uart0 + strength_icon: true switch: - platform: gpio @@ -1843,11 +1878,6 @@ display: intensity: 3 lambda: |- it.print("1234"); - - platform: nextion - uart_id: uart0 - lambda: |- - it.set_component_value("gauge", 50); - it.set_component_text("textview", "Hello World!"); - platform: pcd8544 cs_pin: GPIO23 dc_pin: GPIO23 diff --git a/tests/test3.yaml b/tests/test3.yaml index af5398b604..acd975d794 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -269,6 +269,7 @@ wled: adalight: + sensor: - platform: apds9960 type: proximity @@ -534,6 +535,15 @@ sensor: export_reactive_energy: name: 'Export Reactive Energy' + - platform: nextion + id: testnumber + name: 'testnumber' + variable_name: testnumber + - platform: nextion + id: testwave + name: 'testwave' + component_id: 2 + wave_channel_id: 1 time: - platform: homeassistant @@ -605,7 +615,14 @@ binary_sensor: binary_sensors: - id: custom_binary_sensor name: Custom Binary Sensor - + - platform: nextion + page_id: 0 + component_id: 2 + name: 'Nextion Component 2 Touch' + - platform: nextion + id: r0_sensor + name: 'R0 Sensor' + component_name: page0.r0 globals: - id: my_global_string type: std::string @@ -653,6 +670,11 @@ text_sensor: text_sensors: - id: custom_text_sensor name: Custom Text Sensor + - platform: nextion + name: text0 + id: text0 + update_interval: 4s + component_name: text0 script: - id: my_script @@ -704,6 +726,10 @@ switch: switches: - id: custom_switch name: Custom Switch + - platform: nextion + id: r0 + name: 'R0 Switch' + component_name: page0.r0 custom_component: lambda: |- @@ -1086,6 +1112,16 @@ display: id: my_matrix lambda: |- it.printdigit("hello"); + - platform: nextion + uart_id: uart1 + tft_url: 'http://esphome.io/default35.tft' + update_interval: 5s + on_sleep: + then: + lambda: 'ESP_LOGD("display","Display went to sleep");' + on_wake: + then: + lambda: 'ESP_LOGD("display","Display woke up");' http_request: useragent: esphome/device @@ -1097,27 +1133,27 @@ fingerprint_grow: new_password: 0xA65B9840 on_finger_scan_matched: - homeassistant.event: - event: esphome.${devicename}_fingerprint_grow_finger_scan_matched + event: esphome.${device_name}_fingerprint_grow_finger_scan_matched data: finger_id: !lambda 'return finger_id;' confidence: !lambda 'return confidence;' on_finger_scan_unmatched: - homeassistant.event: - event: esphome.${devicename}_fingerprint_grow_finger_scan_unmatched + event: esphome.${device_name}_fingerprint_grow_finger_scan_unmatched on_enrollment_scan: - homeassistant.event: - event: esphome.${devicename}_fingerprint_grow_enrollment_scan + event: esphome.${device_name}_fingerprint_grow_enrollment_scan data: finger_id: !lambda 'return finger_id;' scan_num: !lambda 'return scan_num;' on_enrollment_done: - homeassistant.event: - event: esphome.${devicename}_fingerprint_grow_node_enrollment_done + event: esphome.${device_name}_fingerprint_grow_node_enrollment_done data: finger_id: !lambda 'return finger_id;' on_enrollment_failed: - homeassistant.event: - event: esphome.${devicename}_fingerprint_grow_enrollment_failed + event: esphome.${device_name}_fingerprint_grow_enrollment_failed data: finger_id: !lambda 'return finger_id;' uart_id: uart6 diff --git a/tests/test5.yaml b/tests/test5.yaml index ba047721e2..35225402a3 100644 --- a/tests/test5.yaml +++ b/tests/test5.yaml @@ -38,3 +38,20 @@ esp32_improv: authorizer: io0_button authorized_duration: 1min status_indicator: built_in_led + +number: + - platform: template + name: My template number + id: template_number_id + optimistic: true + on_value: + - logger.log: + format: "Number changed to %f" + args: ["x"] + set_action: + - logger.log: + format: "Template Number set to %f" + args: ["x"] + max_value: 100 + min_value: 0 + step: 5 diff --git a/tests/unit_tests/strategies.py b/tests/unit_tests/strategies.py index 4bc0482f5f..30768f9d56 100644 --- a/tests/unit_tests/strategies.py +++ b/tests/unit_tests/strategies.py @@ -4,7 +4,7 @@ import hypothesis.strategies._internal.core as st from hypothesis.strategies._internal.strategies import SearchStrategy -@st.defines_strategy_with_reusable_values +@st.defines_strategy(force_reusable_values=True) def mac_addr_strings(): # type: () -> SearchStrategy[Text] """A strategy for MAC address strings.