Merge pull request #1742 from esphome/bump-1.17.0

1.17.0
This commit is contained in:
Jesse Hills 2021-05-04 10:48:51 +12:00 committed by GitHub
commit 34487c9de4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
553 changed files with 27573 additions and 13992 deletions

View file

@ -1,13 +1,47 @@
## Description: # What does this implement/fix?
Quick description
**Related issue (if applicable):** fixes <link to issue> ## Types of changes
- [ ] Bugfix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Configuration change (this will require users to update their yaml configuration files to keep working)
**Related issue or feature (if applicable):** fixes <link to issue>
**Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs#<esphome-docs PR number goes here> **Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs#<esphome-docs PR number goes here>
# Test Environment
- [ ] ESP32
- [ ] ESP8266
- [ ] Windows
- [ ] Mac OS
- [ ] Linux
## Example entry for `config.yaml`:
<!--
Supplying a configuration snippet, makes it easier for a maintainer to test
your PR. Furthermore, for new integrations, it gives an impression of how
the configuration would look like.
Note: Remove this section if this PR does not have an example entry.
-->
```yaml
# Example config.yaml
```
# Explain your changes
Describe your changes here to communicate to the maintainers **why we should accept this pull request**.
Very important to fill if no issue linked
## Checklist: ## Checklist:
- [ ] The code change is tested and works locally. - [ ] The code change is tested and works locally.
- [ ] Tests have been added to verify that the new code works (under `tests/` folder). - [ ] Tests have been added to verify that the new code works (under `tests/` folder).
If user exposed functionality or configuration variables are added/changed: If user exposed functionality or configuration variables are added/changed:
- [ ] Documentation added/updated in [esphome-docs](https://github.com/esphome/esphome-docs). - [ ] Documentation added/updated in [esphome-docs](https://github.com/esphome/esphome-docs).

View file

@ -26,7 +26,7 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Set up env variables - name: Set up env variables
run: | run: |
base_version="2.6.0" base_version="3.0.0"
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
@ -45,7 +45,7 @@ jobs:
run: | run: |
docker pull "${BUILD_TO}:dev" || true docker pull "${BUILD_TO}:dev" || true
- name: Register QEMU binfmt - name: Register QEMU binfmt
run: docker run --rm --privileged multiarch/qemu-user-static:5.0.0-2 --reset -p yes run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes
- run: | - run: |
docker build \ docker build \
--build-arg "BUILD_FROM=${BUILD_FROM}" \ --build-arg "BUILD_FROM=${BUILD_FROM}" \

View file

@ -18,15 +18,6 @@ jobs:
container: esphome/esphome-lint:latest container: esphome/esphome-lint:latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
# Cache platformio intermediary files (like libraries etc)
# Note: platformio platform versions should be cached via the esphome-lint image
- name: Cache Platformio
uses: actions/cache@v1
with:
path: .pio
key: lint-cpp-pio-${{ hashFiles('platformio.ini') }}
restore-keys: |
lint-cpp-pio-
# Set up the pio project so that the cpp checks know how files are compiled # Set up the pio project so that the cpp checks know how files are compiled
# (build flags, libraries etc) # (build flags, libraries etc)
- name: Set up platformio environment - name: Set up platformio environment
@ -49,15 +40,6 @@ jobs:
split: [1, 2, 3, 4] split: [1, 2, 3, 4]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
# Cache platformio intermediary files (like libraries etc)
# Note: platformio platform versions should be cached via the esphome-lint image
- name: Cache Platformio
uses: actions/cache@v1
with:
path: .pio
key: lint-cpp-pio-${{ hashFiles('platformio.ini') }}
restore-keys: |
lint-cpp-pio-
# Set up the pio project so that the cpp checks know how files are compiled # Set up the pio project so that the cpp checks know how files are compiled
# (build flags, libraries etc) # (build flags, libraries etc)
- name: Set up platformio environment - name: Set up platformio environment

View file

@ -15,15 +15,6 @@ jobs:
container: esphome/esphome-lint:latest container: esphome/esphome-lint:latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
# Cache platformio intermediary files (like libraries etc)
# Note: platformio platform versions should be cached via the esphome-lint image
- name: Cache Platformio
uses: actions/cache@v1
with:
path: .pio
key: lint-cpp-pio-${{ hashFiles('platformio.ini') }}
restore-keys: |
lint-cpp-pio-
# Set up the pio project so that the cpp checks know how files are compiled # Set up the pio project so that the cpp checks know how files are compiled
# (build flags, libraries etc) # (build flags, libraries etc)
- name: Set up platformio environment - name: Set up platformio environment
@ -46,15 +37,6 @@ jobs:
split: [1, 2, 3, 4] split: [1, 2, 3, 4]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
# Cache platformio intermediary files (like libraries etc)
# Note: platformio platform versions should be cached via the esphome-lint image
- name: Cache Platformio
uses: actions/cache@v1
with:
path: .pio
key: lint-cpp-pio-${{ hashFiles('platformio.ini') }}
restore-keys: |
lint-cpp-pio-
# Set up the pio project so that the cpp checks know how files are compiled # Set up the pio project so that the cpp checks know how files are compiled
# (build flags, libraries etc) # (build flags, libraries etc)
- name: Set up platformio environment - name: Set up platformio environment
@ -192,7 +174,7 @@ jobs:
echo "TAG=${TAG}" >> $GITHUB_ENV echo "TAG=${TAG}" >> $GITHUB_ENV
- name: Set up env variables - name: Set up env variables
run: | run: |
base_version="2.6.0" base_version="3.0.0"
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
@ -211,7 +193,7 @@ jobs:
run: | run: |
docker pull "${BUILD_TO}:dev" || true docker pull "${BUILD_TO}:dev" || true
- name: Register QEMU binfmt - name: Register QEMU binfmt
run: docker run --rm --privileged multiarch/qemu-user-static:5.0.0-2 --reset -p yes run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes
- run: | - run: |
docker build \ docker build \
--build-arg "BUILD_FROM=${BUILD_FROM}" \ --build-arg "BUILD_FROM=${BUILD_FROM}" \

View file

@ -14,15 +14,6 @@ jobs:
container: esphome/esphome-lint:latest container: esphome/esphome-lint:latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
# Cache platformio intermediary files (like libraries etc)
# Note: platformio platform versions should be cached via the esphome-lint image
- name: Cache Platformio
uses: actions/cache@v1
with:
path: .pio
key: lint-cpp-pio-${{ hashFiles('platformio.ini') }}
restore-keys: |
lint-cpp-pio-
# Set up the pio project so that the cpp checks know how files are compiled # Set up the pio project so that the cpp checks know how files are compiled
# (build flags, libraries etc) # (build flags, libraries etc)
- name: Set up platformio environment - name: Set up platformio environment
@ -45,15 +36,6 @@ jobs:
split: [1, 2, 3, 4] split: [1, 2, 3, 4]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
# Cache platformio intermediary files (like libraries etc)
# Note: platformio platform versions should be cached via the esphome-lint image
- name: Cache Platformio
uses: actions/cache@v1
with:
path: .pio
key: lint-cpp-pio-${{ hashFiles('platformio.ini') }}
restore-keys: |
lint-cpp-pio-
# Set up the pio project so that the cpp checks know how files are compiled # Set up the pio project so that the cpp checks know how files are compiled
# (build flags, libraries etc) # (build flags, libraries etc)
- name: Set up platformio environment - name: Set up platformio environment
@ -212,7 +194,7 @@ jobs:
echo "TAG=${TAG}" >> $GITHUB_ENV echo "TAG=${TAG}" >> $GITHUB_ENV
- name: Set up env variables - name: Set up env variables
run: | run: |
base_version="2.6.0" base_version="3.0.0"
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
@ -239,7 +221,7 @@ jobs:
run: | run: |
docker pull "${BUILD_TO}:${CACHE_TAG}" || true docker pull "${BUILD_TO}:${CACHE_TAG}" || true
- name: Register QEMU binfmt - name: Register QEMU binfmt
run: docker run --rm --privileged multiarch/qemu-user-static:5.0.0-2 --reset -p yes run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes
- run: | - run: |
docker build \ docker build \
--build-arg "BUILD_FROM=${BUILD_FROM}" \ --build-arg "BUILD_FROM=${BUILD_FROM}" \

View file

@ -1,11 +1,27 @@
# See https://pre-commit.com for more information # See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks # See https://pre-commit.com/hooks.html for more hooks
repos: repos:
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/ambv/black
rev: v2.4.0 rev: 20.8b1
hooks: hooks:
- id: trailing-whitespace - id: black
- id: end-of-file-fixer args:
- id: check-yaml - --safe
- id: check-added-large-files - --quiet
- id: flake8 files: ^((esphome|script|tests)/.+)?[^/]+\.py$
- repo: https://gitlab.com/pycqa/flake8
rev: 3.8.4
hooks:
- id: flake8
additional_dependencies:
- flake8-docstrings==1.5.0
- pydocstyle==5.1.1
files: ^(esphome|tests)/.+\.py$
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.4.0
hooks:
- id: no-commit-to-branch
args:
- --branch=dev
- --branch=master
- --branch=beta

View file

@ -13,6 +13,7 @@ esphome/core/* @esphome/core
# Integrations # Integrations
esphome/components/ac_dimmer/* @glmnet esphome/components/ac_dimmer/* @glmnet
esphome/components/adc/* @esphome/core esphome/components/adc/* @esphome/core
esphome/components/addressable_light/* @justfalter
esphome/components/animation/* @syndlex esphome/components/animation/* @syndlex
esphome/components/api/* @OttoWinter esphome/components/api/* @OttoWinter
esphome/components/async_tcp/* @OttoWinter esphome/components/async_tcp/* @OttoWinter
@ -37,6 +38,7 @@ esphome/components/globals/* @esphome/core
esphome/components/gpio/* @esphome/core esphome/components/gpio/* @esphome/core
esphome/components/homeassistant/* @OttoWinter esphome/components/homeassistant/* @OttoWinter
esphome/components/i2c/* @esphome/core esphome/components/i2c/* @esphome/core
esphome/components/inkbird_ibsth1_mini/* @fkirill
esphome/components/inkplate6/* @jesserockz esphome/components/inkplate6/* @jesserockz
esphome/components/integration/* @OttoWinter esphome/components/integration/* @OttoWinter
esphome/components/interval/* @esphome/core esphome/components/interval/* @esphome/core
@ -44,10 +46,18 @@ esphome/components/json/* @OttoWinter
esphome/components/ledc/* @OttoWinter esphome/components/ledc/* @OttoWinter
esphome/components/light/* @esphome/core esphome/components/light/* @esphome/core
esphome/components/logger/* @esphome/core esphome/components/logger/* @esphome/core
esphome/components/mcp23s08/* @SenexCrenshaw esphome/components/max7219digit/* @rspaargaren
esphome/components/mcp23s17/* @SenexCrenshaw esphome/components/mcp23008/* @jesserockz
esphome/components/mcp23017/* @jesserockz
esphome/components/mcp23s08/* @SenexCrenshaw @jesserockz
esphome/components/mcp23s17/* @SenexCrenshaw @jesserockz
esphome/components/mcp23x08_base/* @jesserockz
esphome/components/mcp23x17_base/* @jesserockz
esphome/components/mcp23xxx_base/* @jesserockz
esphome/components/mcp2515/* @danielschramm @mvturnho esphome/components/mcp2515/* @danielschramm @mvturnho
esphome/components/mcp9808/* @k7hpn esphome/components/mcp9808/* @k7hpn
esphome/components/midea_ac/* @dudanov
esphome/components/midea_dongle/* @dudanov
esphome/components/network/* @esphome/core esphome/components/network/* @esphome/core
esphome/components/nfc/* @jesserockz esphome/components/nfc/* @jesserockz
esphome/components/ota/* @esphome/core esphome/components/ota/* @esphome/core
@ -57,6 +67,7 @@ esphome/components/pn532/* @OttoWinter @jesserockz
esphome/components/pn532_i2c/* @OttoWinter @jesserockz esphome/components/pn532_i2c/* @OttoWinter @jesserockz
esphome/components/pn532_spi/* @OttoWinter @jesserockz esphome/components/pn532_spi/* @OttoWinter @jesserockz
esphome/components/power_supply/* @esphome/core esphome/components/power_supply/* @esphome/core
esphome/components/pulse_meter/* @stevebaxter
esphome/components/rc522/* @glmnet esphome/components/rc522/* @glmnet
esphome/components/rc522_i2c/* @glmnet esphome/components/rc522_i2c/* @glmnet
esphome/components/rc522_spi/* @glmnet esphome/components/rc522_spi/* @glmnet

View file

@ -1,9 +1,11 @@
ARG BUILD_FROM=esphome/esphome-base-amd64:2.6.0 ARG BUILD_FROM=esphome/esphome-base-amd64:3.0.0
FROM ${BUILD_FROM} FROM ${BUILD_FROM}
# First install requirements to leverage caching when requirements don't change # First install requirements to leverage caching when requirements don't change
COPY requirements.txt / COPY requirements.txt docker/platformio_install_deps.py platformio.ini /
RUN pip3 install --no-cache-dir -r /requirements.txt RUN \
pip3 install --no-cache-dir -r /requirements.txt \
&& /platformio_install_deps.py /platformio.ini
# Then copy esphome and install # Then copy esphome and install
COPY . . COPY . .

View file

@ -1,4 +1,4 @@
FROM esphome/esphome-base-amd64:2.6.0 FROM esphome/esphome-base-amd64:3.0.0
COPY . . COPY . .

View file

@ -2,8 +2,10 @@ ARG BUILD_FROM
FROM ${BUILD_FROM} FROM ${BUILD_FROM}
# First install requirements to leverage caching when requirements don't change # First install requirements to leverage caching when requirements don't change
COPY requirements.txt / COPY requirements.txt docker/platformio_install_deps.py platformio.ini /
RUN pip3 install --no-cache-dir -r /requirements.txt RUN \
pip3 install --no-cache-dir -r /requirements.txt \
&& /platformio_install_deps.py /platformio.ini
# Copy root filesystem # Copy root filesystem
COPY docker/rootfs/ / COPY docker/rootfs/ /

View file

@ -1,7 +1,9 @@
FROM esphome/esphome-lint-base:2.6.0 FROM esphome/esphome-lint-base:3.0.0
COPY requirements.txt requirements_test.txt / COPY requirements.txt requirements_test.txt docker/platformio_install_deps.py platformio.ini /
RUN pip3 install --no-cache-dir -r /requirements.txt -r /requirements_test.txt RUN \
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_test.txt \
&& /platformio_install_deps.py /platformio.ini
VOLUME ["/esphome"] VOLUME ["/esphome"]
WORKDIR /esphome WORKDIR /esphome

View file

@ -0,0 +1,20 @@
#!/usr/bin/env python3
# This script is used in the docker containers to preinstall
# all platformio libraries in the global storage
import configparser
import re
import subprocess
import sys
config = configparser.ConfigParser()
config.read(sys.argv[1])
libs = []
for line in config['common']['lib_deps'].splitlines():
# Format: '1655@1.0.2 ; TinyGPSPlus (has name conflict)' (includes comment)
m = re.search(r'([a-zA-Z0-9-_/]+@[0-9\.]+)', line)
if m is None:
continue
libs.append(m.group(1))
subprocess.check_call(['platformio', 'lib', '-g', 'install', *libs])

View file

@ -8,21 +8,36 @@ from datetime import datetime
from esphome import const, writer, yaml_util from esphome import const, writer, yaml_util
import esphome.codegen as cg import esphome.codegen as cg
from esphome.config import iter_components, read_config, strip_default_ids from esphome.config import iter_components, read_config, strip_default_ids
from esphome.const import CONF_BAUD_RATE, CONF_BROKER, CONF_LOGGER, CONF_OTA, \ from esphome.const import (
CONF_PASSWORD, CONF_PORT, CONF_ESPHOME, CONF_PLATFORMIO_OPTIONS CONF_BAUD_RATE,
CONF_BROKER,
CONF_LOGGER,
CONF_OTA,
CONF_PASSWORD,
CONF_PORT,
CONF_ESPHOME,
CONF_PLATFORMIO_OPTIONS,
)
from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority
from esphome.helpers import color, indent from esphome.helpers import color, indent
from esphome.util import run_external_command, run_external_process, safe_print, list_yaml_files, \ from esphome.util import (
get_serial_ports run_external_command,
run_external_process,
safe_print,
list_yaml_files,
get_serial_ports,
)
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
def choose_prompt(options): def choose_prompt(options):
if not options: if not options:
raise EsphomeError("Found no valid options for upload/logging, please make sure relevant " raise EsphomeError(
"sections (ota, api, mqtt, ...) are in your configuration and/or the " "Found no valid options for upload/logging, please make sure relevant "
"device is plugged in.") "sections (ota, api, mqtt, ...) are in your configuration and/or the "
"device is plugged in."
)
if len(options) == 1: if len(options) == 1:
return options[0][1] return options[0][1]
@ -32,7 +47,7 @@ def choose_prompt(options):
safe_print(f" [{i+1}] {desc}") safe_print(f" [{i+1}] {desc}")
while True: while True:
opt = input('(number): ') opt = input("(number): ")
if opt in options: if opt in options:
opt = options.index(opt) opt = options.index(opt)
break break
@ -42,7 +57,7 @@ def choose_prompt(options):
raise ValueError raise ValueError
break break
except ValueError: except ValueError:
safe_print(color('red', f"Invalid option: '{opt}'")) safe_print(color("red", f"Invalid option: '{opt}'"))
return options[opt - 1][1] return options[opt - 1][1]
@ -50,14 +65,14 @@ def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api
options = [] options = []
for port in get_serial_ports(): for port in get_serial_ports():
options.append((f"{port.path} ({port.description})", port.path)) options.append((f"{port.path} ({port.description})", port.path))
if (show_ota and 'ota' in CORE.config) or (show_api and 'api' in CORE.config): if (show_ota and "ota" in CORE.config) or (show_api and "api" in CORE.config):
options.append((f"Over The Air ({CORE.address})", CORE.address)) options.append((f"Over The Air ({CORE.address})", CORE.address))
if default == 'OTA': if default == "OTA":
return CORE.address return CORE.address
if show_mqtt and 'mqtt' in CORE.config: if show_mqtt and "mqtt" in CORE.config:
options.append(("MQTT ({})".format(CORE.config['mqtt'][CONF_BROKER]), 'MQTT')) options.append(("MQTT ({})".format(CORE.config["mqtt"][CONF_BROKER]), "MQTT"))
if default == 'OTA': if default == "OTA":
return 'MQTT' return "MQTT"
if default is not None: if default is not None:
return default return default
if check_default is not None and check_default in [opt[1] for opt in options]: if check_default is not None and check_default in [opt[1] for opt in options]:
@ -66,11 +81,11 @@ def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api
def get_port_type(port): def get_port_type(port):
if port.startswith('/') or port.startswith('COM'): if port.startswith("/") or port.startswith("COM"):
return 'SERIAL' return "SERIAL"
if port == 'MQTT': if port == "MQTT":
return 'MQTT' return "MQTT"
return 'NETWORK' return "NETWORK"
def run_miniterm(config, port): def run_miniterm(config, port):
@ -80,7 +95,7 @@ def run_miniterm(config, port):
if CONF_LOGGER not in config: if CONF_LOGGER not in config:
_LOGGER.info("Logger is not enabled. Not starting UART logs.") _LOGGER.info("Logger is not enabled. Not starting UART logs.")
return return
baud_rate = config['logger'][CONF_BAUD_RATE] baud_rate = config["logger"][CONF_BAUD_RATE]
if baud_rate == 0: if baud_rate == 0:
_LOGGER.info("UART logging is disabled (baud_rate=0). Not starting UART logs.") _LOGGER.info("UART logging is disabled (baud_rate=0). Not starting UART logs.")
_LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate) _LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate)
@ -93,13 +108,18 @@ def run_miniterm(config, port):
except serial.SerialException: except serial.SerialException:
_LOGGER.error("Serial port closed!") _LOGGER.error("Serial port closed!")
return return
line = raw.replace(b'\r', b'').replace(b'\n', b'').decode('utf8', 'backslashreplace') line = (
time = datetime.now().time().strftime('[%H:%M:%S]') raw.replace(b"\r", b"")
.replace(b"\n", b"")
.decode("utf8", "backslashreplace")
)
time = datetime.now().time().strftime("[%H:%M:%S]")
message = time + line message = time + line
safe_print(message) safe_print(message)
backtrace_state = platformio_api.process_stacktrace( backtrace_state = platformio_api.process_stacktrace(
config, line, backtrace_state=backtrace_state) config, line, backtrace_state=backtrace_state
)
def wrap_to_code(name, comp): def wrap_to_code(name, comp):
@ -111,7 +131,7 @@ def wrap_to_code(name, comp):
cg.add(cg.LineComment(f"{name}:")) cg.add(cg.LineComment(f"{name}:"))
if comp.config_schema is not None: if comp.config_schema is not None:
conf_str = yaml_util.dump(conf) conf_str = yaml_util.dump(conf)
conf_str = conf_str.replace('//', '') conf_str = conf_str.replace("//", "")
cg.add(cg.LineComment(indent(conf_str))) cg.add(cg.LineComment(indent(conf_str)))
yield coro(conf) yield coro(conf)
@ -151,15 +171,31 @@ def compile_program(args, config):
def upload_using_esptool(config, port): def upload_using_esptool(config, port):
path = CORE.firmware_bin path = CORE.firmware_bin
first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get('upload_speed', 460800) first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get(
"upload_speed", 460800
)
def run_esptool(baud_rate): def run_esptool(baud_rate):
cmd = ['esptool.py', '--before', 'default_reset', '--after', 'hard_reset', cmd = [
'--baud', str(baud_rate), "esptool.py",
'--chip', 'esp8266', '--port', port, 'write_flash', '0x0', path] "--before",
"default_reset",
"--after",
"hard_reset",
"--baud",
str(baud_rate),
"--chip",
"esp8266",
"--port",
port,
"write_flash",
"0x0",
path,
]
if os.environ.get('ESPHOME_USE_SUBPROCESS') is None: if os.environ.get("ESPHOME_USE_SUBPROCESS") is None:
import esptool import esptool
# pylint: disable=protected-access # pylint: disable=protected-access
return run_external_command(esptool._main, *cmd) return run_external_command(esptool._main, *cmd)
@ -169,14 +205,16 @@ def upload_using_esptool(config, port):
if rc == 0 or first_baudrate == 115200: if rc == 0 or first_baudrate == 115200:
return rc return rc
# Try with 115200 baud rate, with some serial chips the faster baud rates do not work well # Try with 115200 baud rate, with some serial chips the faster baud rates do not work well
_LOGGER.info("Upload with baud rate %s failed. Trying again with baud rate 115200.", _LOGGER.info(
first_baudrate) "Upload with baud rate %s failed. Trying again with baud rate 115200.",
first_baudrate,
)
return run_esptool(115200) return run_esptool(115200)
def upload_program(config, args, host): def upload_program(config, args, host):
# if upload is to a serial port use platformio, otherwise assume ota # if upload is to a serial port use platformio, otherwise assume ota
if get_port_type(host) == 'SERIAL': if get_port_type(host) == "SERIAL":
from esphome import platformio_api from esphome import platformio_api
if CORE.is_esp8266: if CORE.is_esp8266:
@ -186,8 +224,10 @@ def upload_program(config, args, host):
from esphome import espota2 from esphome import espota2
if CONF_OTA not in config: if CONF_OTA not in config:
raise EsphomeError("Cannot upload Over the Air as the config does not include the ota: " raise EsphomeError(
"component") "Cannot upload Over the Air as the config does not include the ota: "
"component"
)
ota_conf = config[CONF_OTA] ota_conf = config[CONF_OTA]
remote_port = ota_conf[CONF_PORT] remote_port = ota_conf[CONF_PORT]
@ -196,19 +236,21 @@ def upload_program(config, args, host):
def show_logs(config, args, port): def show_logs(config, args, port):
if 'logger' not in config: if "logger" not in config:
raise EsphomeError("Logger is not configured!") raise EsphomeError("Logger is not configured!")
if get_port_type(port) == 'SERIAL': if get_port_type(port) == "SERIAL":
run_miniterm(config, port) run_miniterm(config, port)
return 0 return 0
if get_port_type(port) == 'NETWORK' and 'api' in config: if get_port_type(port) == "NETWORK" and "api" in config:
from esphome.api.client import run_logs from esphome.api.client import run_logs
return run_logs(config, port) return run_logs(config, port)
if get_port_type(port) == 'MQTT' and 'mqtt' in config: if get_port_type(port) == "MQTT" and "mqtt" in config:
from esphome import mqtt from esphome import mqtt
return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id) return mqtt.show_logs(
config, args.topic, args.username, args.password, args.client_id
)
raise EsphomeError("No remote or local logging method configured (api/mqtt/logger)") raise EsphomeError("No remote or local logging method configured (api/mqtt/logger)")
@ -216,7 +258,9 @@ def show_logs(config, args, port):
def clean_mqtt(config, args): def clean_mqtt(config, args):
from esphome import mqtt from esphome import mqtt
return mqtt.clear_topic(config, args.topic, args.username, args.password, args.client_id) return mqtt.clear_topic(
config, args.topic, args.username, args.password, args.client_id
)
def setup_log(debug=False, quiet=False): def setup_log(debug=False, quiet=False):
@ -230,27 +274,31 @@ def setup_log(debug=False, quiet=False):
logging.basicConfig(level=log_level) logging.basicConfig(level=log_level)
fmt = "%(levelname)s %(message)s" fmt = "%(levelname)s %(message)s"
colorfmt = f"%(log_color)s{fmt}%(reset)s" colorfmt = f"%(log_color)s{fmt}%(reset)s"
datefmt = '%H:%M:%S' datefmt = "%H:%M:%S"
logging.getLogger('urllib3').setLevel(logging.WARNING) logging.getLogger("urllib3").setLevel(logging.WARNING)
try: try:
import colorama import colorama
colorama.init(strip=True) colorama.init(strip=True)
from colorlog import ColoredFormatter from colorlog import ColoredFormatter
logging.getLogger().handlers[0].setFormatter(ColoredFormatter(
colorfmt, logging.getLogger().handlers[0].setFormatter(
datefmt=datefmt, ColoredFormatter(
reset=True, colorfmt,
log_colors={ datefmt=datefmt,
'DEBUG': 'cyan', reset=True,
'INFO': 'green', log_colors={
'WARNING': 'yellow', "DEBUG": "cyan",
'ERROR': 'red', "INFO": "green",
'CRITICAL': 'red', "WARNING": "yellow",
} "ERROR": "red",
)) "CRITICAL": "red",
},
)
)
except ImportError: except ImportError:
pass pass
@ -272,6 +320,8 @@ def command_config(args, config):
def command_vscode(args): def command_vscode(args):
from esphome import vscode from esphome import vscode
logging.disable(logging.INFO)
logging.disable(logging.WARNING)
CORE.config_path = args.configuration[0] CORE.config_path = args.configuration[0]
vscode.read_config(args) vscode.read_config(args)
@ -291,8 +341,13 @@ def command_compile(args, config):
def command_upload(args, config): def command_upload(args, config):
port = choose_upload_log_host(default=args.upload_port, check_default=None, port = choose_upload_log_host(
show_ota=True, show_mqtt=False, show_api=False) default=args.upload_port,
check_default=None,
show_ota=True,
show_mqtt=False,
show_api=False,
)
exit_code = upload_program(config, args, port) exit_code = upload_program(config, args, port)
if exit_code != 0: if exit_code != 0:
return exit_code return exit_code
@ -301,8 +356,13 @@ def command_upload(args, config):
def command_logs(args, config): def command_logs(args, config):
port = choose_upload_log_host(default=args.serial_port, check_default=None, port = choose_upload_log_host(
show_ota=False, show_mqtt=True, show_api=True) default=args.serial_port,
check_default=None,
show_ota=False,
show_mqtt=True,
show_api=True,
)
return show_logs(config, args, port) return show_logs(config, args, port)
@ -314,16 +374,26 @@ def command_run(args, config):
if exit_code != 0: if exit_code != 0:
return exit_code return exit_code
_LOGGER.info("Successfully compiled program.") _LOGGER.info("Successfully compiled program.")
port = choose_upload_log_host(default=args.upload_port, check_default=None, port = choose_upload_log_host(
show_ota=True, show_mqtt=False, show_api=True) default=args.upload_port,
check_default=None,
show_ota=True,
show_mqtt=False,
show_api=True,
)
exit_code = upload_program(config, args, port) exit_code = upload_program(config, args, port)
if exit_code != 0: if exit_code != 0:
return exit_code return exit_code
_LOGGER.info("Successfully uploaded program.") _LOGGER.info("Successfully uploaded program.")
if args.no_logs: if args.no_logs:
return 0 return 0
port = choose_upload_log_host(default=args.upload_port, check_default=port, port = choose_upload_log_host(
show_ota=False, show_mqtt=True, show_api=True) default=args.upload_port,
check_default=port,
show_ota=False,
show_mqtt=True,
show_api=True,
)
return show_logs(config, args, port) return show_logs(config, args, port)
@ -372,137 +442,189 @@ def command_update_all(args):
click.echo(f"{half_line}{middle_text}{half_line}") click.echo(f"{half_line}{middle_text}{half_line}")
for f in files: for f in files:
print("Updating {}".format(color('cyan', f))) print("Updating {}".format(color("cyan", f)))
print('-' * twidth) print("-" * twidth)
print() print()
rc = run_external_process('esphome', '--dashboard', f, 'run', '--no-logs', '--upload-port', rc = run_external_process(
'OTA') "esphome", "--dashboard", f, "run", "--no-logs", "--upload-port", "OTA"
)
if rc == 0: if rc == 0:
print_bar("[{}] {}".format(color('bold_green', 'SUCCESS'), f)) print_bar("[{}] {}".format(color("bold_green", "SUCCESS"), f))
success[f] = True success[f] = True
else: else:
print_bar("[{}] {}".format(color('bold_red', 'ERROR'), f)) print_bar("[{}] {}".format(color("bold_red", "ERROR"), f))
success[f] = False success[f] = False
print() print()
print() print()
print() print()
print_bar('[{}]'.format(color('bold_white', 'SUMMARY'))) print_bar("[{}]".format(color("bold_white", "SUMMARY")))
failed = 0 failed = 0
for f in files: for f in files:
if success[f]: if success[f]:
print(" - {}: {}".format(f, color('green', 'SUCCESS'))) print(" - {}: {}".format(f, color("green", "SUCCESS")))
else: else:
print(" - {}: {}".format(f, color('bold_red', 'FAILED'))) print(" - {}: {}".format(f, color("bold_red", "FAILED")))
failed += 1 failed += 1
return failed return failed
PRE_CONFIG_ACTIONS = { PRE_CONFIG_ACTIONS = {
'wizard': command_wizard, "wizard": command_wizard,
'version': command_version, "version": command_version,
'dashboard': command_dashboard, "dashboard": command_dashboard,
'vscode': command_vscode, "vscode": command_vscode,
'update-all': command_update_all, "update-all": command_update_all,
} }
POST_CONFIG_ACTIONS = { POST_CONFIG_ACTIONS = {
'config': command_config, "config": command_config,
'compile': command_compile, "compile": command_compile,
'upload': command_upload, "upload": command_upload,
'logs': command_logs, "logs": command_logs,
'run': command_run, "run": command_run,
'clean-mqtt': command_clean_mqtt, "clean-mqtt": command_clean_mqtt,
'mqtt-fingerprint': command_mqtt_fingerprint, "mqtt-fingerprint": command_mqtt_fingerprint,
'clean': command_clean, "clean": command_clean,
} }
def parse_args(argv): def parse_args(argv):
parser = argparse.ArgumentParser(description=f'ESPHome v{const.__version__}') parser = argparse.ArgumentParser(description=f"ESPHome v{const.__version__}")
parser.add_argument('-v', '--verbose', help="Enable verbose esphome logs.", parser.add_argument(
action='store_true') "-v", "--verbose", help="Enable verbose esphome logs.", action="store_true"
parser.add_argument('-q', '--quiet', help="Disable all esphome logs.", )
action='store_true') parser.add_argument(
parser.add_argument('--dashboard', help=argparse.SUPPRESS, action='store_true') "-q", "--quiet", help="Disable all esphome logs.", action="store_true"
parser.add_argument('-s', '--substitution', nargs=2, action='append', )
help='Add a substitution', metavar=('key', 'value')) parser.add_argument("--dashboard", help=argparse.SUPPRESS, action="store_true")
parser.add_argument('configuration', help='Your YAML configuration file.', nargs='*') parser.add_argument(
"-s",
"--substitution",
nargs=2,
action="append",
help="Add a substitution",
metavar=("key", "value"),
)
parser.add_argument(
"configuration", help="Your YAML configuration file.", nargs="*"
)
subparsers = parser.add_subparsers(help='Commands', dest='command') subparsers = parser.add_subparsers(help="Commands", dest="command")
subparsers.required = True subparsers.required = True
subparsers.add_parser('config', help='Validate the configuration and spit it out.') subparsers.add_parser("config", help="Validate the configuration and spit it out.")
parser_compile = subparsers.add_parser('compile', parser_compile = subparsers.add_parser(
help='Read the configuration and compile a program.') "compile", help="Read the configuration and compile a program."
parser_compile.add_argument('--only-generate', )
help="Only generate source code, do not compile.", parser_compile.add_argument(
action='store_true') "--only-generate",
help="Only generate source code, do not compile.",
action="store_true",
)
parser_upload = subparsers.add_parser('upload', help='Validate the configuration ' parser_upload = subparsers.add_parser(
'and upload the latest binary.') "upload", help="Validate the configuration " "and upload the latest binary."
parser_upload.add_argument('--upload-port', help="Manually specify the upload port to use. " )
"For example /dev/cu.SLAB_USBtoUART.") parser_upload.add_argument(
"--upload-port",
help="Manually specify the upload port to use. "
"For example /dev/cu.SLAB_USBtoUART.",
)
parser_logs = subparsers.add_parser('logs', help='Validate the configuration ' parser_logs = subparsers.add_parser(
'and show all MQTT logs.') "logs", help="Validate the configuration " "and show all MQTT logs."
parser_logs.add_argument('--topic', help='Manually set the topic to subscribe to.') )
parser_logs.add_argument('--username', help='Manually set the username.') parser_logs.add_argument("--topic", help="Manually set the topic to subscribe to.")
parser_logs.add_argument('--password', help='Manually set the password.') parser_logs.add_argument("--username", help="Manually set the username.")
parser_logs.add_argument('--client-id', help='Manually set the client id.') parser_logs.add_argument("--password", help="Manually set the password.")
parser_logs.add_argument('--serial-port', help="Manually specify a serial port to use" parser_logs.add_argument("--client-id", help="Manually set the client id.")
"For example /dev/cu.SLAB_USBtoUART.") parser_logs.add_argument(
"--serial-port",
help="Manually specify a serial port to use"
"For example /dev/cu.SLAB_USBtoUART.",
)
parser_run = subparsers.add_parser('run', help='Validate the configuration, create a binary, ' parser_run = subparsers.add_parser(
'upload it, and start MQTT logs.') "run",
parser_run.add_argument('--upload-port', help="Manually specify the upload port/ip to use. " help="Validate the configuration, create a binary, "
"For example /dev/cu.SLAB_USBtoUART.") "upload it, and start MQTT logs.",
parser_run.add_argument('--no-logs', help='Disable starting MQTT logs.', )
action='store_true') parser_run.add_argument(
parser_run.add_argument('--topic', help='Manually set the topic to subscribe to for logs.') "--upload-port",
parser_run.add_argument('--username', help='Manually set the MQTT username for logs.') help="Manually specify the upload port/ip to use. "
parser_run.add_argument('--password', help='Manually set the MQTT password for logs.') "For example /dev/cu.SLAB_USBtoUART.",
parser_run.add_argument('--client-id', help='Manually set the client id for logs.') )
parser_run.add_argument(
"--no-logs", help="Disable starting MQTT logs.", action="store_true"
)
parser_run.add_argument(
"--topic", help="Manually set the topic to subscribe to for logs."
)
parser_run.add_argument(
"--username", help="Manually set the MQTT username for logs."
)
parser_run.add_argument(
"--password", help="Manually set the MQTT password for logs."
)
parser_run.add_argument("--client-id", help="Manually set the client id for logs.")
parser_clean = subparsers.add_parser('clean-mqtt', help="Helper to clear an MQTT topic from " parser_clean = subparsers.add_parser(
"retain messages.") "clean-mqtt", help="Helper to clear an MQTT topic from " "retain messages."
parser_clean.add_argument('--topic', help='Manually set the topic to subscribe to.') )
parser_clean.add_argument('--username', help='Manually set the username.') parser_clean.add_argument("--topic", help="Manually set the topic to subscribe to.")
parser_clean.add_argument('--password', help='Manually set the password.') parser_clean.add_argument("--username", help="Manually set the username.")
parser_clean.add_argument('--client-id', help='Manually set the client id.') parser_clean.add_argument("--password", help="Manually set the password.")
parser_clean.add_argument("--client-id", help="Manually set the client id.")
subparsers.add_parser('wizard', help="A helpful setup wizard that will guide " subparsers.add_parser(
"you through setting up esphome.") "wizard",
help="A helpful setup wizard that will guide "
"you through setting up esphome.",
)
subparsers.add_parser('mqtt-fingerprint', help="Get the SSL fingerprint from a MQTT broker.") subparsers.add_parser(
"mqtt-fingerprint", help="Get the SSL fingerprint from a MQTT broker."
)
subparsers.add_parser('version', help="Print the esphome version and exit.") subparsers.add_parser("version", help="Print the esphome version and exit.")
subparsers.add_parser('clean', help="Delete all temporary build files.") subparsers.add_parser("clean", help="Delete all temporary build files.")
dashboard = subparsers.add_parser('dashboard', dashboard = subparsers.add_parser(
help="Create a simple web server for a dashboard.") "dashboard", help="Create a simple web server for a dashboard."
dashboard.add_argument("--port", help="The HTTP port to open connections on. Defaults to 6052.", )
type=int, default=6052) dashboard.add_argument(
dashboard.add_argument("--username", help="The optional username to require " "--port",
"for authentication.", help="The HTTP port to open connections on. Defaults to 6052.",
type=str, default='') type=int,
dashboard.add_argument("--password", help="The optional password to require " default=6052,
"for authentication.", )
type=str, default='') dashboard.add_argument(
dashboard.add_argument("--open-ui", help="Open the dashboard UI in a browser.", "--username",
action='store_true') help="The optional username to require " "for authentication.",
dashboard.add_argument("--hassio", type=str,
help=argparse.SUPPRESS, default="",
action="store_true") )
dashboard.add_argument("--socket", dashboard.add_argument(
help="Make the dashboard serve under a unix socket", type=str) "--password",
help="The optional password to require " "for authentication.",
type=str,
default="",
)
dashboard.add_argument(
"--open-ui", help="Open the dashboard UI in a browser.", action="store_true"
)
dashboard.add_argument("--hassio", help=argparse.SUPPRESS, action="store_true")
dashboard.add_argument(
"--socket", help="Make the dashboard serve under a unix socket", type=str
)
vscode = subparsers.add_parser('vscode', help=argparse.SUPPRESS) vscode = subparsers.add_parser("vscode", help=argparse.SUPPRESS)
vscode.add_argument('--ace', action='store_true') vscode.add_argument("--ace", action="store_true")
subparsers.add_parser('update-all', help=argparse.SUPPRESS) subparsers.add_parser("update-all", help=argparse.SUPPRESS)
return parser.parse_args(argv[1:]) return parser.parse_args(argv[1:])
@ -512,13 +634,15 @@ def run_esphome(argv):
CORE.dashboard = args.dashboard CORE.dashboard = args.dashboard
setup_log(args.verbose, args.quiet) setup_log(args.verbose, args.quiet)
if args.command != 'version' and not args.configuration: if args.command != "version" and not args.configuration:
_LOGGER.error("Missing configuration parameter, see esphome --help.") _LOGGER.error("Missing configuration parameter, see esphome --help.")
return 1 return 1
if sys.version_info < (3, 6, 0): if sys.version_info < (3, 6, 0):
_LOGGER.error("You're running ESPHome with Python <3.6. ESPHome is no longer compatible " _LOGGER.error(
"with this Python version. Please reinstall ESPHome with Python 3.6+") "You're running ESPHome with Python <3.6. ESPHome is no longer compatible "
"with this Python version. Please reinstall ESPHome with Python 3.6+"
)
return 1 return 1
if args.command in PRE_CONFIG_ACTIONS: if args.command in PRE_CONFIG_ACTIONS:

File diff suppressed because one or more lines are too long

View file

@ -177,10 +177,14 @@ class APIClient(threading.Thread):
try: try:
ip = resolve_ip_address(self._address) ip = resolve_ip_address(self._address)
except EsphomeError as err: except EsphomeError as err:
_LOGGER.warning("Error resolving IP address of %s. Is it connected to WiFi?", _LOGGER.warning(
self._address) "Error resolving IP address of %s. Is it connected to WiFi?",
_LOGGER.warning("(If this error persists, please set a static IP address: " self._address,
"https://esphome.io/components/wifi.html#manual-ips)") )
_LOGGER.warning(
"(If this error persists, please set a static IP address: "
"https://esphome.io/components/wifi.html#manual-ips)"
)
raise APIConnectionError(err) from err raise APIConnectionError(err) from err
_LOGGER.info("Connecting to %s:%s (%s)", self._address, self._port, ip) _LOGGER.info("Connecting to %s:%s (%s)", self._address, self._port, ip)
@ -198,14 +202,19 @@ class APIClient(threading.Thread):
self._socket_open_event.set() self._socket_open_event.set()
hello = pb.HelloRequest() hello = pb.HelloRequest()
hello.client_info = f'ESPHome v{const.__version__}' hello.client_info = f"ESPHome v{const.__version__}"
try: try:
resp = self._send_message_await_response(hello, pb.HelloResponse) resp = self._send_message_await_response(hello, pb.HelloResponse)
except APIConnectionError as err: except APIConnectionError as err:
self._fatal_error(err) self._fatal_error(err)
raise err raise err
_LOGGER.debug("Successfully connected to %s ('%s' API=%s.%s)", self._address, _LOGGER.debug(
resp.server_info, resp.api_version_major, resp.api_version_minor) "Successfully connected to %s ('%s' API=%s.%s)",
self._address,
resp.server_info,
resp.api_version_major,
resp.api_version_minor,
)
self._connected = True self._connected = True
self._refresh_ping() self._refresh_ping()
if self.on_connect is not None: if self.on_connect is not None:
@ -270,7 +279,9 @@ class APIClient(threading.Thread):
req += encoded req += encoded
self._write(req) self._write(req)
def _send_message_await_response_complex(self, send_msg, do_append, do_stop, timeout=5): def _send_message_await_response_complex(
self, send_msg, do_append, do_stop, timeout=5
):
event = threading.Event() event = threading.Event()
responses = [] responses = []
@ -295,12 +306,15 @@ class APIClient(threading.Thread):
def is_response(msg): def is_response(msg):
return isinstance(msg, response_type) return isinstance(msg, response_type)
return self._send_message_await_response_complex(send_msg, is_response, is_response, return self._send_message_await_response_complex(
timeout)[0] send_msg, is_response, is_response, timeout
)[0]
def device_info(self): def device_info(self):
self._check_connected() self._check_connected()
return self._send_message_await_response(pb.DeviceInfoRequest(), pb.DeviceInfoResponse) return self._send_message_await_response(
pb.DeviceInfoRequest(), pb.DeviceInfoResponse
)
def ping(self): def ping(self):
self._check_connected() self._check_connected()
@ -310,7 +324,9 @@ class APIClient(threading.Thread):
self._check_connected() self._check_connected()
try: try:
self._send_message_await_response(pb.DisconnectRequest(), pb.DisconnectResponse) self._send_message_await_response(
pb.DisconnectRequest(), pb.DisconnectResponse
)
except APIConnectionError: except APIConnectionError:
pass pass
self._close_socket() self._close_socket()
@ -415,7 +431,7 @@ class APIClient(threading.Thread):
def run_logs(config, address): def run_logs(config, address):
conf = config['api'] conf = config["api"]
port = conf[CONF_PORT] port = conf[CONF_PORT]
password = conf[CONF_PASSWORD] password = conf[CONF_PASSWORD]
_LOGGER.info("Starting log output from %s using esphome API", address) _LOGGER.info("Starting log output from %s using esphome API", address)
@ -447,24 +463,35 @@ def run_logs(config, address):
_LOGGER.info("Successfully connected to %s", address) _LOGGER.info("Successfully connected to %s", address)
return return
wait_time = int(min(1.5**min(tries, 100), 30)) wait_time = int(min(1.5 ** min(tries, 100), 30))
if not has_connects: if not has_connects:
_LOGGER.warning("Initial connection failed. The ESP might not be connected " _LOGGER.warning(
"to WiFi yet (%s). Re-Trying in %s seconds", "Initial connection failed. The ESP might not be connected "
error, wait_time) "to WiFi yet (%s). Re-Trying in %s seconds",
error,
wait_time,
)
else: else:
_LOGGER.warning("Couldn't connect to API (%s). Trying to reconnect in %s seconds", _LOGGER.warning(
error, wait_time) "Couldn't connect to API (%s). Trying to reconnect in %s seconds",
timer = threading.Timer(wait_time, functools.partial(try_connect, None, tries + 1)) error,
wait_time,
)
timer = threading.Timer(
wait_time, functools.partial(try_connect, None, tries + 1)
)
timer.start() timer.start()
retry_timer.append(timer) retry_timer.append(timer)
def on_log(msg): def on_log(msg):
time_ = datetime.now().time().strftime('[%H:%M:%S]') time_ = datetime.now().time().strftime("[%H:%M:%S]")
text = msg.message text = msg.message
if msg.send_failed: if msg.send_failed:
text = color('white', '(Message skipped because it was too big to fit in ' text = color(
'TCP buffer - This is only cosmetic)') "white",
"(Message skipped because it was too big to fit in "
"TCP buffer - This is only cosmetic)",
)
safe_print(time_ + text) safe_print(time_ + text)
def on_login(): def on_login():

View file

@ -1,8 +1,17 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import CONF_AUTOMATION_ID, CONF_CONDITION, CONF_ELSE, CONF_ID, CONF_THEN, \ from esphome.const import (
CONF_TRIGGER_ID, CONF_TYPE_ID, CONF_TIME CONF_AUTOMATION_ID,
CONF_CONDITION,
CONF_ELSE,
CONF_ID,
CONF_THEN,
CONF_TRIGGER_ID,
CONF_TYPE_ID,
CONF_TIME,
)
from esphome.core import coroutine from esphome.core import coroutine
from esphome.jsonschema import jschema_extractor
from esphome.util import Registry from esphome.util import Registry
@ -13,7 +22,12 @@ def maybe_simple_id(*validators):
def maybe_conf(conf, *validators): def maybe_conf(conf, *validators):
validator = cv.All(*validators) validator = cv.All(*validators)
@jschema_extractor("maybe")
def validate(value): def validate(value):
# pylint: disable=comparison-with-callable
if value == jschema_extractor:
return validator
if isinstance(value, dict): if isinstance(value, dict):
return validator(value) return validator(value)
with cv.remove_prepend_path([conf]): with cv.remove_prepend_path([conf]):
@ -30,36 +44,34 @@ def register_condition(name, condition_type, schema):
return CONDITION_REGISTRY.register(name, condition_type, schema) return CONDITION_REGISTRY.register(name, condition_type, schema)
Action = cg.esphome_ns.class_('Action') Action = cg.esphome_ns.class_("Action")
Trigger = cg.esphome_ns.class_('Trigger') Trigger = cg.esphome_ns.class_("Trigger")
ACTION_REGISTRY = Registry() ACTION_REGISTRY = Registry()
Condition = cg.esphome_ns.class_('Condition') Condition = cg.esphome_ns.class_("Condition")
CONDITION_REGISTRY = Registry() CONDITION_REGISTRY = Registry()
validate_action = cv.validate_registry_entry('action', ACTION_REGISTRY) validate_action = cv.validate_registry_entry("action", ACTION_REGISTRY)
validate_action_list = cv.validate_registry('action', ACTION_REGISTRY) validate_action_list = cv.validate_registry("action", ACTION_REGISTRY)
validate_condition = cv.validate_registry_entry('condition', CONDITION_REGISTRY) validate_condition = cv.validate_registry_entry("condition", CONDITION_REGISTRY)
validate_condition_list = cv.validate_registry('condition', CONDITION_REGISTRY) validate_condition_list = cv.validate_registry("condition", CONDITION_REGISTRY)
def validate_potentially_and_condition(value): def validate_potentially_and_condition(value):
if isinstance(value, list): if isinstance(value, list):
with cv.remove_prepend_path(['and']): with cv.remove_prepend_path(["and"]):
return validate_condition({ return validate_condition({"and": value})
'and': value
})
return validate_condition(value) return validate_condition(value)
DelayAction = cg.esphome_ns.class_('DelayAction', Action, cg.Component) DelayAction = cg.esphome_ns.class_("DelayAction", Action, cg.Component)
LambdaAction = cg.esphome_ns.class_('LambdaAction', Action) LambdaAction = cg.esphome_ns.class_("LambdaAction", Action)
IfAction = cg.esphome_ns.class_('IfAction', Action) IfAction = cg.esphome_ns.class_("IfAction", Action)
WhileAction = cg.esphome_ns.class_('WhileAction', Action) WhileAction = cg.esphome_ns.class_("WhileAction", Action)
WaitUntilAction = cg.esphome_ns.class_('WaitUntilAction', Action, cg.Component) WaitUntilAction = cg.esphome_ns.class_("WaitUntilAction", Action, cg.Component)
UpdateComponentAction = cg.esphome_ns.class_('UpdateComponentAction', Action) UpdateComponentAction = cg.esphome_ns.class_("UpdateComponentAction", Action)
Automation = cg.esphome_ns.class_('Automation') Automation = cg.esphome_ns.class_("Automation")
LambdaCondition = cg.esphome_ns.class_('LambdaCondition', Condition) LambdaCondition = cg.esphome_ns.class_("LambdaCondition", Condition)
ForCondition = cg.esphome_ns.class_('ForCondition', Condition, cg.Component) ForCondition = cg.esphome_ns.class_("ForCondition", Condition, cg.Component)
def validate_automation(extra_schema=None, extra_validators=None, single=False): def validate_automation(extra_schema=None, extra_validators=None, single=False):
@ -83,10 +95,10 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False):
try: try:
return cv.Schema([schema])(value) return cv.Schema([schema])(value)
except cv.Invalid as err2: except cv.Invalid as err2:
if 'extra keys not allowed' in str(err2) and len(err2.path) == 2: if "extra keys not allowed" in str(err2) and len(err2.path) == 2:
# pylint: disable=raise-missing-from # pylint: disable=raise-missing-from
raise err raise err
if 'Unable to find action' in str(err): if "Unable to find action" in str(err):
raise err2 raise err2
raise cv.MultipleInvalid([err, err2]) raise cv.MultipleInvalid([err, err2])
elif isinstance(value, dict): elif isinstance(value, dict):
@ -97,7 +109,13 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False):
# This should only happen with invalid configs, but let's have a nice error message. # This should only happen with invalid configs, but let's have a nice error message.
return [schema(value)] return [schema(value)]
@jschema_extractor("automation")
def validator(value): def validator(value):
# hack to get the schema
# pylint: disable=comparison-with-callable
if value == jschema_extractor:
return schema
value = validator_(value) value = validator_(value)
if extra_validators is not None: if extra_validators is not None:
value = cv.Schema([extra_validators])(value) value = cv.Schema([extra_validators])(value)
@ -110,47 +128,59 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False):
return validator return validator
AUTOMATION_SCHEMA = cv.Schema({ AUTOMATION_SCHEMA = cv.Schema(
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Trigger), {
cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_id(Automation), cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Trigger),
cv.Required(CONF_THEN): validate_action_list, cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_id(Automation),
}) cv.Required(CONF_THEN): validate_action_list,
}
)
AndCondition = cg.esphome_ns.class_('AndCondition', Condition) AndCondition = cg.esphome_ns.class_("AndCondition", Condition)
OrCondition = cg.esphome_ns.class_('OrCondition', Condition) OrCondition = cg.esphome_ns.class_("OrCondition", Condition)
NotCondition = cg.esphome_ns.class_('NotCondition', Condition) NotCondition = cg.esphome_ns.class_("NotCondition", Condition)
@register_condition('and', AndCondition, validate_condition_list) @register_condition("and", AndCondition, validate_condition_list)
def and_condition_to_code(config, condition_id, template_arg, args): def and_condition_to_code(config, condition_id, template_arg, args):
conditions = yield build_condition_list(config, template_arg, args) conditions = yield build_condition_list(config, template_arg, args)
yield cg.new_Pvariable(condition_id, template_arg, conditions) yield cg.new_Pvariable(condition_id, template_arg, conditions)
@register_condition('or', OrCondition, validate_condition_list) @register_condition("or", OrCondition, validate_condition_list)
def or_condition_to_code(config, condition_id, template_arg, args): def or_condition_to_code(config, condition_id, template_arg, args):
conditions = yield build_condition_list(config, template_arg, args) conditions = yield build_condition_list(config, template_arg, args)
yield cg.new_Pvariable(condition_id, template_arg, conditions) yield cg.new_Pvariable(condition_id, template_arg, conditions)
@register_condition('not', NotCondition, validate_potentially_and_condition) @register_condition("not", NotCondition, validate_potentially_and_condition)
def not_condition_to_code(config, condition_id, template_arg, args): def not_condition_to_code(config, condition_id, template_arg, args):
condition = yield build_condition(config, template_arg, args) condition = yield build_condition(config, template_arg, args)
yield cg.new_Pvariable(condition_id, template_arg, condition) yield cg.new_Pvariable(condition_id, template_arg, condition)
@register_condition('lambda', LambdaCondition, cv.lambda_) @register_condition("lambda", LambdaCondition, cv.lambda_)
def lambda_condition_to_code(config, condition_id, template_arg, args): def lambda_condition_to_code(config, condition_id, template_arg, args):
lambda_ = yield cg.process_lambda(config, args, return_type=bool) lambda_ = yield cg.process_lambda(config, args, return_type=bool)
yield cg.new_Pvariable(condition_id, template_arg, lambda_) yield cg.new_Pvariable(condition_id, template_arg, lambda_)
@register_condition('for', ForCondition, cv.Schema({ @register_condition(
cv.Required(CONF_TIME): cv.templatable(cv.positive_time_period_milliseconds), "for",
cv.Required(CONF_CONDITION): validate_potentially_and_condition, ForCondition,
}).extend(cv.COMPONENT_SCHEMA)) cv.Schema(
{
cv.Required(CONF_TIME): cv.templatable(
cv.positive_time_period_milliseconds
),
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
}
).extend(cv.COMPONENT_SCHEMA),
)
def for_condition_to_code(config, condition_id, template_arg, args): def for_condition_to_code(config, condition_id, template_arg, args):
condition = yield build_condition(config[CONF_CONDITION], cg.TemplateArguments(), []) condition = yield build_condition(
config[CONF_CONDITION], cg.TemplateArguments(), []
)
var = cg.new_Pvariable(condition_id, template_arg, condition) var = cg.new_Pvariable(condition_id, template_arg, condition)
yield cg.register_component(var, config) yield cg.register_component(var, config)
templ = yield cg.templatable(config[CONF_TIME], args, cg.uint32) templ = yield cg.templatable(config[CONF_TIME], args, cg.uint32)
@ -158,7 +188,9 @@ def for_condition_to_code(config, condition_id, template_arg, args):
yield var yield var
@register_action('delay', DelayAction, cv.templatable(cv.positive_time_period_milliseconds)) @register_action(
"delay", DelayAction, cv.templatable(cv.positive_time_period_milliseconds)
)
def delay_action_to_code(config, action_id, template_arg, args): def delay_action_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_component(var, {}) yield cg.register_component(var, {})
@ -167,11 +199,18 @@ def delay_action_to_code(config, action_id, template_arg, args):
yield var yield var
@register_action('if', IfAction, cv.All({ @register_action(
cv.Required(CONF_CONDITION): validate_potentially_and_condition, "if",
cv.Optional(CONF_THEN): validate_action_list, IfAction,
cv.Optional(CONF_ELSE): validate_action_list, cv.All(
}, cv.has_at_least_one_key(CONF_THEN, CONF_ELSE))) {
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
cv.Optional(CONF_THEN): validate_action_list,
cv.Optional(CONF_ELSE): validate_action_list,
},
cv.has_at_least_one_key(CONF_THEN, CONF_ELSE),
),
)
def if_action_to_code(config, action_id, template_arg, args): def if_action_to_code(config, action_id, template_arg, args):
conditions = yield build_condition(config[CONF_CONDITION], template_arg, args) conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
var = cg.new_Pvariable(action_id, template_arg, conditions) var = cg.new_Pvariable(action_id, template_arg, conditions)
@ -184,10 +223,16 @@ def if_action_to_code(config, action_id, template_arg, args):
yield var yield var
@register_action('while', WhileAction, cv.Schema({ @register_action(
cv.Required(CONF_CONDITION): validate_potentially_and_condition, "while",
cv.Required(CONF_THEN): validate_action_list, WhileAction,
})) cv.Schema(
{
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
cv.Required(CONF_THEN): validate_action_list,
}
),
)
def while_action_to_code(config, action_id, template_arg, args): def while_action_to_code(config, action_id, template_arg, args):
conditions = yield build_condition(config[CONF_CONDITION], template_arg, args) conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
var = cg.new_Pvariable(action_id, template_arg, conditions) var = cg.new_Pvariable(action_id, template_arg, conditions)
@ -197,15 +242,17 @@ def while_action_to_code(config, action_id, template_arg, args):
def validate_wait_until(value): def validate_wait_until(value):
schema = cv.Schema({ schema = cv.Schema(
cv.Required(CONF_CONDITION): validate_potentially_and_condition, {
}) cv.Required(CONF_CONDITION): validate_potentially_and_condition,
}
)
if isinstance(value, dict) and CONF_CONDITION in value: if isinstance(value, dict) and CONF_CONDITION in value:
return schema(value) return schema(value)
return validate_wait_until({CONF_CONDITION: value}) return validate_wait_until({CONF_CONDITION: value})
@register_action('wait_until', WaitUntilAction, validate_wait_until) @register_action("wait_until", WaitUntilAction, validate_wait_until)
def wait_until_action_to_code(config, action_id, template_arg, args): def wait_until_action_to_code(config, action_id, template_arg, args):
conditions = yield build_condition(config[CONF_CONDITION], template_arg, args) conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
var = cg.new_Pvariable(action_id, template_arg, conditions) var = cg.new_Pvariable(action_id, template_arg, conditions)
@ -213,15 +260,21 @@ def wait_until_action_to_code(config, action_id, template_arg, args):
yield var yield var
@register_action('lambda', LambdaAction, cv.lambda_) @register_action("lambda", LambdaAction, cv.lambda_)
def lambda_action_to_code(config, action_id, template_arg, args): def lambda_action_to_code(config, action_id, template_arg, args):
lambda_ = yield cg.process_lambda(config, args, return_type=cg.void) lambda_ = yield cg.process_lambda(config, args, return_type=cg.void)
yield cg.new_Pvariable(action_id, template_arg, lambda_) yield cg.new_Pvariable(action_id, template_arg, lambda_)
@register_action('component.update', UpdateComponentAction, maybe_simple_id({ @register_action(
cv.Required(CONF_ID): cv.use_id(cg.PollingComponent), "component.update",
})) UpdateComponentAction,
maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(cg.PollingComponent),
}
),
)
def component_update_action_to_code(config, action_id, template_arg, args): def component_update_action_to_code(config, action_id, template_arg, args):
comp = yield cg.get_variable(config[CONF_ID]) comp = yield cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(action_id, template_arg, comp) yield cg.new_Pvariable(action_id, template_arg, comp)
@ -229,7 +282,9 @@ def component_update_action_to_code(config, action_id, template_arg, args):
@coroutine @coroutine
def build_action(full_config, template_arg, args): def build_action(full_config, template_arg, args):
registry_entry, config = cg.extract_registry_entry_config(ACTION_REGISTRY, full_config) registry_entry, config = cg.extract_registry_entry_config(
ACTION_REGISTRY, full_config
)
action_id = full_config[CONF_TYPE_ID] action_id = full_config[CONF_TYPE_ID]
builder = registry_entry.coroutine_fun builder = registry_entry.coroutine_fun
yield builder(config, action_id, template_arg, args) yield builder(config, action_id, template_arg, args)
@ -246,7 +301,9 @@ def build_action_list(config, templ, arg_type):
@coroutine @coroutine
def build_condition(full_config, template_arg, args): def build_condition(full_config, template_arg, args):
registry_entry, config = cg.extract_registry_entry_config(CONDITION_REGISTRY, full_config) registry_entry, config = cg.extract_registry_entry_config(
CONDITION_REGISTRY, full_config
)
action_id = full_config[CONF_TYPE_ID] action_id = full_config[CONF_TYPE_ID]
builder = registry_entry.coroutine_fun builder = registry_entry.coroutine_fun
yield builder(config, action_id, template_arg, args) yield builder(config, action_id, template_arg, args)

View file

@ -9,18 +9,71 @@
# pylint: disable=unused-import # pylint: disable=unused-import
from esphome.cpp_generator import ( # noqa from esphome.cpp_generator import ( # noqa
Expression, RawExpression, RawStatement, TemplateArguments, Expression,
StructInitializer, ArrayInitializer, safe_exp, Statement, LineComment, RawExpression,
progmem_array, statement, variable, Pvariable, new_Pvariable, RawStatement,
add, add_global, add_library, add_build_flag, add_define, TemplateArguments,
get_variable, get_variable_with_full_id, process_lambda, is_template, templatable, MockObj, StructInitializer,
MockObjClass) ArrayInitializer,
safe_exp,
Statement,
LineComment,
progmem_array,
statement,
variable,
new_variable,
Pvariable,
new_Pvariable,
add,
add_global,
add_library,
add_build_flag,
add_define,
get_variable,
get_variable_with_full_id,
process_lambda,
is_template,
templatable,
MockObj,
MockObjClass,
)
from esphome.cpp_helpers import ( # noqa from esphome.cpp_helpers import ( # noqa
gpio_pin_expression, register_component, build_registry_entry, gpio_pin_expression,
build_registry_list, extract_registry_entry_config, register_parented) register_component,
build_registry_entry,
build_registry_list,
extract_registry_entry_config,
register_parented,
)
from esphome.cpp_types import ( # noqa from esphome.cpp_types import ( # noqa
global_ns, void, nullptr, float_, double, bool_, int_, std_ns, std_string, global_ns,
std_vector, uint8, uint16, uint32, int32, const_char_ptr, NAN, void,
esphome_ns, App, Nameable, Component, ComponentPtr, nullptr,
PollingComponent, Application, optional, arduino_json_ns, JsonObject, float_,
JsonObjectRef, JsonObjectConstRef, Controller, GPIOPin) double,
bool_,
int_,
std_ns,
std_string,
std_vector,
uint8,
uint16,
uint32,
int32,
const_char_ptr,
NAN,
esphome_ns,
App,
Nameable,
Component,
ComponentPtr,
PollingComponent,
Application,
optional,
arduino_json_ns,
JsonObject,
JsonObjectRef,
JsonObjectConstRef,
Controller,
GPIOPin,
)

View file

@ -11,6 +11,7 @@ void A4988::setup() {
if (this->sleep_pin_ != nullptr) { if (this->sleep_pin_ != nullptr) {
this->sleep_pin_->setup(); this->sleep_pin_->setup();
this->sleep_pin_->digital_write(false); this->sleep_pin_->digital_write(false);
this->sleep_pin_state_ = false;
} }
this->step_pin_->setup(); this->step_pin_->setup();
this->step_pin_->digital_write(false); this->step_pin_->digital_write(false);
@ -27,7 +28,12 @@ void A4988::dump_config() {
void A4988::loop() { void A4988::loop() {
bool at_target = this->has_reached_target(); bool at_target = this->has_reached_target();
if (this->sleep_pin_ != nullptr) { if (this->sleep_pin_ != nullptr) {
bool sleep_rising_edge = !sleep_pin_state_ & !at_target;
this->sleep_pin_->digital_write(!at_target); this->sleep_pin_->digital_write(!at_target);
this->sleep_pin_state_ = !at_target;
if (sleep_rising_edge) {
delayMicroseconds(1000);
}
} }
if (at_target) { if (at_target) {
this->high_freq_.stop(); this->high_freq_.stop();

View file

@ -21,6 +21,7 @@ class A4988 : public stepper::Stepper, public Component {
GPIOPin *step_pin_; GPIOPin *step_pin_;
GPIOPin *dir_pin_; GPIOPin *dir_pin_;
GPIOPin *sleep_pin_{nullptr}; GPIOPin *sleep_pin_{nullptr};
bool sleep_pin_state_;
HighFrequencyLoopRequester high_freq_; HighFrequencyLoopRequester high_freq_;
}; };

View file

@ -5,15 +5,17 @@ import esphome.codegen as cg
from esphome.const import CONF_DIR_PIN, CONF_ID, CONF_SLEEP_PIN, CONF_STEP_PIN from esphome.const import CONF_DIR_PIN, CONF_ID, CONF_SLEEP_PIN, CONF_STEP_PIN
a4988_ns = cg.esphome_ns.namespace('a4988') a4988_ns = cg.esphome_ns.namespace("a4988")
A4988 = a4988_ns.class_('A4988', stepper.Stepper, cg.Component) A4988 = a4988_ns.class_("A4988", stepper.Stepper, cg.Component)
CONFIG_SCHEMA = stepper.STEPPER_SCHEMA.extend({ CONFIG_SCHEMA = stepper.STEPPER_SCHEMA.extend(
cv.Required(CONF_ID): cv.declare_id(A4988), {
cv.Required(CONF_STEP_PIN): pins.gpio_output_pin_schema, cv.Required(CONF_ID): cv.declare_id(A4988),
cv.Required(CONF_DIR_PIN): pins.gpio_output_pin_schema, cv.Required(CONF_STEP_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_SLEEP_PIN): pins.gpio_output_pin_schema, cv.Required(CONF_DIR_PIN): pins.gpio_output_pin_schema,
}).extend(cv.COMPONENT_SCHEMA) cv.Optional(CONF_SLEEP_PIN): pins.gpio_output_pin_schema,
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config): def to_code(config):

View file

@ -4,28 +4,32 @@ from esphome import pins
from esphome.components import output from esphome.components import output
from esphome.const import CONF_ID, CONF_MIN_POWER, CONF_METHOD from esphome.const import CONF_ID, CONF_MIN_POWER, CONF_METHOD
CODEOWNERS = ['@glmnet'] CODEOWNERS = ["@glmnet"]
ac_dimmer_ns = cg.esphome_ns.namespace('ac_dimmer') ac_dimmer_ns = cg.esphome_ns.namespace("ac_dimmer")
AcDimmer = ac_dimmer_ns.class_('AcDimmer', output.FloatOutput, cg.Component) AcDimmer = ac_dimmer_ns.class_("AcDimmer", output.FloatOutput, cg.Component)
DimMethod = ac_dimmer_ns.enum('DimMethod') DimMethod = ac_dimmer_ns.enum("DimMethod")
DIM_METHODS = { DIM_METHODS = {
'LEADING_PULSE': DimMethod.DIM_METHOD_LEADING_PULSE, "LEADING_PULSE": DimMethod.DIM_METHOD_LEADING_PULSE,
'LEADING': DimMethod.DIM_METHOD_LEADING, "LEADING": DimMethod.DIM_METHOD_LEADING,
'TRAILING': DimMethod.DIM_METHOD_TRAILING, "TRAILING": DimMethod.DIM_METHOD_TRAILING,
} }
CONF_GATE_PIN = 'gate_pin' CONF_GATE_PIN = "gate_pin"
CONF_ZERO_CROSS_PIN = 'zero_cross_pin' CONF_ZERO_CROSS_PIN = "zero_cross_pin"
CONF_INIT_WITH_HALF_CYCLE = 'init_with_half_cycle' CONF_INIT_WITH_HALF_CYCLE = "init_with_half_cycle"
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({ CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
cv.Required(CONF_ID): cv.declare_id(AcDimmer), {
cv.Required(CONF_GATE_PIN): pins.internal_gpio_output_pin_schema, cv.Required(CONF_ID): cv.declare_id(AcDimmer),
cv.Required(CONF_ZERO_CROSS_PIN): pins.internal_gpio_input_pin_schema, cv.Required(CONF_GATE_PIN): pins.internal_gpio_output_pin_schema,
cv.Optional(CONF_INIT_WITH_HALF_CYCLE, default=True): cv.boolean, cv.Required(CONF_ZERO_CROSS_PIN): pins.internal_gpio_input_pin_schema,
cv.Optional(CONF_METHOD, default='leading pulse'): cv.enum(DIM_METHODS, upper=True, space='_'), cv.Optional(CONF_INIT_WITH_HALF_CYCLE, default=True): cv.boolean,
}).extend(cv.COMPONENT_SCHEMA) cv.Optional(CONF_METHOD, default="leading pulse"): cv.enum(
DIM_METHODS, upper=True, space="_"
),
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config): def to_code(config):

View file

@ -5,18 +5,22 @@ from esphome.components.light.types import AddressableLightEffect
from esphome.components.light.effects import register_addressable_effect from esphome.components.light.effects import register_addressable_effect
from esphome.const import CONF_NAME, CONF_UART_ID from esphome.const import CONF_NAME, CONF_UART_ID
DEPENDENCIES = ['uart'] DEPENDENCIES = ["uart"]
adalight_ns = cg.esphome_ns.namespace('adalight') adalight_ns = cg.esphome_ns.namespace("adalight")
AdalightLightEffect = adalight_ns.class_( AdalightLightEffect = adalight_ns.class_(
'AdalightLightEffect', uart.UARTDevice, AddressableLightEffect) "AdalightLightEffect", uart.UARTDevice, AddressableLightEffect
)
CONFIG_SCHEMA = cv.Schema({}) CONFIG_SCHEMA = cv.Schema({})
@register_addressable_effect('adalight', AdalightLightEffect, "Adalight", { @register_addressable_effect(
cv.GenerateID(CONF_UART_ID): cv.use_id(uart.UARTComponent) "adalight",
}) AdalightLightEffect,
"Adalight",
{cv.GenerateID(CONF_UART_ID): cv.use_id(uart.UARTComponent)},
)
def adalight_light_effect_to_code(config, effect_id): def adalight_light_effect_to_code(config, effect_id):
effect = cg.new_Pvariable(effect_id, config[CONF_NAME]) effect = cg.new_Pvariable(effect_id, config[CONF_NAME])
yield uart.register_uart_device(effect, config) yield uart.register_uart_device(effect, config)

View file

@ -42,11 +42,11 @@ void AdalightLightEffect::reset_frame_(light::AddressableLight &it) {
void AdalightLightEffect::blank_all_leds_(light::AddressableLight &it) { void AdalightLightEffect::blank_all_leds_(light::AddressableLight &it) {
for (int led = it.size(); led-- > 0;) { for (int led = it.size(); led-- > 0;) {
it[led].set(light::ESPColor::BLACK); it[led].set(COLOR_BLACK);
} }
} }
void AdalightLightEffect::apply(light::AddressableLight &it, const light::ESPColor &current_color) { void AdalightLightEffect::apply(light::AddressableLight &it, const Color &current_color) {
const uint32_t now = millis(); const uint32_t now = millis();
if (now - this->last_ack_ >= ADALIGHT_ACK_INTERVAL) { if (now - this->last_ack_ >= ADALIGHT_ACK_INTERVAL) {
@ -130,7 +130,7 @@ AdalightLightEffect::Frame AdalightLightEffect::parse_frame_(light::AddressableL
for (int led = 0; led < accepted_led_count; led++, led_data += 3) { for (int led = 0; led < accepted_led_count; led++, led_data += 3) {
auto white = std::min(std::min(led_data[0], led_data[1]), led_data[2]); auto white = std::min(std::min(led_data[0], led_data[1]), led_data[2]);
it[led].set(light::ESPColor(led_data[0], led_data[1], led_data[2], white)); it[led].set(Color(led_data[0], led_data[1], led_data[2], white));
} }
return CONSUMED; return CONSUMED;

View file

@ -16,7 +16,7 @@ class AdalightLightEffect : public light::AddressableLightEffect, public uart::U
public: public:
void start() override; void start() override;
void stop() override; void stop() override;
void apply(light::AddressableLight &it, const light::ESPColor &current_color) override; void apply(light::AddressableLight &it, const Color &current_color) override;
protected: protected:
enum Frame { enum Frame {

View file

@ -1 +1 @@
CODEOWNERS = ['@esphome/core'] CODEOWNERS = ["@esphome/core"]

View file

@ -16,7 +16,9 @@ void ADCSensor::set_attenuation(adc_attenuation_t attenuation) { this->attenuati
void ADCSensor::setup() { void ADCSensor::setup() {
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str()); ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
#ifndef USE_ADC_SENSOR_VCC
GPIOPin(this->pin_, INPUT).setup(); GPIOPin(this->pin_, INPUT).setup();
#endif
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
analogSetPinAttenuation(this->pin_, this->attenuation_); analogSetPinAttenuation(this->pin_, this->attenuation_);

View file

@ -2,36 +2,51 @@ import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import pins from esphome import pins
from esphome.components import sensor, voltage_sampler from esphome.components import sensor, voltage_sampler
from esphome.const import CONF_ATTENUATION, CONF_ID, CONF_PIN, ICON_FLASH, UNIT_VOLT from esphome.const import (
CONF_ATTENUATION,
CONF_ID,
CONF_PIN,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
UNIT_VOLT,
)
AUTO_LOAD = ['voltage_sampler'] AUTO_LOAD = ["voltage_sampler"]
ATTENUATION_MODES = { ATTENUATION_MODES = {
'0db': cg.global_ns.ADC_0db, "0db": cg.global_ns.ADC_0db,
'2.5db': cg.global_ns.ADC_2_5db, "2.5db": cg.global_ns.ADC_2_5db,
'6db': cg.global_ns.ADC_6db, "6db": cg.global_ns.ADC_6db,
'11db': cg.global_ns.ADC_11db, "11db": cg.global_ns.ADC_11db,
} }
def validate_adc_pin(value): def validate_adc_pin(value):
vcc = str(value).upper() vcc = str(value).upper()
if vcc == 'VCC': if vcc == "VCC":
return cv.only_on_esp8266(vcc) return cv.only_on_esp8266(vcc)
return pins.analog_pin(value) return pins.analog_pin(value)
adc_ns = cg.esphome_ns.namespace('adc') adc_ns = cg.esphome_ns.namespace("adc")
ADCSensor = adc_ns.class_('ADCSensor', sensor.Sensor, cg.PollingComponent, ADCSensor = adc_ns.class_(
voltage_sampler.VoltageSampler) "ADCSensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
)
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2).extend({ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(ADCSensor), sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE)
cv.Required(CONF_PIN): validate_adc_pin, .extend(
cv.SplitDefault(CONF_ATTENUATION, esp32='0db'): {
cv.All(cv.only_on_esp32, cv.enum(ATTENUATION_MODES, lower=True)), cv.GenerateID(): cv.declare_id(ADCSensor),
}).extend(cv.polling_component_schema('60s')) cv.Required(CONF_PIN): validate_adc_pin,
cv.SplitDefault(CONF_ATTENUATION, esp32="0db"): cv.All(
cv.only_on_esp32, cv.enum(ATTENUATION_MODES, lower=True)
),
}
)
.extend(cv.polling_component_schema("60s"))
)
def to_code(config): def to_code(config):
@ -39,8 +54,8 @@ def to_code(config):
yield cg.register_component(var, config) yield cg.register_component(var, config)
yield sensor.register_sensor(var, config) yield sensor.register_sensor(var, config)
if config[CONF_PIN] == 'VCC': if config[CONF_PIN] == "VCC":
cg.add_define('USE_ADC_SENSOR_VCC') cg.add_define("USE_ADC_SENSOR_VCC")
else: else:
cg.add(var.set_pin(config[CONF_PIN])) cg.add(var.set_pin(config[CONF_PIN]))

View file

@ -0,0 +1,67 @@
#include "addressable_light_display.h"
#include "esphome/core/log.h"
namespace esphome {
namespace addressable_light {
static const char* TAG = "addressable_light.display";
int AddressableLightDisplay::get_width_internal() { return this->width_; }
int AddressableLightDisplay::get_height_internal() { return this->height_; }
void AddressableLightDisplay::setup() {
this->addressable_light_buffer_.resize(this->width_ * this->height_, {0, 0, 0, 0});
}
void AddressableLightDisplay::update() {
if (!this->enabled_)
return;
this->do_update_();
this->display();
}
void AddressableLightDisplay::display() {
bool dirty = false;
uint8_t old_r, old_g, old_b, old_w;
Color* c;
for (uint32_t offset = 0; offset < this->addressable_light_buffer_.size(); offset++) {
c = &(this->addressable_light_buffer_[offset]);
light::ESPColorView pixel = (*this->light_)[offset];
// Track the original values for the pixel view. If it has changed updating, then
// we trigger a redraw. Avoiding redraws == avoiding flicker!
old_r = pixel.get_red();
old_g = pixel.get_green();
old_b = pixel.get_blue();
old_w = pixel.get_white();
pixel.set_rgbw(c->r, c->g, c->b, c->w);
// If the actual value of the pixel changed, then schedule a redraw.
if (pixel.get_red() != old_r || pixel.get_green() != old_g || pixel.get_blue() != old_b ||
pixel.get_white() != old_w) {
dirty = true;
}
}
if (dirty) {
this->light_->schedule_show();
}
}
void HOT AddressableLightDisplay::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->pixel_mapper_f_.has_value()) {
// Params are passed by reference, so they may be modified in call.
this->addressable_light_buffer_[(*this->pixel_mapper_f_)(x, y)] = color;
} else {
this->addressable_light_buffer_[y * this->get_width_internal() + x] = color;
}
}
} // namespace addressable_light
} // namespace esphome

View file

@ -0,0 +1,59 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/color.h"
#include "esphome/components/display/display_buffer.h"
#include "esphome/components/light/addressable_light.h"
namespace esphome {
namespace addressable_light {
class AddressableLightDisplay : public display::DisplayBuffer, public PollingComponent {
public:
light::AddressableLight *get_light() const { return this->light_; }
void set_width(int32_t width) { width_ = width; }
void set_height(int32_t height) { height_ = height; }
void set_light(light::LightState *state) {
light_state_ = state;
light_ = static_cast<light::AddressableLight *>(state->get_output());
}
void set_enabled(bool enabled) {
if (light_state_) {
if (enabled_ && !enabled) { // enabled -> disabled
// - Tell the parent light to refresh, effectively wiping the display. Also
// restores the previous effect (if any).
light_state_->make_call().set_effect(this->last_effect_).perform();
} else if (!enabled_ && enabled) { // disabled -> enabled
// - Save the current effect.
this->last_effect_ = light_state_->get_effect_name();
// - Disable any current effect.
light_state_->make_call().set_effect(0).perform();
}
}
enabled_ = enabled;
}
bool get_enabled() { return enabled_; }
void set_pixel_mapper(std::function<int(int, int)> &&pixel_mapper_f) { this->pixel_mapper_f_ = pixel_mapper_f; }
void setup() override;
void display();
protected:
int get_width_internal() override;
int get_height_internal() override;
void draw_absolute_pixel_internal(int x, int y, Color color) override;
void update() override;
light::LightState *light_state_;
light::AddressableLight *light_;
bool enabled_{true};
int32_t width_;
int32_t height_;
std::vector<Color> addressable_light_buffer_;
optional<std::string> last_effect_;
optional<std::function<int(int, int)>> pixel_mapper_f_;
};
} // namespace addressable_light
} // namespace esphome

View file

@ -0,0 +1,63 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import display, light
from esphome.const import (
CONF_ID,
CONF_LAMBDA,
CONF_PAGES,
CONF_ADDRESSABLE_LIGHT_ID,
CONF_HEIGHT,
CONF_WIDTH,
CONF_UPDATE_INTERVAL,
CONF_PIXEL_MAPPER,
)
CODEOWNERS = ["@justfalter"]
addressable_light_ns = cg.esphome_ns.namespace("addressable_light")
AddressableLightDisplay = addressable_light_ns.class_(
"AddressableLightDisplay", display.DisplayBuffer, cg.PollingComponent
)
CONFIG_SCHEMA = cv.All(
display.FULL_DISPLAY_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(AddressableLightDisplay),
cv.Required(CONF_ADDRESSABLE_LIGHT_ID): cv.use_id(
light.AddressableLightState
),
cv.Required(CONF_WIDTH): cv.positive_int,
cv.Required(CONF_HEIGHT): cv.positive_int,
cv.Optional(
CONF_UPDATE_INTERVAL, default="16ms"
): cv.positive_time_period_milliseconds,
cv.Optional(CONF_PIXEL_MAPPER): cv.returning_lambda,
}
),
cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA),
)
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
wrapped_light = yield cg.get_variable(config[CONF_ADDRESSABLE_LIGHT_ID])
cg.add(var.set_width(config[CONF_WIDTH]))
cg.add(var.set_height(config[CONF_HEIGHT]))
cg.add(var.set_light(wrapped_light))
yield cg.register_component(var, config)
yield display.register_display(var, config)
if CONF_PIXEL_MAPPER in config:
pixel_mapper_template_ = yield cg.process_lambda(
config[CONF_PIXEL_MAPPER],
[(int, "x"), (int, "y")],
return_type=cg.int_,
)
cg.add(var.set_pixel_mapper(pixel_mapper_template_))
if CONF_LAMBDA in config:
lambda_ = yield cg.process_lambda(
config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void
)
cg.add(var.set_writer(lambda_))

View file

@ -2,29 +2,54 @@ import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor, i2c from esphome.components import sensor, i2c
from esphome import pins from esphome import pins
from esphome.const import CONF_ID, CONF_VOLTAGE, \ from esphome.const import (
UNIT_VOLT, ICON_FLASH, UNIT_AMPERE, UNIT_WATT CONF_ID,
CONF_VOLTAGE,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
UNIT_VOLT,
UNIT_AMPERE,
UNIT_WATT,
)
DEPENDENCIES = ['i2c'] DEPENDENCIES = ["i2c"]
ade7953_ns = cg.esphome_ns.namespace('ade7953') ade7953_ns = cg.esphome_ns.namespace("ade7953")
ADE7953 = ade7953_ns.class_('ADE7953', cg.PollingComponent, i2c.I2CDevice) ADE7953 = ade7953_ns.class_("ADE7953", cg.PollingComponent, i2c.I2CDevice)
CONF_IRQ_PIN = 'irq_pin' CONF_IRQ_PIN = "irq_pin"
CONF_CURRENT_A = 'current_a' CONF_CURRENT_A = "current_a"
CONF_CURRENT_B = 'current_b' CONF_CURRENT_B = "current_b"
CONF_ACTIVE_POWER_A = 'active_power_a' CONF_ACTIVE_POWER_A = "active_power_a"
CONF_ACTIVE_POWER_B = 'active_power_b' CONF_ACTIVE_POWER_B = "active_power_b"
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(ADE7953), cv.Schema(
cv.Optional(CONF_IRQ_PIN): pins.input_pin, {
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1), cv.GenerateID(): cv.declare_id(ADE7953),
cv.Optional(CONF_CURRENT_A): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2), cv.Optional(CONF_IRQ_PIN): pins.input_pin,
cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2), cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 1), UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE
cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 1), ),
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x38)) cv.Optional(CONF_CURRENT_A): sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT
),
cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT
),
cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER
),
cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x38))
)
def to_code(config): def to_code(config):
@ -35,10 +60,15 @@ def to_code(config):
if CONF_IRQ_PIN in config: if CONF_IRQ_PIN in config:
cg.add(var.set_irq_pin(config[CONF_IRQ_PIN])) cg.add(var.set_irq_pin(config[CONF_IRQ_PIN]))
for key in [CONF_VOLTAGE, CONF_CURRENT_A, CONF_CURRENT_B, CONF_ACTIVE_POWER_A, for key in [
CONF_ACTIVE_POWER_B]: CONF_VOLTAGE,
CONF_CURRENT_A,
CONF_CURRENT_B,
CONF_ACTIVE_POWER_A,
CONF_ACTIVE_POWER_B,
]:
if key not in config: if key not in config:
continue continue
conf = config[key] conf = config[key]
sens = yield sensor.new_sensor(conf) sens = yield sensor.new_sensor(conf)
cg.add(getattr(var, f'set_{key}_sensor')(sens)) cg.add(getattr(var, f"set_{key}_sensor")(sens))

View file

@ -3,18 +3,24 @@ import esphome.config_validation as cv
from esphome.components import i2c from esphome.components import i2c
from esphome.const import CONF_ID from esphome.const import CONF_ID
DEPENDENCIES = ['i2c'] DEPENDENCIES = ["i2c"]
AUTO_LOAD = ['sensor', 'voltage_sampler'] AUTO_LOAD = ["sensor", "voltage_sampler"]
MULTI_CONF = True MULTI_CONF = True
ads1115_ns = cg.esphome_ns.namespace('ads1115') ads1115_ns = cg.esphome_ns.namespace("ads1115")
ADS1115Component = ads1115_ns.class_('ADS1115Component', cg.Component, i2c.I2CDevice) ADS1115Component = ads1115_ns.class_("ADS1115Component", cg.Component, i2c.I2CDevice)
CONF_CONTINUOUS_MODE = 'continuous_mode' CONF_CONTINUOUS_MODE = "continuous_mode"
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(ADS1115Component), cv.Schema(
cv.Optional(CONF_CONTINUOUS_MODE, default=False): cv.boolean, {
}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(None)) cv.GenerateID(): cv.declare_id(ADS1115Component),
cv.Optional(CONF_CONTINUOUS_MODE, default=False): cv.boolean,
}
)
.extend(cv.COMPONENT_SCHEMA)
.extend(i2c.i2c_device_schema(None))
)
def to_code(config): def to_code(config):

View file

@ -1,53 +1,67 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor, voltage_sampler from esphome.components import sensor, voltage_sampler
from esphome.const import CONF_GAIN, CONF_MULTIPLEXER, ICON_FLASH, UNIT_VOLT, CONF_ID from esphome.const import (
CONF_GAIN,
CONF_MULTIPLEXER,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
UNIT_VOLT,
CONF_ID,
)
from . import ads1115_ns, ADS1115Component from . import ads1115_ns, ADS1115Component
DEPENDENCIES = ['ads1115'] DEPENDENCIES = ["ads1115"]
ADS1115Multiplexer = ads1115_ns.enum('ADS1115Multiplexer') ADS1115Multiplexer = ads1115_ns.enum("ADS1115Multiplexer")
MUX = { MUX = {
'A0_A1': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N1, "A0_A1": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N1,
'A0_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N3, "A0_A3": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N3,
'A1_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_N3, "A1_A3": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_N3,
'A2_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_N3, "A2_A3": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_N3,
'A0_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_NG, "A0_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_NG,
'A1_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_NG, "A1_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_NG,
'A2_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_NG, "A2_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_NG,
'A3_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P3_NG, "A3_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P3_NG,
} }
ADS1115Gain = ads1115_ns.enum('ADS1115Gain') ADS1115Gain = ads1115_ns.enum("ADS1115Gain")
GAIN = { GAIN = {
'6.144': ADS1115Gain.ADS1115_GAIN_6P144, "6.144": ADS1115Gain.ADS1115_GAIN_6P144,
'4.096': ADS1115Gain.ADS1115_GAIN_4P096, "4.096": ADS1115Gain.ADS1115_GAIN_4P096,
'2.048': ADS1115Gain.ADS1115_GAIN_2P048, "2.048": ADS1115Gain.ADS1115_GAIN_2P048,
'1.024': ADS1115Gain.ADS1115_GAIN_1P024, "1.024": ADS1115Gain.ADS1115_GAIN_1P024,
'0.512': ADS1115Gain.ADS1115_GAIN_0P512, "0.512": ADS1115Gain.ADS1115_GAIN_0P512,
'0.256': ADS1115Gain.ADS1115_GAIN_0P256, "0.256": ADS1115Gain.ADS1115_GAIN_0P256,
} }
def validate_gain(value): def validate_gain(value):
if isinstance(value, float): if isinstance(value, float):
value = f'{value:0.03f}' value = f"{value:0.03f}"
elif not isinstance(value, str): elif not isinstance(value, str):
raise cv.Invalid(f'invalid gain "{value}"') raise cv.Invalid(f'invalid gain "{value}"')
return cv.enum(GAIN)(value) return cv.enum(GAIN)(value)
ADS1115Sensor = ads1115_ns.class_('ADS1115Sensor', sensor.Sensor, cg.PollingComponent, ADS1115Sensor = ads1115_ns.class_(
voltage_sampler.VoltageSampler) "ADS1115Sensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
)
CONF_ADS1115_ID = 'ads1115_id' CONF_ADS1115_ID = "ads1115_id"
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 3).extend({ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(ADS1115Sensor), sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE)
cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component), .extend(
cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space='_'), {
cv.Required(CONF_GAIN): validate_gain, cv.GenerateID(): cv.declare_id(ADS1115Sensor),
}).extend(cv.polling_component_schema('60s')) cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component),
cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space="_"),
cv.Required(CONF_GAIN): validate_gain,
}
)
.extend(cv.polling_component_schema("60s"))
)
def to_code(config): def to_code(config):

View file

@ -1,19 +1,37 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import i2c, sensor from esphome.components import i2c, sensor
from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, \ from esphome.const import (
UNIT_CELSIUS, ICON_THERMOMETER, ICON_WATER_PERCENT, UNIT_PERCENT CONF_HUMIDITY,
CONF_ID,
CONF_TEMPERATURE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
UNIT_CELSIUS,
UNIT_PERCENT,
)
DEPENDENCIES = ['i2c'] DEPENDENCIES = ["i2c"]
aht10_ns = cg.esphome_ns.namespace('aht10') aht10_ns = cg.esphome_ns.namespace("aht10")
AHT10Component = aht10_ns.class_('AHT10Component', cg.PollingComponent, i2c.I2CDevice) AHT10Component = aht10_ns.class_("AHT10Component", cg.PollingComponent, i2c.I2CDevice)
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(AHT10Component), cv.Schema(
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 2), {
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 2), cv.GenerateID(): cv.declare_id(AHT10Component),
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x38)) cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 2, DEVICE_CLASS_TEMPERATURE
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT, ICON_EMPTY, 2, DEVICE_CLASS_HUMIDITY
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x38))
)
def to_code(config): def to_code(config):

View file

@ -1,19 +1,39 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import i2c, sensor from esphome.components import i2c, sensor
from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, \ from esphome.const import (
UNIT_CELSIUS, ICON_THERMOMETER, ICON_WATER_PERCENT, UNIT_PERCENT CONF_HUMIDITY,
CONF_ID,
CONF_TEMPERATURE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
UNIT_CELSIUS,
ICON_EMPTY,
UNIT_PERCENT,
)
DEPENDENCIES = ['i2c'] DEPENDENCIES = ["i2c"]
am2320_ns = cg.esphome_ns.namespace('am2320') am2320_ns = cg.esphome_ns.namespace("am2320")
AM2320Component = am2320_ns.class_('AM2320Component', cg.PollingComponent, i2c.I2CDevice) AM2320Component = am2320_ns.class_(
"AM2320Component", cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(AM2320Component), cv.Schema(
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), {
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), cv.GenerateID(): cv.declare_id(AM2320Component),
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x5C)) cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x5C))
)
def to_code(config): def to_code(config):

View file

@ -10,24 +10,28 @@ from esphome.core import CORE, HexInt
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['display'] DEPENDENCIES = ["display"]
MULTI_CONF = True MULTI_CONF = True
Animation_ = display.display_ns.class_('Animation') Animation_ = display.display_ns.class_("Animation")
CONF_RAW_DATA_ID = 'raw_data_id' CONF_RAW_DATA_ID = "raw_data_id"
ANIMATION_SCHEMA = cv.Schema({ ANIMATION_SCHEMA = cv.Schema(
cv.Required(CONF_ID): cv.declare_id(Animation_), {
cv.Required(CONF_FILE): cv.file_, cv.Required(CONF_ID): cv.declare_id(Animation_),
cv.Optional(CONF_RESIZE): cv.dimensions, cv.Required(CONF_FILE): cv.file_,
cv.Optional(CONF_TYPE, default='BINARY'): cv.enum(espImage.IMAGE_TYPE, upper=True), cv.Optional(CONF_RESIZE): cv.dimensions,
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), cv.Optional(CONF_TYPE, default="BINARY"): cv.enum(
}) espImage.IMAGE_TYPE, upper=True
),
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
}
)
CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, ANIMATION_SCHEMA) CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, ANIMATION_SCHEMA)
CODEOWNERS = ['@syndlex'] CODEOWNERS = ["@syndlex"]
def to_code(config): def to_code(config):
@ -46,26 +50,28 @@ def to_code(config):
width, height = image.size width, height = image.size
else: else:
if width > 500 or height > 500: if width > 500 or height > 500:
_LOGGER.warning("The image you requested is very big. Please consider using" _LOGGER.warning(
" the resize parameter.") "The image you requested is very big. Please consider using"
" the resize parameter."
)
if config[CONF_TYPE] == 'GRAYSCALE': if config[CONF_TYPE] == "GRAYSCALE":
data = [0 for _ in range(height * width * frames)] data = [0 for _ in range(height * width * frames)]
pos = 0 pos = 0
for frameIndex in range(frames): for frameIndex in range(frames):
image.seek(frameIndex) image.seek(frameIndex)
frame = image.convert('L', dither=Image.NONE) frame = image.convert("L", dither=Image.NONE)
pixels = list(frame.getdata()) pixels = list(frame.getdata())
for pix in pixels: for pix in pixels:
data[pos] = pix data[pos] = pix
pos += 1 pos += 1
elif config[CONF_TYPE] == 'RGB24': elif config[CONF_TYPE] == "RGB24":
data = [0 for _ in range(height * width * 3 * frames)] data = [0 for _ in range(height * width * 3 * frames)]
pos = 0 pos = 0
for frameIndex in range(frames): for frameIndex in range(frames):
image.seek(frameIndex) image.seek(frameIndex)
frame = image.convert('RGB') frame = image.convert("RGB")
pixels = list(frame.getdata()) pixels = list(frame.getdata())
for pix in pixels: for pix in pixels:
data[pos] = pix[0] data[pos] = pix[0]
@ -75,12 +81,12 @@ def to_code(config):
data[pos] = pix[2] data[pos] = pix[2]
pos += 1 pos += 1
elif config[CONF_TYPE] == 'BINARY': elif config[CONF_TYPE] == "BINARY":
width8 = ((width + 7) // 8) * 8 width8 = ((width + 7) // 8) * 8
data = [0 for _ in range((height * width8 // 8) * frames)] data = [0 for _ in range((height * width8 // 8) * frames)]
for frameIndex in range(frames): for frameIndex in range(frames):
image.seek(frameIndex) image.seek(frameIndex)
frame = image.convert('1', dither=Image.NONE) frame = image.convert("1", dither=Image.NONE)
for y in range(height): for y in range(height):
for x in range(width): for x in range(width):
if frame.getpixel((x, y)): if frame.getpixel((x, y)):
@ -90,5 +96,11 @@ def to_code(config):
rhs = [HexInt(x) for x in data] rhs = [HexInt(x) for x in data]
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
cg.new_Pvariable(config[CONF_ID], prog_arr, width, height, frames, cg.new_Pvariable(
espImage.IMAGE_TYPE[config[CONF_TYPE]]) config[CONF_ID],
prog_arr,
width,
height,
frames,
espImage.IMAGE_TYPE[config[CONF_TYPE]],
)

View file

@ -3,18 +3,24 @@ import esphome.config_validation as cv
from esphome.components import i2c from esphome.components import i2c
from esphome.const import CONF_ID from esphome.const import CONF_ID
DEPENDENCIES = ['i2c'] DEPENDENCIES = ["i2c"]
AUTO_LOAD = ['sensor', 'binary_sensor'] AUTO_LOAD = ["sensor", "binary_sensor"]
MULTI_CONF = True MULTI_CONF = True
CONF_APDS9960_ID = 'apds9960_id' CONF_APDS9960_ID = "apds9960_id"
apds9960_nds = cg.esphome_ns.namespace('apds9960') apds9960_nds = cg.esphome_ns.namespace("apds9960")
APDS9960 = apds9960_nds.class_('APDS9960', cg.PollingComponent, i2c.I2CDevice) APDS9960 = apds9960_nds.class_("APDS9960", cg.PollingComponent, i2c.I2CDevice)
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(APDS9960), cv.Schema(
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x39)) {
cv.GenerateID(): cv.declare_id(APDS9960),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x39))
)
def to_code(config): def to_code(config):

View file

@ -4,20 +4,24 @@ from esphome.components import binary_sensor
from esphome.const import CONF_DIRECTION, CONF_DEVICE_CLASS, DEVICE_CLASS_MOVING from esphome.const import CONF_DIRECTION, CONF_DEVICE_CLASS, DEVICE_CLASS_MOVING
from . import APDS9960, CONF_APDS9960_ID from . import APDS9960, CONF_APDS9960_ID
DEPENDENCIES = ['apds9960'] DEPENDENCIES = ["apds9960"]
DIRECTIONS = { DIRECTIONS = {
'UP': 'set_up_direction', "UP": "set_up_direction",
'DOWN': 'set_down_direction', "DOWN": "set_down_direction",
'LEFT': 'set_left_direction', "LEFT": "set_left_direction",
'RIGHT': 'set_right_direction', "RIGHT": "set_right_direction",
} }
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, upper=True), {
cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960), cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, upper=True),
cv.Optional(CONF_DEVICE_CLASS, default=DEVICE_CLASS_MOVING): binary_sensor.device_class, cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960),
}) cv.Optional(
CONF_DEVICE_CLASS, default=DEVICE_CLASS_MOVING
): binary_sensor.device_class,
}
)
def to_code(config): def to_code(config):

View file

@ -1,23 +1,27 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor from esphome.components import sensor
from esphome.const import CONF_TYPE, UNIT_PERCENT, ICON_LIGHTBULB from esphome.const import CONF_TYPE, DEVICE_CLASS_EMPTY, UNIT_PERCENT, ICON_LIGHTBULB
from . import APDS9960, CONF_APDS9960_ID from . import APDS9960, CONF_APDS9960_ID
DEPENDENCIES = ['apds9960'] DEPENDENCIES = ["apds9960"]
TYPES = { TYPES = {
'CLEAR': 'set_clear_channel', "CLEAR": "set_clear_channel",
'RED': 'set_red_channel', "RED": "set_red_channel",
'GREEN': 'set_green_channel', "GREEN": "set_green_channel",
'BLUE': 'set_blue_channel', "BLUE": "set_blue_channel",
'PROXIMITY': 'set_proximity', "PROXIMITY": "set_proximity",
} }
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PERCENT, ICON_LIGHTBULB, 1).extend({ CONFIG_SCHEMA = sensor.sensor_schema(
cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True), UNIT_PERCENT, ICON_LIGHTBULB, 1, DEVICE_CLASS_EMPTY
cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960), ).extend(
}) {
cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True),
cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960),
}
)
def to_code(config): def to_code(config):

View file

@ -2,46 +2,69 @@ import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import automation from esphome import automation
from esphome.automation import Condition from esphome.automation import Condition
from esphome.const import CONF_DATA, CONF_DATA_TEMPLATE, CONF_ID, CONF_PASSWORD, CONF_PORT, \ from esphome.const import (
CONF_REBOOT_TIMEOUT, CONF_SERVICE, CONF_VARIABLES, CONF_SERVICES, CONF_TRIGGER_ID, CONF_EVENT, \ CONF_DATA,
CONF_TAG CONF_DATA_TEMPLATE,
CONF_ID,
CONF_PASSWORD,
CONF_PORT,
CONF_REBOOT_TIMEOUT,
CONF_SERVICE,
CONF_VARIABLES,
CONF_SERVICES,
CONF_TRIGGER_ID,
CONF_EVENT,
CONF_TAG,
)
from esphome.core import coroutine_with_priority from esphome.core import coroutine_with_priority
DEPENDENCIES = ['network'] DEPENDENCIES = ["network"]
AUTO_LOAD = ['async_tcp'] AUTO_LOAD = ["async_tcp"]
CODEOWNERS = ['@OttoWinter'] CODEOWNERS = ["@OttoWinter"]
api_ns = cg.esphome_ns.namespace('api') api_ns = cg.esphome_ns.namespace("api")
APIServer = api_ns.class_('APIServer', cg.Component, cg.Controller) APIServer = api_ns.class_("APIServer", cg.Component, cg.Controller)
HomeAssistantServiceCallAction = api_ns.class_('HomeAssistantServiceCallAction', automation.Action) HomeAssistantServiceCallAction = api_ns.class_(
APIConnectedCondition = api_ns.class_('APIConnectedCondition', Condition) "HomeAssistantServiceCallAction", automation.Action
)
APIConnectedCondition = api_ns.class_("APIConnectedCondition", Condition)
UserServiceTrigger = api_ns.class_('UserServiceTrigger', automation.Trigger) UserServiceTrigger = api_ns.class_("UserServiceTrigger", automation.Trigger)
ListEntitiesServicesArgument = api_ns.class_('ListEntitiesServicesArgument') ListEntitiesServicesArgument = api_ns.class_("ListEntitiesServicesArgument")
SERVICE_ARG_NATIVE_TYPES = { SERVICE_ARG_NATIVE_TYPES = {
'bool': bool, "bool": bool,
'int': cg.int32, "int": cg.int32,
'float': float, "float": float,
'string': cg.std_string, "string": cg.std_string,
'bool[]': cg.std_vector.template(bool), "bool[]": cg.std_vector.template(bool),
'int[]': cg.std_vector.template(cg.int32), "int[]": cg.std_vector.template(cg.int32),
'float[]': cg.std_vector.template(float), "float[]": cg.std_vector.template(float),
'string[]': cg.std_vector.template(cg.std_string), "string[]": cg.std_vector.template(cg.std_string),
} }
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(APIServer), {
cv.Optional(CONF_PORT, default=6053): cv.port, cv.GenerateID(): cv.declare_id(APIServer),
cv.Optional(CONF_PASSWORD, default=''): cv.string_strict, cv.Optional(CONF_PORT, default=6053): cv.port,
cv.Optional(CONF_REBOOT_TIMEOUT, default='15min'): cv.positive_time_period_milliseconds, cv.Optional(CONF_PASSWORD, default=""): cv.string_strict,
cv.Optional(CONF_SERVICES): automation.validate_automation({ cv.Optional(
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UserServiceTrigger), CONF_REBOOT_TIMEOUT, default="15min"
cv.Required(CONF_SERVICE): cv.valid_name, ): cv.positive_time_period_milliseconds,
cv.Optional(CONF_VARIABLES, default={}): cv.Schema({ cv.Optional(CONF_SERVICES): automation.validate_automation(
cv.validate_id_name: cv.one_of(*SERVICE_ARG_NATIVE_TYPES, lower=True), {
}), cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UserServiceTrigger),
}), cv.Required(CONF_SERVICE): cv.valid_name,
}).extend(cv.COMPONENT_SCHEMA) cv.Optional(CONF_VARIABLES, default={}): cv.Schema(
{
cv.validate_id_name: cv.one_of(
*SERVICE_ARG_NATIVE_TYPES, lower=True
),
}
),
}
),
}
).extend(cv.COMPONENT_SCHEMA)
@coroutine_with_priority(40.0) @coroutine_with_priority(40.0)
@ -63,28 +86,36 @@ def to_code(config):
func_args.append((native, name)) func_args.append((native, name))
service_arg_names.append(name) service_arg_names.append(name)
templ = cg.TemplateArguments(*template_args) templ = cg.TemplateArguments(*template_args)
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], templ, trigger = cg.new_Pvariable(
conf[CONF_SERVICE], service_arg_names) conf[CONF_TRIGGER_ID], templ, conf[CONF_SERVICE], service_arg_names
)
cg.add(var.register_user_service(trigger)) cg.add(var.register_user_service(trigger))
yield automation.build_automation(trigger, func_args, conf) yield automation.build_automation(trigger, func_args, conf)
cg.add_define('USE_API') cg.add_define("USE_API")
cg.add_global(api_ns.using) cg.add_global(api_ns.using)
KEY_VALUE_SCHEMA = cv.Schema({cv.string: cv.templatable(cv.string_strict)}) KEY_VALUE_SCHEMA = cv.Schema({cv.string: cv.templatable(cv.string_strict)})
HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema({ HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema(
cv.GenerateID(): cv.use_id(APIServer), {
cv.Required(CONF_SERVICE): cv.templatable(cv.string), cv.GenerateID(): cv.use_id(APIServer),
cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA, cv.Required(CONF_SERVICE): cv.templatable(cv.string),
cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA, cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA,
cv.Optional(CONF_VARIABLES, default={}): cv.Schema({cv.string: cv.returning_lambda}), cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA,
}) cv.Optional(CONF_VARIABLES, default={}): cv.Schema(
{cv.string: cv.returning_lambda}
),
}
)
@automation.register_action('homeassistant.service', HomeAssistantServiceCallAction, @automation.register_action(
HOMEASSISTANT_SERVICE_ACTION_SCHEMA) "homeassistant.service",
HomeAssistantServiceCallAction,
HOMEASSISTANT_SERVICE_ACTION_SCHEMA,
)
def homeassistant_service_to_code(config, action_id, template_arg, args): def homeassistant_service_to_code(config, action_id, template_arg, args):
serv = yield cg.get_variable(config[CONF_ID]) serv = yield cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, serv, False) var = cg.new_Pvariable(action_id, template_arg, serv, False)
@ -104,23 +135,30 @@ def homeassistant_service_to_code(config, action_id, template_arg, args):
def validate_homeassistant_event(value): def validate_homeassistant_event(value):
value = cv.string(value) value = cv.string(value)
if not value.startswith('esphome.'): if not value.startswith("esphome."):
raise cv.Invalid("ESPHome can only generate Home Assistant events that begin with " raise cv.Invalid(
"esphome. For example 'esphome.xyz'") "ESPHome can only generate Home Assistant events that begin with "
"esphome. For example 'esphome.xyz'"
)
return value return value
HOMEASSISTANT_EVENT_ACTION_SCHEMA = cv.Schema({ HOMEASSISTANT_EVENT_ACTION_SCHEMA = cv.Schema(
cv.GenerateID(): cv.use_id(APIServer), {
cv.Required(CONF_EVENT): validate_homeassistant_event, cv.GenerateID(): cv.use_id(APIServer),
cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA, cv.Required(CONF_EVENT): validate_homeassistant_event,
cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA, cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA,
cv.Optional(CONF_VARIABLES, default={}): KEY_VALUE_SCHEMA, cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA,
}) cv.Optional(CONF_VARIABLES, default={}): KEY_VALUE_SCHEMA,
}
)
@automation.register_action('homeassistant.event', HomeAssistantServiceCallAction, @automation.register_action(
HOMEASSISTANT_EVENT_ACTION_SCHEMA) "homeassistant.event",
HomeAssistantServiceCallAction,
HOMEASSISTANT_EVENT_ACTION_SCHEMA,
)
def homeassistant_event_to_code(config, action_id, template_arg, args): def homeassistant_event_to_code(config, action_id, template_arg, args):
serv = yield cg.get_variable(config[CONF_ID]) serv = yield cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, serv, True) var = cg.new_Pvariable(action_id, template_arg, serv, True)
@ -138,23 +176,29 @@ def homeassistant_event_to_code(config, action_id, template_arg, args):
yield var yield var
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value({ HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value(
cv.GenerateID(): cv.use_id(APIServer), {
cv.Required(CONF_TAG): cv.templatable(cv.string_strict), cv.GenerateID(): cv.use_id(APIServer),
}, key=CONF_TAG) cv.Required(CONF_TAG): cv.templatable(cv.string_strict),
},
key=CONF_TAG,
)
@automation.register_action('homeassistant.tag_scanned', HomeAssistantServiceCallAction, @automation.register_action(
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA) "homeassistant.tag_scanned",
HomeAssistantServiceCallAction,
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA,
)
def homeassistant_tag_scanned_to_code(config, action_id, template_arg, args): def homeassistant_tag_scanned_to_code(config, action_id, template_arg, args):
serv = yield cg.get_variable(config[CONF_ID]) serv = yield cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, serv, True) var = cg.new_Pvariable(action_id, template_arg, serv, True)
cg.add(var.set_service('esphome.tag_scanned')) cg.add(var.set_service("esphome.tag_scanned"))
templ = yield cg.templatable(config[CONF_TAG], args, cg.std_string) templ = yield cg.templatable(config[CONF_TAG], args, cg.std_string)
cg.add(var.add_data('tag_id', templ)) cg.add(var.add_data("tag_id", templ))
yield var yield var
@automation.register_condition('api.connected', APIConnectedCondition, {}) @automation.register_condition("api.connected", APIConnectedCondition, {})
def api_connected_to_code(config, condition_id, template_arg, args): def api_connected_to_code(config, condition_id, template_arg, args):
yield cg.new_Pvariable(condition_id, template_arg) yield cg.new_Pvariable(condition_id, template_arg)

View file

@ -46,6 +46,7 @@ service APIConnection {
// The Home Assistant protocol is structured as a simple // The Home Assistant protocol is structured as a simple
// TCP socket with short binary messages encoded in the protocol buffers format // TCP socket with short binary messages encoded in the protocol buffers format
// First, a message in this protocol has a specific format: // First, a message in this protocol has a specific format:
// * A zero byte.
// * VarInt denoting the size of the message object. (type is not part of this) // * VarInt denoting the size of the message object. (type is not part of this)
// * VarInt denoting the type of message. // * VarInt denoting the type of message.
// * The message object encoded as a ProtoBuf message // * The message object encoded as a ProtoBuf message
@ -302,6 +303,7 @@ message ListEntitiesFanResponse {
bool supports_oscillation = 5; bool supports_oscillation = 5;
bool supports_speed = 6; bool supports_speed = 6;
bool supports_direction = 7; bool supports_direction = 7;
int32 supported_speed_count = 8;
} }
enum FanSpeed { enum FanSpeed {
FAN_SPEED_LOW = 0; FAN_SPEED_LOW = 0;
@ -321,8 +323,9 @@ message FanStateResponse {
fixed32 key = 1; fixed32 key = 1;
bool state = 2; bool state = 2;
bool oscillating = 3; bool oscillating = 3;
FanSpeed speed = 4; FanSpeed speed = 4 [deprecated = true];
FanDirection direction = 5; FanDirection direction = 5;
int32 speed_level = 6;
} }
message FanCommandRequest { message FanCommandRequest {
option (id) = 31; option (id) = 31;
@ -333,12 +336,14 @@ message FanCommandRequest {
fixed32 key = 1; fixed32 key = 1;
bool has_state = 2; bool has_state = 2;
bool state = 3; bool state = 3;
bool has_speed = 4; bool has_speed = 4 [deprecated = true];
FanSpeed speed = 5; FanSpeed speed = 5 [deprecated = true];
bool has_oscillating = 6; bool has_oscillating = 6;
bool oscillating = 7; bool oscillating = 7;
bool has_direction = 8; bool has_direction = 8;
FanDirection direction = 9; FanDirection direction = 9;
bool has_speed_level = 10;
int32 speed_level = 11;
} }
// ==================== LIGHT ==================== // ==================== LIGHT ====================
@ -418,6 +423,7 @@ message ListEntitiesSensorResponse {
string unit_of_measurement = 6; string unit_of_measurement = 6;
int32 accuracy_decimals = 7; int32 accuracy_decimals = 7;
bool force_update = 8; bool force_update = 8;
string device_class = 9;
} }
message SensorStateResponse { message SensorStateResponse {
option (id) = 25; option (id) = 25;

View file

@ -9,6 +9,9 @@
#ifdef USE_HOMEASSISTANT_TIME #ifdef USE_HOMEASSISTANT_TIME
#include "esphome/components/homeassistant/time/homeassistant_time.h" #include "esphome/components/homeassistant/time/homeassistant_time.h"
#endif #endif
#ifdef USE_FAN
#include "esphome/components/fan/fan_helpers.h"
#endif
namespace esphome { namespace esphome {
namespace api { namespace api {
@ -246,8 +249,10 @@ bool APIConnection::send_fan_state(fan::FanState *fan) {
resp.state = fan->state; resp.state = fan->state;
if (traits.supports_oscillation()) if (traits.supports_oscillation())
resp.oscillating = fan->oscillating; resp.oscillating = fan->oscillating;
if (traits.supports_speed()) if (traits.supports_speed()) {
resp.speed = static_cast<enums::FanSpeed>(fan->speed); resp.speed_level = fan->speed;
resp.speed = static_cast<enums::FanSpeed>(fan::speed_level_to_enum(fan->speed, traits.supported_speed_count()));
}
if (traits.supports_direction()) if (traits.supports_direction())
resp.direction = static_cast<enums::FanDirection>(fan->direction); resp.direction = static_cast<enums::FanDirection>(fan->direction);
return this->send_fan_state_response(resp); return this->send_fan_state_response(resp);
@ -262,6 +267,7 @@ bool APIConnection::send_fan_info(fan::FanState *fan) {
msg.supports_oscillation = traits.supports_oscillation(); msg.supports_oscillation = traits.supports_oscillation();
msg.supports_speed = traits.supports_speed(); msg.supports_speed = traits.supports_speed();
msg.supports_direction = traits.supports_direction(); msg.supports_direction = traits.supports_direction();
msg.supported_speed_count = traits.supported_speed_count();
return this->send_list_entities_fan_response(msg); return this->send_list_entities_fan_response(msg);
} }
void APIConnection::fan_command(const FanCommandRequest &msg) { void APIConnection::fan_command(const FanCommandRequest &msg) {
@ -269,13 +275,19 @@ void APIConnection::fan_command(const FanCommandRequest &msg) {
if (fan == nullptr) if (fan == nullptr)
return; return;
auto traits = fan->get_traits();
auto call = fan->make_call(); auto call = fan->make_call();
if (msg.has_state) if (msg.has_state)
call.set_state(msg.state); call.set_state(msg.state);
if (msg.has_oscillating) if (msg.has_oscillating)
call.set_oscillating(msg.oscillating); call.set_oscillating(msg.oscillating);
if (msg.has_speed) if (msg.has_speed_level) {
call.set_speed(static_cast<fan::FanSpeed>(msg.speed)); // Prefer level
call.set_speed(msg.speed_level);
} else if (msg.has_speed) {
call.set_speed(fan::speed_enum_to_level(static_cast<fan::FanSpeed>(msg.speed), traits.supported_speed_count()));
}
if (msg.has_direction) if (msg.has_direction)
call.set_direction(static_cast<fan::FanDirection>(msg.direction)); call.set_direction(static_cast<fan::FanDirection>(msg.direction));
call.perform(); call.perform();
@ -382,6 +394,7 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
msg.unit_of_measurement = sensor->get_unit_of_measurement(); msg.unit_of_measurement = sensor->get_unit_of_measurement();
msg.accuracy_decimals = sensor->get_accuracy_decimals(); msg.accuracy_decimals = sensor->get_accuracy_decimals();
msg.force_update = sensor->get_force_update(); msg.force_update = sensor->get_force_update();
msg.device_class = sensor->get_device_class();
return this->send_list_entities_sensor_response(msg); return this->send_list_entities_sensor_response(msg);
} }
#endif #endif
@ -589,7 +602,7 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
HelloResponse resp; HelloResponse resp;
resp.api_version_major = 1; resp.api_version_major = 1;
resp.api_version_minor = 3; resp.api_version_minor = 4;
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")"; resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
this->connection_state_ = ConnectionState::CONNECTED; this->connection_state_ = ConnectionState::CONNECTED;
return resp; return resp;

View file

@ -774,6 +774,10 @@ bool ListEntitiesFanResponse::decode_varint(uint32_t field_id, ProtoVarInt value
this->supports_direction = value.as_bool(); this->supports_direction = value.as_bool();
return true; return true;
} }
case 8: {
this->supported_speed_count = value.as_int32();
return true;
}
default: default:
return false; return false;
} }
@ -814,6 +818,7 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(5, this->supports_oscillation); buffer.encode_bool(5, this->supports_oscillation);
buffer.encode_bool(6, this->supports_speed); buffer.encode_bool(6, this->supports_speed);
buffer.encode_bool(7, this->supports_direction); buffer.encode_bool(7, this->supports_direction);
buffer.encode_int32(8, this->supported_speed_count);
} }
void ListEntitiesFanResponse::dump_to(std::string &out) const { void ListEntitiesFanResponse::dump_to(std::string &out) const {
char buffer[64]; char buffer[64];
@ -846,6 +851,11 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const {
out.append(" supports_direction: "); out.append(" supports_direction: ");
out.append(YESNO(this->supports_direction)); out.append(YESNO(this->supports_direction));
out.append("\n"); out.append("\n");
out.append(" supported_speed_count: ");
sprintf(buffer, "%d", this->supported_speed_count);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
bool FanStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { bool FanStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
@ -866,6 +876,10 @@ bool FanStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
this->direction = value.as_enum<enums::FanDirection>(); this->direction = value.as_enum<enums::FanDirection>();
return true; return true;
} }
case 6: {
this->speed_level = value.as_int32();
return true;
}
default: default:
return false; return false;
} }
@ -886,6 +900,7 @@ void FanStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(3, this->oscillating); buffer.encode_bool(3, this->oscillating);
buffer.encode_enum<enums::FanSpeed>(4, this->speed); buffer.encode_enum<enums::FanSpeed>(4, this->speed);
buffer.encode_enum<enums::FanDirection>(5, this->direction); buffer.encode_enum<enums::FanDirection>(5, this->direction);
buffer.encode_int32(6, this->speed_level);
} }
void FanStateResponse::dump_to(std::string &out) const { void FanStateResponse::dump_to(std::string &out) const {
char buffer[64]; char buffer[64];
@ -910,6 +925,11 @@ void FanStateResponse::dump_to(std::string &out) const {
out.append(" direction: "); out.append(" direction: ");
out.append(proto_enum_to_string<enums::FanDirection>(this->direction)); out.append(proto_enum_to_string<enums::FanDirection>(this->direction));
out.append("\n"); out.append("\n");
out.append(" speed_level: ");
sprintf(buffer, "%d", this->speed_level);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) { bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
@ -946,6 +966,14 @@ bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
this->direction = value.as_enum<enums::FanDirection>(); this->direction = value.as_enum<enums::FanDirection>();
return true; return true;
} }
case 10: {
this->has_speed_level = value.as_bool();
return true;
}
case 11: {
this->speed_level = value.as_int32();
return true;
}
default: default:
return false; return false;
} }
@ -970,6 +998,8 @@ void FanCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(7, this->oscillating); buffer.encode_bool(7, this->oscillating);
buffer.encode_bool(8, this->has_direction); buffer.encode_bool(8, this->has_direction);
buffer.encode_enum<enums::FanDirection>(9, this->direction); buffer.encode_enum<enums::FanDirection>(9, this->direction);
buffer.encode_bool(10, this->has_speed_level);
buffer.encode_int32(11, this->speed_level);
} }
void FanCommandRequest::dump_to(std::string &out) const { void FanCommandRequest::dump_to(std::string &out) const {
char buffer[64]; char buffer[64];
@ -1010,6 +1040,15 @@ void FanCommandRequest::dump_to(std::string &out) const {
out.append(" direction: "); out.append(" direction: ");
out.append(proto_enum_to_string<enums::FanDirection>(this->direction)); out.append(proto_enum_to_string<enums::FanDirection>(this->direction));
out.append("\n"); out.append("\n");
out.append(" has_speed_level: ");
out.append(YESNO(this->has_speed_level));
out.append("\n");
out.append(" speed_level: ");
sprintf(buffer, "%d", this->speed_level);
out.append(buffer);
out.append("\n");
out.append("}"); out.append("}");
} }
bool ListEntitiesLightResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { bool ListEntitiesLightResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
@ -1494,6 +1533,10 @@ bool ListEntitiesSensorResponse::decode_length(uint32_t field_id, ProtoLengthDel
this->unit_of_measurement = value.as_string(); this->unit_of_measurement = value.as_string();
return true; return true;
} }
case 9: {
this->device_class = value.as_string();
return true;
}
default: default:
return false; return false;
} }
@ -1517,6 +1560,7 @@ void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(6, this->unit_of_measurement); buffer.encode_string(6, this->unit_of_measurement);
buffer.encode_int32(7, this->accuracy_decimals); buffer.encode_int32(7, this->accuracy_decimals);
buffer.encode_bool(8, this->force_update); buffer.encode_bool(8, this->force_update);
buffer.encode_string(9, this->device_class);
} }
void ListEntitiesSensorResponse::dump_to(std::string &out) const { void ListEntitiesSensorResponse::dump_to(std::string &out) const {
char buffer[64]; char buffer[64];
@ -1554,6 +1598,10 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const {
out.append(" force_update: "); out.append(" force_update: ");
out.append(YESNO(this->force_update)); out.append(YESNO(this->force_update));
out.append("\n"); out.append("\n");
out.append(" device_class: ");
out.append("'").append(this->device_class).append("'");
out.append("\n");
out.append("}"); out.append("}");
} }
bool SensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { bool SensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {

View file

@ -284,6 +284,7 @@ class ListEntitiesFanResponse : public ProtoMessage {
bool supports_oscillation{false}; // NOLINT bool supports_oscillation{false}; // NOLINT
bool supports_speed{false}; // NOLINT bool supports_speed{false}; // NOLINT
bool supports_direction{false}; // NOLINT bool supports_direction{false}; // NOLINT
int32_t supported_speed_count{0}; // NOLINT
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -299,6 +300,7 @@ class FanStateResponse : public ProtoMessage {
bool oscillating{false}; // NOLINT bool oscillating{false}; // NOLINT
enums::FanSpeed speed{}; // NOLINT enums::FanSpeed speed{}; // NOLINT
enums::FanDirection direction{}; // NOLINT enums::FanDirection direction{}; // NOLINT
int32_t speed_level{0}; // NOLINT
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -317,6 +319,8 @@ class FanCommandRequest : public ProtoMessage {
bool oscillating{false}; // NOLINT bool oscillating{false}; // NOLINT
bool has_direction{false}; // NOLINT bool has_direction{false}; // NOLINT
enums::FanDirection direction{}; // NOLINT enums::FanDirection direction{}; // NOLINT
bool has_speed_level{false}; // NOLINT
int32_t speed_level{0}; // NOLINT
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;
@ -403,6 +407,7 @@ class ListEntitiesSensorResponse : public ProtoMessage {
std::string unit_of_measurement{}; // NOLINT std::string unit_of_measurement{}; // NOLINT
int32_t accuracy_decimals{0}; // NOLINT int32_t accuracy_decimals{0}; // NOLINT
bool force_update{false}; // NOLINT bool force_update{false}; // NOLINT
std::string device_class{}; // NOLINT
void encode(ProtoWriteBuffer buffer) const override; void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override; void dump_to(std::string &out) const override;

View file

@ -1,33 +1,43 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import pins from esphome import pins
from esphome.const import CONF_INDOOR, CONF_WATCHDOG_THRESHOLD, \ from esphome.const import (
CONF_NOISE_LEVEL, CONF_SPIKE_REJECTION, CONF_LIGHTNING_THRESHOLD, \ CONF_INDOOR,
CONF_MASK_DISTURBER, CONF_DIV_RATIO, CONF_CAPACITANCE CONF_WATCHDOG_THRESHOLD,
CONF_NOISE_LEVEL,
CONF_SPIKE_REJECTION,
CONF_LIGHTNING_THRESHOLD,
CONF_MASK_DISTURBER,
CONF_DIV_RATIO,
CONF_CAPACITANCE,
)
from esphome.core import coroutine from esphome.core import coroutine
AUTO_LOAD = ['sensor', 'binary_sensor'] AUTO_LOAD = ["sensor", "binary_sensor"]
MULTI_CONF = True MULTI_CONF = True
CONF_AS3935_ID = 'as3935_id' CONF_AS3935_ID = "as3935_id"
as3935_ns = cg.esphome_ns.namespace('as3935') as3935_ns = cg.esphome_ns.namespace("as3935")
AS3935 = as3935_ns.class_('AS3935Component', cg.Component) AS3935 = as3935_ns.class_("AS3935Component", cg.Component)
CONF_IRQ_PIN = 'irq_pin' CONF_IRQ_PIN = "irq_pin"
AS3935_SCHEMA = cv.Schema({ AS3935_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(AS3935), {
cv.Required(CONF_IRQ_PIN): pins.gpio_input_pin_schema, cv.GenerateID(): cv.declare_id(AS3935),
cv.Required(CONF_IRQ_PIN): pins.gpio_input_pin_schema,
cv.Optional(CONF_INDOOR, default=True): cv.boolean, cv.Optional(CONF_INDOOR, default=True): cv.boolean,
cv.Optional(CONF_NOISE_LEVEL, default=2): cv.int_range(min=1, max=7), cv.Optional(CONF_NOISE_LEVEL, default=2): cv.int_range(min=1, max=7),
cv.Optional(CONF_WATCHDOG_THRESHOLD, default=2): cv.int_range(min=1, max=10), cv.Optional(CONF_WATCHDOG_THRESHOLD, default=2): cv.int_range(min=1, max=10),
cv.Optional(CONF_SPIKE_REJECTION, default=2): cv.int_range(min=1, max=11), cv.Optional(CONF_SPIKE_REJECTION, default=2): cv.int_range(min=1, max=11),
cv.Optional(CONF_LIGHTNING_THRESHOLD, default=1): cv.one_of(1, 5, 9, 16, int=True), cv.Optional(CONF_LIGHTNING_THRESHOLD, default=1): cv.one_of(
cv.Optional(CONF_MASK_DISTURBER, default=False): cv.boolean, 1, 5, 9, 16, int=True
cv.Optional(CONF_DIV_RATIO, default=0): cv.one_of(0, 16, 32, 64, 128, int=True), ),
cv.Optional(CONF_CAPACITANCE, default=0): cv.int_range(min=0, max=15), cv.Optional(CONF_MASK_DISTURBER, default=False): cv.boolean,
}) cv.Optional(CONF_DIV_RATIO, default=0): cv.one_of(0, 16, 32, 64, 128, int=True),
cv.Optional(CONF_CAPACITANCE, default=0): cv.int_range(min=0, max=15),
}
)
@coroutine @coroutine

View file

@ -3,11 +3,13 @@ import esphome.config_validation as cv
from esphome.components import binary_sensor from esphome.components import binary_sensor
from . import AS3935, CONF_AS3935_ID from . import AS3935, CONF_AS3935_ID
DEPENDENCIES = ['as3935'] DEPENDENCIES = ["as3935"]
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935), {
}) cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935),
}
)
def to_code(config): def to_code(config):

View file

@ -1,19 +1,30 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor from esphome.components import sensor
from esphome.const import CONF_DISTANCE, CONF_LIGHTNING_ENERGY, \ from esphome.const import (
UNIT_KILOMETER, UNIT_EMPTY, ICON_SIGNAL_DISTANCE_VARIANT, ICON_FLASH CONF_DISTANCE,
CONF_LIGHTNING_ENERGY,
DEVICE_CLASS_EMPTY,
UNIT_KILOMETER,
UNIT_EMPTY,
ICON_SIGNAL_DISTANCE_VARIANT,
ICON_FLASH,
)
from . import AS3935, CONF_AS3935_ID from . import AS3935, CONF_AS3935_ID
DEPENDENCIES = ['as3935'] DEPENDENCIES = ["as3935"]
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935), {
cv.Optional(CONF_DISTANCE): cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935),
sensor.sensor_schema(UNIT_KILOMETER, ICON_SIGNAL_DISTANCE_VARIANT, 1), cv.Optional(CONF_DISTANCE): sensor.sensor_schema(
cv.Optional(CONF_LIGHTNING_ENERGY): UNIT_KILOMETER, ICON_SIGNAL_DISTANCE_VARIANT, 1, DEVICE_CLASS_EMPTY
sensor.sensor_schema(UNIT_EMPTY, ICON_FLASH, 1), ),
}).extend(cv.COMPONENT_SCHEMA) cv.Optional(CONF_LIGHTNING_ENERGY): sensor.sensor_schema(
UNIT_EMPTY, ICON_FLASH, 1, DEVICE_CLASS_EMPTY
),
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config): def to_code(config):

View file

@ -3,15 +3,21 @@ import esphome.config_validation as cv
from esphome.components import as3935, i2c from esphome.components import as3935, i2c
from esphome.const import CONF_ID from esphome.const import CONF_ID
AUTO_LOAD = ['as3935'] AUTO_LOAD = ["as3935"]
DEPENDENCIES = ['i2c'] DEPENDENCIES = ["i2c"]
as3935_i2c_ns = cg.esphome_ns.namespace('as3935_i2c') as3935_i2c_ns = cg.esphome_ns.namespace("as3935_i2c")
I2CAS3935 = as3935_i2c_ns.class_('I2CAS3935Component', as3935.AS3935, i2c.I2CDevice) I2CAS3935 = as3935_i2c_ns.class_("I2CAS3935Component", as3935.AS3935, i2c.I2CDevice)
CONFIG_SCHEMA = cv.All(as3935.AS3935_SCHEMA.extend({ CONFIG_SCHEMA = cv.All(
cv.GenerateID(): cv.declare_id(I2CAS3935), as3935.AS3935_SCHEMA.extend(
}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x03))) {
cv.GenerateID(): cv.declare_id(I2CAS3935),
}
)
.extend(cv.COMPONENT_SCHEMA)
.extend(i2c.i2c_device_schema(0x03))
)
def to_code(config): def to_code(config):

View file

@ -3,15 +3,21 @@ import esphome.config_validation as cv
from esphome.components import as3935, spi from esphome.components import as3935, spi
from esphome.const import CONF_ID from esphome.const import CONF_ID
AUTO_LOAD = ['as3935'] AUTO_LOAD = ["as3935"]
DEPENDENCIES = ['spi'] DEPENDENCIES = ["spi"]
as3935_spi_ns = cg.esphome_ns.namespace('as3935_spi') as3935_spi_ns = cg.esphome_ns.namespace("as3935_spi")
SPIAS3935 = as3935_spi_ns.class_('SPIAS3935Component', as3935.AS3935, spi.SPIDevice) SPIAS3935 = as3935_spi_ns.class_("SPIAS3935Component", as3935.AS3935, spi.SPIDevice)
CONFIG_SCHEMA = cv.All(as3935.AS3935_SCHEMA.extend({ CONFIG_SCHEMA = cv.All(
cv.GenerateID(): cv.declare_id(SPIAS3935), as3935.AS3935_SCHEMA.extend(
}).extend(cv.COMPONENT_SCHEMA).extend(spi.spi_device_schema(cs_pin_required=True))) {
cv.GenerateID(): cv.declare_id(SPIAS3935),
}
)
.extend(cv.COMPONENT_SCHEMA)
.extend(spi.spi_device_schema(cs_pin_required=True))
)
def to_code(config): def to_code(config):

View file

@ -2,14 +2,14 @@
import esphome.codegen as cg import esphome.codegen as cg
from esphome.core import CORE, coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
CODEOWNERS = ['@OttoWinter'] CODEOWNERS = ["@OttoWinter"]
@coroutine_with_priority(200.0) @coroutine_with_priority(200.0)
def to_code(config): def to_code(config):
if CORE.is_esp32: if CORE.is_esp32:
# https://github.com/OttoWinter/AsyncTCP/blob/master/library.json # https://github.com/OttoWinter/AsyncTCP/blob/master/library.json
cg.add_library('AsyncTCP-esphome', '1.1.1') cg.add_library("AsyncTCP-esphome", "1.1.1")
elif CORE.is_esp8266: elif CORE.is_esp8266:
# https://github.com/OttoWinter/ESPAsyncTCP # https://github.com/OttoWinter/ESPAsyncTCP
cg.add_library('ESPAsyncTCP-esphome', '1.2.3') cg.add_library("ESPAsyncTCP-esphome", "1.2.3")

View file

@ -1,27 +1,54 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor, esp32_ble_tracker from esphome.components import sensor, esp32_ble_tracker
from esphome.const import CONF_BATTERY_LEVEL, CONF_BATTERY_VOLTAGE, CONF_MAC_ADDRESS, \ from esphome.const import (
CONF_HUMIDITY, CONF_TEMPERATURE, CONF_ID, UNIT_CELSIUS, UNIT_PERCENT, UNIT_VOLT, \ CONF_BATTERY_LEVEL,
ICON_BATTERY, ICON_THERMOMETER, ICON_WATER_PERCENT CONF_BATTERY_VOLTAGE,
CONF_MAC_ADDRESS,
CONF_HUMIDITY,
CONF_TEMPERATURE,
CONF_ID,
DEVICE_CLASS_BATTERY,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
UNIT_CELSIUS,
UNIT_PERCENT,
UNIT_VOLT,
)
CODEOWNERS = ['@ahpohl'] CODEOWNERS = ["@ahpohl"]
DEPENDENCIES = ['esp32_ble_tracker'] DEPENDENCIES = ["esp32_ble_tracker"]
atc_mithermometer_ns = cg.esphome_ns.namespace('atc_mithermometer') atc_mithermometer_ns = cg.esphome_ns.namespace("atc_mithermometer")
ATCMiThermometer = atc_mithermometer_ns.class_('ATCMiThermometer', ATCMiThermometer = atc_mithermometer_ns.class_(
esp32_ble_tracker.ESPBTDeviceListener, "ATCMiThermometer", esp32_ble_tracker.ESPBTDeviceListener, cg.Component
cg.Component) )
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(ATCMiThermometer), cv.Schema(
cv.Required(CONF_MAC_ADDRESS): cv.mac_address, {
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), cv.GenerateID(): cv.declare_id(ATCMiThermometer),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0), cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0), cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_BATTERY, 3), UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA) ),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_HUMIDITY
),
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_BATTERY
),
cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE
),
}
)
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
.extend(cv.COMPONENT_SCHEMA)
)
def to_code(config): def to_code(config):

View file

@ -1,61 +1,106 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor, spi from esphome.components import sensor, spi
from esphome.const import \ from esphome.const import (
CONF_ID, CONF_VOLTAGE, CONF_CURRENT, CONF_POWER, CONF_POWER_FACTOR, CONF_FREQUENCY, \ CONF_ID,
ICON_FLASH, ICON_LIGHTBULB, ICON_CURRENT_AC, ICON_THERMOMETER, \ CONF_VOLTAGE,
UNIT_HERTZ, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT, UNIT_EMPTY, UNIT_CELSIUS, UNIT_VOLT_AMPS_REACTIVE CONF_CURRENT,
CONF_POWER,
CONF_POWER_FACTOR,
CONF_FREQUENCY,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_POWER,
DEVICE_CLASS_POWER_FACTOR,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
ICON_LIGHTBULB,
ICON_CURRENT_AC,
UNIT_HERTZ,
UNIT_VOLT,
UNIT_AMPERE,
UNIT_WATT,
UNIT_EMPTY,
UNIT_CELSIUS,
UNIT_VOLT_AMPS_REACTIVE,
)
CONF_PHASE_A = 'phase_a' CONF_PHASE_A = "phase_a"
CONF_PHASE_B = 'phase_b' CONF_PHASE_B = "phase_b"
CONF_PHASE_C = 'phase_c' CONF_PHASE_C = "phase_c"
CONF_REACTIVE_POWER = 'reactive_power' CONF_REACTIVE_POWER = "reactive_power"
CONF_LINE_FREQUENCY = 'line_frequency' CONF_LINE_FREQUENCY = "line_frequency"
CONF_CHIP_TEMPERATURE = 'chip_temperature' CONF_CHIP_TEMPERATURE = "chip_temperature"
CONF_GAIN_PGA = 'gain_pga' CONF_GAIN_PGA = "gain_pga"
CONF_CURRENT_PHASES = 'current_phases' CONF_CURRENT_PHASES = "current_phases"
CONF_GAIN_VOLTAGE = 'gain_voltage' CONF_GAIN_VOLTAGE = "gain_voltage"
CONF_GAIN_CT = 'gain_ct' CONF_GAIN_CT = "gain_ct"
LINE_FREQS = { LINE_FREQS = {
'50HZ': 50, "50HZ": 50,
'60HZ': 60, "60HZ": 60,
} }
CURRENT_PHASES = { CURRENT_PHASES = {
'2': 2, "2": 2,
'3': 3, "3": 3,
} }
PGA_GAINS = { PGA_GAINS = {
'1X': 0x0, "1X": 0x0,
'2X': 0x15, "2X": 0x15,
'4X': 0x2A, "4X": 0x2A,
} }
atm90e32_ns = cg.esphome_ns.namespace('atm90e32') atm90e32_ns = cg.esphome_ns.namespace("atm90e32")
ATM90E32Component = atm90e32_ns.class_('ATM90E32Component', cg.PollingComponent, spi.SPIDevice) ATM90E32Component = atm90e32_ns.class_(
"ATM90E32Component", cg.PollingComponent, spi.SPIDevice
)
ATM90E32_PHASE_SCHEMA = cv.Schema({ ATM90E32_PHASE_SCHEMA = cv.Schema(
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2), {
cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_CURRENT_AC, 2), cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 2), UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE
cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema(UNIT_VOLT_AMPS_REACTIVE, ),
ICON_LIGHTBULB, 2), cv.Optional(CONF_CURRENT): sensor.sensor_schema(
cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(UNIT_EMPTY, ICON_FLASH, 2), UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT
cv.Optional(CONF_GAIN_VOLTAGE, default=7305): cv.uint16_t, ),
cv.Optional(CONF_GAIN_CT, default=27961): cv.uint16_t, cv.Optional(CONF_POWER): sensor.sensor_schema(
}) UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER
),
cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema(
UNIT_VOLT_AMPS_REACTIVE, ICON_LIGHTBULB, 2, DEVICE_CLASS_EMPTY
),
cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 2, DEVICE_CLASS_POWER_FACTOR
),
cv.Optional(CONF_GAIN_VOLTAGE, default=7305): cv.uint16_t,
cv.Optional(CONF_GAIN_CT, default=27961): cv.uint16_t,
}
)
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(ATM90E32Component), cv.Schema(
cv.Optional(CONF_PHASE_A): ATM90E32_PHASE_SCHEMA, {
cv.Optional(CONF_PHASE_B): ATM90E32_PHASE_SCHEMA, cv.GenerateID(): cv.declare_id(ATM90E32Component),
cv.Optional(CONF_PHASE_C): ATM90E32_PHASE_SCHEMA, cv.Optional(CONF_PHASE_A): ATM90E32_PHASE_SCHEMA,
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(UNIT_HERTZ, ICON_CURRENT_AC, 1), cv.Optional(CONF_PHASE_B): ATM90E32_PHASE_SCHEMA,
cv.Optional(CONF_CHIP_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), cv.Optional(CONF_PHASE_C): ATM90E32_PHASE_SCHEMA,
cv.Required(CONF_LINE_FREQUENCY): cv.enum(LINE_FREQS, upper=True), cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(
cv.Optional(CONF_CURRENT_PHASES, default='3'): cv.enum(CURRENT_PHASES, upper=True), UNIT_HERTZ, ICON_CURRENT_AC, 1, DEVICE_CLASS_EMPTY
cv.Optional(CONF_GAIN_PGA, default='2X'): cv.enum(PGA_GAINS, upper=True), ),
}).extend(cv.polling_component_schema('60s')).extend(spi.spi_device_schema()) cv.Optional(CONF_CHIP_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
),
cv.Required(CONF_LINE_FREQUENCY): cv.enum(LINE_FREQS, upper=True),
cv.Optional(CONF_CURRENT_PHASES, default="3"): cv.enum(
CURRENT_PHASES, upper=True
),
cv.Optional(CONF_GAIN_PGA, default="2X"): cv.enum(PGA_GAINS, upper=True),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(spi.spi_device_schema())
)
def to_code(config): def to_code(config):

View file

@ -1 +1 @@
CODEOWNERS = ['@OttoWinter'] CODEOWNERS = ["@OttoWinter"]

View file

@ -2,27 +2,41 @@ import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import automation from esphome import automation
from esphome.components import climate, sensor from esphome.components import climate, sensor
from esphome.const import CONF_AWAY_CONFIG, CONF_COOL_ACTION, \ from esphome.const import (
CONF_DEFAULT_TARGET_TEMPERATURE_HIGH, CONF_DEFAULT_TARGET_TEMPERATURE_LOW, CONF_HEAT_ACTION, \ CONF_AWAY_CONFIG,
CONF_ID, CONF_IDLE_ACTION, CONF_SENSOR CONF_COOL_ACTION,
CONF_DEFAULT_TARGET_TEMPERATURE_HIGH,
CONF_DEFAULT_TARGET_TEMPERATURE_LOW,
CONF_HEAT_ACTION,
CONF_ID,
CONF_IDLE_ACTION,
CONF_SENSOR,
)
bang_bang_ns = cg.esphome_ns.namespace('bang_bang') bang_bang_ns = cg.esphome_ns.namespace("bang_bang")
BangBangClimate = bang_bang_ns.class_('BangBangClimate', climate.Climate, cg.Component) BangBangClimate = bang_bang_ns.class_("BangBangClimate", climate.Climate, cg.Component)
BangBangClimateTargetTempConfig = bang_bang_ns.struct('BangBangClimateTargetTempConfig') BangBangClimateTargetTempConfig = bang_bang_ns.struct("BangBangClimateTargetTempConfig")
CONFIG_SCHEMA = cv.All(climate.CLIMATE_SCHEMA.extend({ CONFIG_SCHEMA = cv.All(
cv.GenerateID(): cv.declare_id(BangBangClimate), climate.CLIMATE_SCHEMA.extend(
cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor), {
cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature, cv.GenerateID(): cv.declare_id(BangBangClimate),
cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_HIGH): cv.temperature, cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor),
cv.Required(CONF_IDLE_ACTION): automation.validate_automation(single=True), cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature,
cv.Optional(CONF_COOL_ACTION): automation.validate_automation(single=True), cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_HIGH): cv.temperature,
cv.Optional(CONF_HEAT_ACTION): automation.validate_automation(single=True), cv.Required(CONF_IDLE_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_AWAY_CONFIG): cv.Schema({ cv.Optional(CONF_COOL_ACTION): automation.validate_automation(single=True),
cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature, cv.Optional(CONF_HEAT_ACTION): automation.validate_automation(single=True),
cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_HIGH): cv.temperature, cv.Optional(CONF_AWAY_CONFIG): cv.Schema(
}), {
}).extend(cv.COMPONENT_SCHEMA), cv.has_at_least_one_key(CONF_COOL_ACTION, CONF_HEAT_ACTION)) cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature,
cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_HIGH): cv.temperature,
}
),
}
).extend(cv.COMPONENT_SCHEMA),
cv.has_at_least_one_key(CONF_COOL_ACTION, CONF_HEAT_ACTION),
)
def to_code(config): def to_code(config):
@ -35,23 +49,29 @@ def to_code(config):
normal_config = BangBangClimateTargetTempConfig( normal_config = BangBangClimateTargetTempConfig(
config[CONF_DEFAULT_TARGET_TEMPERATURE_LOW], config[CONF_DEFAULT_TARGET_TEMPERATURE_LOW],
config[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH] config[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH],
) )
cg.add(var.set_normal_config(normal_config)) cg.add(var.set_normal_config(normal_config))
yield automation.build_automation(var.get_idle_trigger(), [], config[CONF_IDLE_ACTION]) yield automation.build_automation(
var.get_idle_trigger(), [], config[CONF_IDLE_ACTION]
)
if CONF_COOL_ACTION in config: if CONF_COOL_ACTION in config:
yield automation.build_automation(var.get_cool_trigger(), [], config[CONF_COOL_ACTION]) yield automation.build_automation(
var.get_cool_trigger(), [], config[CONF_COOL_ACTION]
)
cg.add(var.set_supports_cool(True)) cg.add(var.set_supports_cool(True))
if CONF_HEAT_ACTION in config: if CONF_HEAT_ACTION in config:
yield automation.build_automation(var.get_heat_trigger(), [], config[CONF_HEAT_ACTION]) yield automation.build_automation(
var.get_heat_trigger(), [], config[CONF_HEAT_ACTION]
)
cg.add(var.set_supports_heat(True)) cg.add(var.set_supports_heat(True))
if CONF_AWAY_CONFIG in config: if CONF_AWAY_CONFIG in config:
away = config[CONF_AWAY_CONFIG] away = config[CONF_AWAY_CONFIG]
away_config = BangBangClimateTargetTempConfig( away_config = BangBangClimateTargetTempConfig(
away[CONF_DEFAULT_TARGET_TEMPERATURE_LOW], away[CONF_DEFAULT_TARGET_TEMPERATURE_LOW],
away[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH] away[CONF_DEFAULT_TARGET_TEMPERATURE_HIGH],
) )
cg.add(var.set_away_config(away_config)) cg.add(var.set_away_config(away_config))

View file

@ -1,26 +1,45 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import i2c, sensor from esphome.components import i2c, sensor
from esphome.const import CONF_ID, CONF_RESOLUTION, UNIT_LUX, ICON_BRIGHTNESS_5 from esphome.const import (
CONF_ID,
CONF_RESOLUTION,
DEVICE_CLASS_ILLUMINANCE,
ICON_EMPTY,
UNIT_LUX,
)
DEPENDENCIES = ['i2c'] DEPENDENCIES = ["i2c"]
bh1750_ns = cg.esphome_ns.namespace('bh1750') bh1750_ns = cg.esphome_ns.namespace("bh1750")
BH1750Resolution = bh1750_ns.enum('BH1750Resolution') BH1750Resolution = bh1750_ns.enum("BH1750Resolution")
BH1750_RESOLUTIONS = { BH1750_RESOLUTIONS = {
4.0: BH1750Resolution.BH1750_RESOLUTION_4P0_LX, 4.0: BH1750Resolution.BH1750_RESOLUTION_4P0_LX,
1.0: BH1750Resolution.BH1750_RESOLUTION_1P0_LX, 1.0: BH1750Resolution.BH1750_RESOLUTION_1P0_LX,
0.5: BH1750Resolution.BH1750_RESOLUTION_0P5_LX, 0.5: BH1750Resolution.BH1750_RESOLUTION_0P5_LX,
} }
BH1750Sensor = bh1750_ns.class_('BH1750Sensor', sensor.Sensor, cg.PollingComponent, i2c.I2CDevice) BH1750Sensor = bh1750_ns.class_(
"BH1750Sensor", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice
)
CONF_MEASUREMENT_TIME = 'measurement_time' CONF_MEASUREMENT_TIME = "measurement_time"
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_LUX, ICON_BRIGHTNESS_5, 1).extend({ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(BH1750Sensor), sensor.sensor_schema(UNIT_LUX, ICON_EMPTY, 1, DEVICE_CLASS_ILLUMINANCE)
cv.Optional(CONF_RESOLUTION, default=0.5): cv.enum(BH1750_RESOLUTIONS, float=True), .extend(
cv.Optional(CONF_MEASUREMENT_TIME, default=69): cv.int_range(min=31, max=254), {
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x23)) cv.GenerateID(): cv.declare_id(BH1750Sensor),
cv.Optional(CONF_RESOLUTION, default=0.5): cv.enum(
BH1750_RESOLUTIONS, float=True
),
cv.Optional(CONF_MEASUREMENT_TIME, default=69): cv.int_range(
min=31, max=254
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x23))
)
def to_code(config): def to_code(config):

View file

@ -1,3 +1,3 @@
import esphome.codegen as cg import esphome.codegen as cg
binary_ns = cg.esphome_ns.namespace('binary') binary_ns = cg.esphome_ns.namespace("binary")

View file

@ -1,18 +1,24 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import fan, output from esphome.components import fan, output
from esphome.const import CONF_DIRECTION_OUTPUT, CONF_OSCILLATION_OUTPUT, \ from esphome.const import (
CONF_OUTPUT, CONF_OUTPUT_ID CONF_DIRECTION_OUTPUT,
CONF_OSCILLATION_OUTPUT,
CONF_OUTPUT,
CONF_OUTPUT_ID,
)
from .. import binary_ns from .. import binary_ns
BinaryFan = binary_ns.class_('BinaryFan', cg.Component) BinaryFan = binary_ns.class_("BinaryFan", cg.Component)
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend({ CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(BinaryFan), {
cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput), cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(BinaryFan),
cv.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput), cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput),
cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput), cv.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput),
}).extend(cv.COMPONENT_SCHEMA) cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput),
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config): def to_code(config):

View file

@ -16,7 +16,7 @@ void binary::BinaryFan::dump_config() {
} }
} }
void BinaryFan::setup() { void BinaryFan::setup() {
auto traits = fan::FanTraits(this->oscillating_ != nullptr, false, this->direction_ != nullptr); auto traits = fan::FanTraits(this->oscillating_ != nullptr, false, this->direction_ != nullptr, 0);
this->fan_->set_traits(traits); this->fan_->set_traits(traits);
this->fan_->add_on_state_callback([this]() { this->next_update_ = true; }); this->fan_->add_on_state_callback([this]() { this->next_update_ = true; });
} }

View file

@ -4,12 +4,14 @@ from esphome.components import light, output
from esphome.const import CONF_OUTPUT_ID, CONF_OUTPUT from esphome.const import CONF_OUTPUT_ID, CONF_OUTPUT
from .. import binary_ns from .. import binary_ns
BinaryLightOutput = binary_ns.class_('BinaryLightOutput', light.LightOutput) BinaryLightOutput = binary_ns.class_("BinaryLightOutput", light.LightOutput)
CONFIG_SCHEMA = light.BINARY_LIGHT_SCHEMA.extend({ CONFIG_SCHEMA = light.BINARY_LIGHT_SCHEMA.extend(
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(BinaryLightOutput), {
cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput), cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(BinaryLightOutput),
}) cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput),
}
)
def to_code(config): def to_code(config):

View file

@ -3,131 +3,214 @@ import esphome.config_validation as cv
from esphome import automation, core from esphome import automation, core
from esphome.automation import Condition, maybe_simple_id from esphome.automation import Condition, maybe_simple_id
from esphome.components import mqtt from esphome.components import mqtt
from esphome.const import CONF_DEVICE_CLASS, CONF_FILTERS, \ from esphome.const import (
CONF_ID, CONF_INTERNAL, CONF_INVALID_COOLDOWN, CONF_INVERTED, \ CONF_DEVICE_CLASS,
CONF_MAX_LENGTH, CONF_MIN_LENGTH, CONF_ON_CLICK, \ CONF_FILTERS,
CONF_ON_DOUBLE_CLICK, CONF_ON_MULTI_CLICK, CONF_ON_PRESS, CONF_ON_RELEASE, CONF_ON_STATE, \ CONF_ID,
CONF_STATE, CONF_TIMING, CONF_TRIGGER_ID, CONF_FOR, CONF_NAME, CONF_MQTT_ID CONF_INTERNAL,
CONF_INVALID_COOLDOWN,
CONF_INVERTED,
CONF_MAX_LENGTH,
CONF_MIN_LENGTH,
CONF_ON_CLICK,
CONF_ON_DOUBLE_CLICK,
CONF_ON_MULTI_CLICK,
CONF_ON_PRESS,
CONF_ON_RELEASE,
CONF_ON_STATE,
CONF_STATE,
CONF_TIMING,
CONF_TRIGGER_ID,
CONF_FOR,
CONF_NAME,
CONF_MQTT_ID,
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_BATTERY,
DEVICE_CLASS_BATTERY_CHARGING,
DEVICE_CLASS_COLD,
DEVICE_CLASS_CONNECTIVITY,
DEVICE_CLASS_DOOR,
DEVICE_CLASS_GARAGE_DOOR,
DEVICE_CLASS_GAS,
DEVICE_CLASS_HEAT,
DEVICE_CLASS_LIGHT,
DEVICE_CLASS_LOCK,
DEVICE_CLASS_MOISTURE,
DEVICE_CLASS_MOTION,
DEVICE_CLASS_MOVING,
DEVICE_CLASS_OCCUPANCY,
DEVICE_CLASS_OPENING,
DEVICE_CLASS_PLUG,
DEVICE_CLASS_POWER,
DEVICE_CLASS_PRESENCE,
DEVICE_CLASS_PROBLEM,
DEVICE_CLASS_SAFETY,
DEVICE_CLASS_SMOKE,
DEVICE_CLASS_SOUND,
DEVICE_CLASS_VIBRATION,
DEVICE_CLASS_WINDOW,
)
from esphome.core import CORE, coroutine, coroutine_with_priority from esphome.core import CORE, coroutine, coroutine_with_priority
from esphome.util import Registry from esphome.util import Registry
CODEOWNERS = ['@esphome/core'] CODEOWNERS = ["@esphome/core"]
DEVICE_CLASSES = [ DEVICE_CLASSES = [
'', 'battery', 'cold', 'connectivity', 'door', 'garage_door', 'gas', DEVICE_CLASS_EMPTY,
'heat', 'light', 'lock', 'moisture', 'motion', 'moving', 'occupancy', DEVICE_CLASS_BATTERY,
'opening', 'plug', 'power', 'presence', 'problem', 'safety', 'smoke', DEVICE_CLASS_BATTERY_CHARGING,
'sound', 'vibration', 'window' DEVICE_CLASS_COLD,
DEVICE_CLASS_CONNECTIVITY,
DEVICE_CLASS_DOOR,
DEVICE_CLASS_GARAGE_DOOR,
DEVICE_CLASS_GAS,
DEVICE_CLASS_HEAT,
DEVICE_CLASS_LIGHT,
DEVICE_CLASS_LOCK,
DEVICE_CLASS_MOISTURE,
DEVICE_CLASS_MOTION,
DEVICE_CLASS_MOVING,
DEVICE_CLASS_OCCUPANCY,
DEVICE_CLASS_OPENING,
DEVICE_CLASS_PLUG,
DEVICE_CLASS_POWER,
DEVICE_CLASS_PRESENCE,
DEVICE_CLASS_PROBLEM,
DEVICE_CLASS_SAFETY,
DEVICE_CLASS_SMOKE,
DEVICE_CLASS_SOUND,
DEVICE_CLASS_VIBRATION,
DEVICE_CLASS_WINDOW,
] ]
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
binary_sensor_ns = cg.esphome_ns.namespace('binary_sensor') binary_sensor_ns = cg.esphome_ns.namespace("binary_sensor")
BinarySensor = binary_sensor_ns.class_('BinarySensor', cg.Nameable) BinarySensor = binary_sensor_ns.class_("BinarySensor", cg.Nameable)
BinarySensorInitiallyOff = binary_sensor_ns.class_('BinarySensorInitiallyOff', BinarySensor) BinarySensorInitiallyOff = binary_sensor_ns.class_(
BinarySensorPtr = BinarySensor.operator('ptr') "BinarySensorInitiallyOff", BinarySensor
)
BinarySensorPtr = BinarySensor.operator("ptr")
# Triggers # Triggers
PressTrigger = binary_sensor_ns.class_('PressTrigger', automation.Trigger.template()) PressTrigger = binary_sensor_ns.class_("PressTrigger", automation.Trigger.template())
ReleaseTrigger = binary_sensor_ns.class_('ReleaseTrigger', automation.Trigger.template()) ReleaseTrigger = binary_sensor_ns.class_(
ClickTrigger = binary_sensor_ns.class_('ClickTrigger', automation.Trigger.template()) "ReleaseTrigger", automation.Trigger.template()
DoubleClickTrigger = binary_sensor_ns.class_('DoubleClickTrigger', automation.Trigger.template()) )
MultiClickTrigger = binary_sensor_ns.class_('MultiClickTrigger', automation.Trigger.template(), ClickTrigger = binary_sensor_ns.class_("ClickTrigger", automation.Trigger.template())
cg.Component) DoubleClickTrigger = binary_sensor_ns.class_(
MultiClickTriggerEvent = binary_sensor_ns.struct('MultiClickTriggerEvent') "DoubleClickTrigger", automation.Trigger.template()
StateTrigger = binary_sensor_ns.class_('StateTrigger', automation.Trigger.template(bool)) )
BinarySensorPublishAction = binary_sensor_ns.class_('BinarySensorPublishAction', automation.Action) MultiClickTrigger = binary_sensor_ns.class_(
"MultiClickTrigger", automation.Trigger.template(), cg.Component
)
MultiClickTriggerEvent = binary_sensor_ns.struct("MultiClickTriggerEvent")
StateTrigger = binary_sensor_ns.class_(
"StateTrigger", automation.Trigger.template(bool)
)
BinarySensorPublishAction = binary_sensor_ns.class_(
"BinarySensorPublishAction", automation.Action
)
# Condition # Condition
BinarySensorCondition = binary_sensor_ns.class_('BinarySensorCondition', Condition) BinarySensorCondition = binary_sensor_ns.class_("BinarySensorCondition", Condition)
# Filters # Filters
Filter = binary_sensor_ns.class_('Filter') Filter = binary_sensor_ns.class_("Filter")
DelayedOnOffFilter = binary_sensor_ns.class_('DelayedOnOffFilter', Filter, cg.Component) DelayedOnOffFilter = binary_sensor_ns.class_("DelayedOnOffFilter", Filter, cg.Component)
DelayedOnFilter = binary_sensor_ns.class_('DelayedOnFilter', Filter, cg.Component) DelayedOnFilter = binary_sensor_ns.class_("DelayedOnFilter", Filter, cg.Component)
DelayedOffFilter = binary_sensor_ns.class_('DelayedOffFilter', Filter, cg.Component) DelayedOffFilter = binary_sensor_ns.class_("DelayedOffFilter", Filter, cg.Component)
InvertFilter = binary_sensor_ns.class_('InvertFilter', Filter) InvertFilter = binary_sensor_ns.class_("InvertFilter", Filter)
LambdaFilter = binary_sensor_ns.class_('LambdaFilter', Filter) LambdaFilter = binary_sensor_ns.class_("LambdaFilter", Filter)
FILTER_REGISTRY = Registry() FILTER_REGISTRY = Registry()
validate_filters = cv.validate_registry('filter', FILTER_REGISTRY) validate_filters = cv.validate_registry("filter", FILTER_REGISTRY)
@FILTER_REGISTRY.register('invert', InvertFilter, {}) @FILTER_REGISTRY.register("invert", InvertFilter, {})
def invert_filter_to_code(config, filter_id): def invert_filter_to_code(config, filter_id):
yield cg.new_Pvariable(filter_id) yield cg.new_Pvariable(filter_id)
@FILTER_REGISTRY.register('delayed_on_off', DelayedOnOffFilter, @FILTER_REGISTRY.register(
cv.positive_time_period_milliseconds) "delayed_on_off", DelayedOnOffFilter, cv.positive_time_period_milliseconds
)
def delayed_on_off_filter_to_code(config, filter_id): def delayed_on_off_filter_to_code(config, filter_id):
var = cg.new_Pvariable(filter_id, config) var = cg.new_Pvariable(filter_id, config)
yield cg.register_component(var, {}) yield cg.register_component(var, {})
yield var yield var
@FILTER_REGISTRY.register('delayed_on', DelayedOnFilter, @FILTER_REGISTRY.register(
cv.positive_time_period_milliseconds) "delayed_on", DelayedOnFilter, cv.positive_time_period_milliseconds
)
def delayed_on_filter_to_code(config, filter_id): def delayed_on_filter_to_code(config, filter_id):
var = cg.new_Pvariable(filter_id, config) var = cg.new_Pvariable(filter_id, config)
yield cg.register_component(var, {}) yield cg.register_component(var, {})
yield var yield var
@FILTER_REGISTRY.register('delayed_off', DelayedOffFilter, cv.positive_time_period_milliseconds) @FILTER_REGISTRY.register(
"delayed_off", DelayedOffFilter, cv.positive_time_period_milliseconds
)
def delayed_off_filter_to_code(config, filter_id): def delayed_off_filter_to_code(config, filter_id):
var = cg.new_Pvariable(filter_id, config) var = cg.new_Pvariable(filter_id, config)
yield cg.register_component(var, {}) yield cg.register_component(var, {})
yield var yield var
@FILTER_REGISTRY.register('lambda', LambdaFilter, cv.returning_lambda) @FILTER_REGISTRY.register("lambda", LambdaFilter, cv.returning_lambda)
def lambda_filter_to_code(config, filter_id): def lambda_filter_to_code(config, filter_id):
lambda_ = yield cg.process_lambda(config, [(bool, 'x')], return_type=cg.optional.template(bool)) lambda_ = yield cg.process_lambda(
config, [(bool, "x")], return_type=cg.optional.template(bool)
)
yield cg.new_Pvariable(filter_id, lambda_) yield cg.new_Pvariable(filter_id, lambda_)
MULTI_CLICK_TIMING_SCHEMA = cv.Schema({ MULTI_CLICK_TIMING_SCHEMA = cv.Schema(
cv.Optional(CONF_STATE): cv.boolean, {
cv.Optional(CONF_MIN_LENGTH): cv.positive_time_period_milliseconds, cv.Optional(CONF_STATE): cv.boolean,
cv.Optional(CONF_MAX_LENGTH): cv.positive_time_period_milliseconds, cv.Optional(CONF_MIN_LENGTH): cv.positive_time_period_milliseconds,
}) cv.Optional(CONF_MAX_LENGTH): cv.positive_time_period_milliseconds,
}
)
def parse_multi_click_timing_str(value): def parse_multi_click_timing_str(value):
if not isinstance(value, str): if not isinstance(value, str):
return value return value
parts = value.lower().split(' ') parts = value.lower().split(" ")
if len(parts) != 5: if len(parts) != 5:
raise cv.Invalid("Multi click timing grammar consists of exactly 5 words, not {}" raise cv.Invalid(
"".format(len(parts))) "Multi click timing grammar consists of exactly 5 words, not {}"
"".format(len(parts))
)
try: try:
state = cv.boolean(parts[0]) state = cv.boolean(parts[0])
except cv.Invalid: except cv.Invalid:
# pylint: disable=raise-missing-from # pylint: disable=raise-missing-from
raise cv.Invalid("First word must either be ON or OFF, not {}".format(parts[0])) raise cv.Invalid("First word must either be ON or OFF, not {}".format(parts[0]))
if parts[1] != 'for': if parts[1] != "for":
raise cv.Invalid("Second word must be 'for', got {}".format(parts[1])) raise cv.Invalid("Second word must be 'for', got {}".format(parts[1]))
if parts[2] == 'at': if parts[2] == "at":
if parts[3] == 'least': if parts[3] == "least":
key = CONF_MIN_LENGTH key = CONF_MIN_LENGTH
elif parts[3] == 'most': elif parts[3] == "most":
key = CONF_MAX_LENGTH key = CONF_MAX_LENGTH
else: else:
raise cv.Invalid("Third word after at must either be 'least' or 'most', got {}" raise cv.Invalid(
"".format(parts[3])) "Third word after at must either be 'least' or 'most', got {}"
"".format(parts[3])
)
try: try:
length = cv.positive_time_period_milliseconds(parts[4]) length = cv.positive_time_period_milliseconds(parts[4])
except cv.Invalid as err: except cv.Invalid as err:
raise cv.Invalid(f"Multi Click Grammar Parsing length failed: {err}") raise cv.Invalid(f"Multi Click Grammar Parsing length failed: {err}")
return { return {CONF_STATE: state, key: str(length)}
CONF_STATE: state,
key: str(length)
}
if parts[3] != 'to': if parts[3] != "to":
raise cv.Invalid("Multi click grammar: 4th word must be 'to'") raise cv.Invalid("Multi click grammar: 4th word must be 'to'")
try: try:
@ -143,7 +226,7 @@ def parse_multi_click_timing_str(value):
return { return {
CONF_STATE: state, CONF_STATE: state,
CONF_MIN_LENGTH: str(min_length), CONF_MIN_LENGTH: str(min_length),
CONF_MAX_LENGTH: str(max_length) CONF_MAX_LENGTH: str(max_length),
} }
@ -163,11 +246,15 @@ def validate_multi_click_timing(value):
new_state = v_.get(CONF_STATE, not state) new_state = v_.get(CONF_STATE, not state)
if new_state == state: if new_state == state:
raise cv.Invalid("Timings must have alternating state. Indices {} and {} have " raise cv.Invalid(
"the same state {}".format(i, i + 1, state)) "Timings must have alternating state. Indices {} and {} have "
"the same state {}".format(i, i + 1, state)
)
if max_length is not None and max_length < min_length: if max_length is not None and max_length < min_length:
raise cv.Invalid("Max length ({}) must be larger than min length ({})." raise cv.Invalid(
"".format(max_length, min_length)) "Max length ({}) must be larger than min length ({})."
"".format(max_length, min_length)
)
state = new_state state = new_state
tim = { tim = {
@ -180,46 +267,71 @@ def validate_multi_click_timing(value):
return timings return timings
device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space='_') device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({ BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend(
cv.GenerateID(): cv.declare_id(BinarySensor), {
cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTBinarySensorComponent), cv.GenerateID(): cv.declare_id(BinarySensor),
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
cv.Optional(CONF_DEVICE_CLASS): device_class, mqtt.MQTTBinarySensorComponent
cv.Optional(CONF_FILTERS): validate_filters, ),
cv.Optional(CONF_ON_PRESS): automation.validate_automation({ cv.Optional(CONF_DEVICE_CLASS): device_class,
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PressTrigger), cv.Optional(CONF_FILTERS): validate_filters,
}), cv.Optional(CONF_ON_PRESS): automation.validate_automation(
cv.Optional(CONF_ON_RELEASE): automation.validate_automation({ {
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReleaseTrigger), cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PressTrigger),
}), }
cv.Optional(CONF_ON_CLICK): automation.validate_automation({ ),
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClickTrigger), cv.Optional(CONF_ON_RELEASE): automation.validate_automation(
cv.Optional(CONF_MIN_LENGTH, default='50ms'): cv.positive_time_period_milliseconds, {
cv.Optional(CONF_MAX_LENGTH, default='350ms'): cv.positive_time_period_milliseconds, cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReleaseTrigger),
}), }
cv.Optional(CONF_ON_DOUBLE_CLICK): automation.validate_automation({ ),
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DoubleClickTrigger), cv.Optional(CONF_ON_CLICK): automation.validate_automation(
cv.Optional(CONF_MIN_LENGTH, default='50ms'): cv.positive_time_period_milliseconds, {
cv.Optional(CONF_MAX_LENGTH, default='350ms'): cv.positive_time_period_milliseconds, cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClickTrigger),
}), cv.Optional(
cv.Optional(CONF_ON_MULTI_CLICK): automation.validate_automation({ CONF_MIN_LENGTH, default="50ms"
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(MultiClickTrigger), ): cv.positive_time_period_milliseconds,
cv.Required(CONF_TIMING): cv.All([parse_multi_click_timing_str], cv.Optional(
validate_multi_click_timing), CONF_MAX_LENGTH, default="350ms"
cv.Optional(CONF_INVALID_COOLDOWN, default='1s'): cv.positive_time_period_milliseconds, ): cv.positive_time_period_milliseconds,
}), }
cv.Optional(CONF_ON_STATE): automation.validate_automation({ ),
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger), cv.Optional(CONF_ON_DOUBLE_CLICK): automation.validate_automation(
}), {
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DoubleClickTrigger),
cv.Optional(CONF_INVERTED): cv.invalid( cv.Optional(
"The inverted binary_sensor property has been replaced by the " CONF_MIN_LENGTH, default="50ms"
"new 'invert' binary sensor filter. Please see " ): cv.positive_time_period_milliseconds,
"https://esphome.io/components/binary_sensor/index.html." cv.Optional(
), CONF_MAX_LENGTH, default="350ms"
}) ): cv.positive_time_period_milliseconds,
}
),
cv.Optional(CONF_ON_MULTI_CLICK): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(MultiClickTrigger),
cv.Required(CONF_TIMING): cv.All(
[parse_multi_click_timing_str], validate_multi_click_timing
),
cv.Optional(
CONF_INVALID_COOLDOWN, default="1s"
): cv.positive_time_period_milliseconds,
}
),
cv.Optional(CONF_ON_STATE): automation.validate_automation(
{
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."
),
}
)
@coroutine @coroutine
@ -244,24 +356,28 @@ def setup_binary_sensor_core_(var, config):
yield automation.build_automation(trigger, [], conf) yield automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_CLICK, []): for conf in config.get(CONF_ON_CLICK, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, trigger = cg.new_Pvariable(
conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH]) conf[CONF_TRIGGER_ID], var, conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH]
)
yield automation.build_automation(trigger, [], conf) yield automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_DOUBLE_CLICK, []): for conf in config.get(CONF_ON_DOUBLE_CLICK, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, trigger = cg.new_Pvariable(
conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH]) conf[CONF_TRIGGER_ID], var, conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH]
)
yield automation.build_automation(trigger, [], conf) yield automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_MULTI_CLICK, []): for conf in config.get(CONF_ON_MULTI_CLICK, []):
timings = [] timings = []
for tim in conf[CONF_TIMING]: for tim in conf[CONF_TIMING]:
timings.append(cg.StructInitializer( timings.append(
MultiClickTriggerEvent, cg.StructInitializer(
('state', tim[CONF_STATE]), MultiClickTriggerEvent,
('min_length', tim[CONF_MIN_LENGTH]), ("state", tim[CONF_STATE]),
('max_length', tim.get(CONF_MAX_LENGTH, 4294967294)), ("min_length", tim[CONF_MIN_LENGTH]),
)) ("max_length", tim.get(CONF_MAX_LENGTH, 4294967294)),
)
)
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, timings) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, timings)
if CONF_INVALID_COOLDOWN in conf: if CONF_INVALID_COOLDOWN in conf:
cg.add(trigger.set_invalid_cooldown(conf[CONF_INVALID_COOLDOWN])) cg.add(trigger.set_invalid_cooldown(conf[CONF_INVALID_COOLDOWN]))
@ -270,7 +386,7 @@ def setup_binary_sensor_core_(var, config):
for conf in config.get(CONF_ON_STATE, []): for conf in config.get(CONF_ON_STATE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
yield automation.build_automation(trigger, [(bool, 'x')], conf) yield automation.build_automation(trigger, [(bool, "x")], conf)
if CONF_MQTT_ID in config: if CONF_MQTT_ID in config:
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var) mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
@ -292,22 +408,28 @@ def new_binary_sensor(config):
yield var yield var
BINARY_SENSOR_CONDITION_SCHEMA = maybe_simple_id({ 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 " cv.Required(CONF_ID): cv.use_id(BinarySensor),
"'for' condition instead."), cv.Optional(CONF_FOR): cv.invalid(
}) "This option has been removed in 1.13, please use the "
"'for' condition instead."
),
}
)
@automation.register_condition('binary_sensor.is_on', BinarySensorCondition, @automation.register_condition(
BINARY_SENSOR_CONDITION_SCHEMA) "binary_sensor.is_on", BinarySensorCondition, BINARY_SENSOR_CONDITION_SCHEMA
)
def binary_sensor_is_on_to_code(config, condition_id, template_arg, args): def binary_sensor_is_on_to_code(config, condition_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID]) paren = yield cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(condition_id, template_arg, paren, True) yield cg.new_Pvariable(condition_id, template_arg, paren, True)
@automation.register_condition('binary_sensor.is_off', BinarySensorCondition, @automation.register_condition(
BINARY_SENSOR_CONDITION_SCHEMA) "binary_sensor.is_off", BinarySensorCondition, BINARY_SENSOR_CONDITION_SCHEMA
)
def binary_sensor_is_off_to_code(config, condition_id, template_arg, args): def binary_sensor_is_off_to_code(config, condition_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID]) paren = yield cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(condition_id, template_arg, paren, False) yield cg.new_Pvariable(condition_id, template_arg, paren, False)
@ -315,5 +437,5 @@ def binary_sensor_is_off_to_code(config, condition_id, template_arg, args):
@coroutine_with_priority(100.0) @coroutine_with_priority(100.0)
def to_code(config): def to_code(config):
cg.add_define('USE_BINARY_SENSOR') cg.add_define("USE_BINARY_SENSOR")
cg.add_global(binary_sensor_ns.using) cg.add_global(binary_sensor_ns.using)

View file

@ -2,14 +2,25 @@ import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor, binary_sensor from esphome.components import sensor, binary_sensor
from esphome.const import CONF_ID, CONF_CHANNELS, CONF_VALUE, CONF_TYPE, UNIT_EMPTY, \ from esphome.const import (
ICON_CHECK_CIRCLE_OUTLINE, CONF_BINARY_SENSOR, CONF_GROUP CONF_ID,
CONF_CHANNELS,
CONF_VALUE,
CONF_TYPE,
DEVICE_CLASS_EMPTY,
UNIT_EMPTY,
ICON_CHECK_CIRCLE_OUTLINE,
CONF_BINARY_SENSOR,
CONF_GROUP,
)
DEPENDENCIES = ['binary_sensor'] DEPENDENCIES = ["binary_sensor"]
binary_sensor_map_ns = cg.esphome_ns.namespace('binary_sensor_map') binary_sensor_map_ns = cg.esphome_ns.namespace("binary_sensor_map")
BinarySensorMap = binary_sensor_map_ns.class_('BinarySensorMap', cg.Component, sensor.Sensor) BinarySensorMap = binary_sensor_map_ns.class_(
SensorMapType = binary_sensor_map_ns.enum('SensorMapType') "BinarySensorMap", cg.Component, sensor.Sensor
)
SensorMapType = binary_sensor_map_ns.enum("SensorMapType")
SENSOR_MAP_TYPES = { SENSOR_MAP_TYPES = {
CONF_GROUP: SensorMapType.BINARY_SENSOR_MAP_TYPE_GROUP, CONF_GROUP: SensorMapType.BINARY_SENSOR_MAP_TYPE_GROUP,
@ -20,12 +31,21 @@ entry = {
cv.Required(CONF_VALUE): cv.float_, cv.Required(CONF_VALUE): cv.float_,
} }
CONFIG_SCHEMA = cv.typed_schema({ CONFIG_SCHEMA = cv.typed_schema(
CONF_GROUP: sensor.sensor_schema(UNIT_EMPTY, ICON_CHECK_CIRCLE_OUTLINE, 0).extend({ {
cv.GenerateID(): cv.declare_id(BinarySensorMap), CONF_GROUP: sensor.sensor_schema(
cv.Required(CONF_CHANNELS): cv.All(cv.ensure_list(entry), cv.Length(min=1)), UNIT_EMPTY, ICON_CHECK_CIRCLE_OUTLINE, 0, DEVICE_CLASS_EMPTY
}), ).extend(
}, lower=True) {
cv.GenerateID(): cv.declare_id(BinarySensorMap),
cv.Required(CONF_CHANNELS): cv.All(
cv.ensure_list(entry), cv.Length(min=1)
),
}
),
},
lower=True,
)
def to_code(config): def to_code(config):

View file

@ -3,18 +3,28 @@ import esphome.config_validation as cv
from esphome.components import binary_sensor, esp32_ble_tracker from esphome.components import binary_sensor, esp32_ble_tracker
from esphome.const import CONF_MAC_ADDRESS, CONF_SERVICE_UUID, CONF_ID from esphome.const import CONF_MAC_ADDRESS, CONF_SERVICE_UUID, CONF_ID
DEPENDENCIES = ['esp32_ble_tracker'] DEPENDENCIES = ["esp32_ble_tracker"]
ble_presence_ns = cg.esphome_ns.namespace('ble_presence') ble_presence_ns = cg.esphome_ns.namespace("ble_presence")
BLEPresenceDevice = ble_presence_ns.class_('BLEPresenceDevice', binary_sensor.BinarySensor, BLEPresenceDevice = ble_presence_ns.class_(
cg.Component, esp32_ble_tracker.ESPBTDeviceListener) "BLEPresenceDevice",
binary_sensor.BinarySensor,
cg.Component,
esp32_ble_tracker.ESPBTDeviceListener,
)
CONFIG_SCHEMA = cv.All(binary_sensor.BINARY_SENSOR_SCHEMA.extend({ CONFIG_SCHEMA = cv.All(
cv.GenerateID(): cv.declare_id(BLEPresenceDevice), binary_sensor.BINARY_SENSOR_SCHEMA.extend(
cv.Optional(CONF_MAC_ADDRESS): cv.mac_address, {
cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, cv.GenerateID(): cv.declare_id(BLEPresenceDevice),
}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend( cv.Optional(CONF_MAC_ADDRESS): cv.mac_address,
cv.COMPONENT_SCHEMA), cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID)) cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
}
)
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
.extend(cv.COMPONENT_SCHEMA),
cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID),
)
def to_code(config): def to_code(config):
@ -28,9 +38,17 @@ def to_code(config):
if CONF_SERVICE_UUID in config: if CONF_SERVICE_UUID in config:
if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format): if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
cg.add(var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))) cg.add(
var.set_service_uuid16(
esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID])
)
)
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format): elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format):
cg.add(var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))) cg.add(
var.set_service_uuid32(
esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID])
)
)
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format): elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_SERVICE_UUID]) uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_SERVICE_UUID])
cg.add(var.set_service_uuid128(uuid128)) cg.add(var.set_service_uuid128(uuid128))

View file

@ -1,20 +1,35 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor, esp32_ble_tracker from esphome.components import sensor, esp32_ble_tracker
from esphome.const import CONF_SERVICE_UUID, CONF_MAC_ADDRESS, CONF_ID, UNIT_DECIBEL, ICON_SIGNAL from esphome.const import (
CONF_SERVICE_UUID,
CONF_MAC_ADDRESS,
CONF_ID,
DEVICE_CLASS_SIGNAL_STRENGTH,
UNIT_DECIBEL,
ICON_EMPTY,
)
DEPENDENCIES = ['esp32_ble_tracker'] DEPENDENCIES = ["esp32_ble_tracker"]
ble_rssi_ns = cg.esphome_ns.namespace('ble_rssi') ble_rssi_ns = cg.esphome_ns.namespace("ble_rssi")
BLERSSISensor = ble_rssi_ns.class_('BLERSSISensor', sensor.Sensor, cg.Component, BLERSSISensor = ble_rssi_ns.class_(
esp32_ble_tracker.ESPBTDeviceListener) "BLERSSISensor", sensor.Sensor, cg.Component, esp32_ble_tracker.ESPBTDeviceListener
)
CONFIG_SCHEMA = cv.All(sensor.sensor_schema(UNIT_DECIBEL, ICON_SIGNAL, 0).extend({ CONFIG_SCHEMA = cv.All(
cv.GenerateID(): cv.declare_id(BLERSSISensor), sensor.sensor_schema(UNIT_DECIBEL, ICON_EMPTY, 0, DEVICE_CLASS_SIGNAL_STRENGTH)
cv.Optional(CONF_MAC_ADDRESS): cv.mac_address, .extend(
cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid, {
}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend( cv.GenerateID(): cv.declare_id(BLERSSISensor),
cv.COMPONENT_SCHEMA), cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID)) cv.Optional(CONF_MAC_ADDRESS): cv.mac_address,
cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
}
)
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
.extend(cv.COMPONENT_SCHEMA),
cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID),
)
def to_code(config): def to_code(config):
@ -28,9 +43,17 @@ def to_code(config):
if CONF_SERVICE_UUID in config: if CONF_SERVICE_UUID in config:
if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format): if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
cg.add(var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))) cg.add(
var.set_service_uuid16(
esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID])
)
)
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format): elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format):
cg.add(var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))) cg.add(
var.set_service_uuid32(
esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID])
)
)
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format): elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_SERVICE_UUID]) uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_SERVICE_UUID])
cg.add(var.set_service_uuid128(uuid128)) cg.add(var.set_service_uuid128(uuid128))

View file

@ -3,16 +3,25 @@ import esphome.config_validation as cv
from esphome.components import text_sensor, esp32_ble_tracker from esphome.components import text_sensor, esp32_ble_tracker
from esphome.const import CONF_ID from esphome.const import CONF_ID
DEPENDENCIES = ['esp32_ble_tracker'] DEPENDENCIES = ["esp32_ble_tracker"]
ble_scanner_ns = cg.esphome_ns.namespace('ble_scanner') ble_scanner_ns = cg.esphome_ns.namespace("ble_scanner")
BLEScanner = ble_scanner_ns.class_('BLEScanner', text_sensor.TextSensor, cg.Component, BLEScanner = ble_scanner_ns.class_(
esp32_ble_tracker.ESPBTDeviceListener) "BLEScanner",
text_sensor.TextSensor,
cg.Component,
esp32_ble_tracker.ESPBTDeviceListener,
)
CONFIG_SCHEMA = cv.All(text_sensor.TEXT_SENSOR_SCHEMA.extend({ CONFIG_SCHEMA = cv.All(
cv.GenerateID(): cv.declare_id(BLEScanner), text_sensor.TEXT_SENSOR_SCHEMA.extend(
}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend( {
cv.COMPONENT_SCHEMA)) cv.GenerateID(): cv.declare_id(BLEScanner),
}
)
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
.extend(cv.COMPONENT_SCHEMA)
)
def to_code(config): def to_code(config):

View file

@ -1,53 +1,87 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import i2c, sensor from esphome.components import i2c, sensor
from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_IIR_FILTER, CONF_OVERSAMPLING, \ from esphome.const import (
CONF_PRESSURE, CONF_TEMPERATURE, ICON_THERMOMETER, \ CONF_HUMIDITY,
UNIT_CELSIUS, UNIT_HECTOPASCAL, ICON_GAUGE, ICON_WATER_PERCENT, UNIT_PERCENT CONF_ID,
CONF_IIR_FILTER,
CONF_OVERSAMPLING,
CONF_PRESSURE,
CONF_TEMPERATURE,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
UNIT_CELSIUS,
UNIT_HECTOPASCAL,
UNIT_PERCENT,
)
DEPENDENCIES = ['i2c'] DEPENDENCIES = ["i2c"]
bme280_ns = cg.esphome_ns.namespace('bme280') bme280_ns = cg.esphome_ns.namespace("bme280")
BME280Oversampling = bme280_ns.enum('BME280Oversampling') BME280Oversampling = bme280_ns.enum("BME280Oversampling")
OVERSAMPLING_OPTIONS = { OVERSAMPLING_OPTIONS = {
'NONE': BME280Oversampling.BME280_OVERSAMPLING_NONE, "NONE": BME280Oversampling.BME280_OVERSAMPLING_NONE,
'1X': BME280Oversampling.BME280_OVERSAMPLING_1X, "1X": BME280Oversampling.BME280_OVERSAMPLING_1X,
'2X': BME280Oversampling.BME280_OVERSAMPLING_2X, "2X": BME280Oversampling.BME280_OVERSAMPLING_2X,
'4X': BME280Oversampling.BME280_OVERSAMPLING_4X, "4X": BME280Oversampling.BME280_OVERSAMPLING_4X,
'8X': BME280Oversampling.BME280_OVERSAMPLING_8X, "8X": BME280Oversampling.BME280_OVERSAMPLING_8X,
'16X': BME280Oversampling.BME280_OVERSAMPLING_16X, "16X": BME280Oversampling.BME280_OVERSAMPLING_16X,
} }
BME280IIRFilter = bme280_ns.enum('BME280IIRFilter') BME280IIRFilter = bme280_ns.enum("BME280IIRFilter")
IIR_FILTER_OPTIONS = { IIR_FILTER_OPTIONS = {
'OFF': BME280IIRFilter.BME280_IIR_FILTER_OFF, "OFF": BME280IIRFilter.BME280_IIR_FILTER_OFF,
'2X': BME280IIRFilter.BME280_IIR_FILTER_2X, "2X": BME280IIRFilter.BME280_IIR_FILTER_2X,
'4X': BME280IIRFilter.BME280_IIR_FILTER_4X, "4X": BME280IIRFilter.BME280_IIR_FILTER_4X,
'8X': BME280IIRFilter.BME280_IIR_FILTER_8X, "8X": BME280IIRFilter.BME280_IIR_FILTER_8X,
'16X': BME280IIRFilter.BME280_IIR_FILTER_16X, "16X": BME280IIRFilter.BME280_IIR_FILTER_16X,
} }
BME280Component = bme280_ns.class_('BME280Component', cg.PollingComponent, i2c.I2CDevice) BME280Component = bme280_ns.class_(
"BME280Component", cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(BME280Component), cv.Schema(
cv.Optional(CONF_TEMPERATURE): {
sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ cv.GenerateID(): cv.declare_id(BME280Component),
cv.Optional(CONF_OVERSAMPLING, default='16X'): cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
cv.enum(OVERSAMPLING_OPTIONS, upper=True), UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
}), ).extend(
cv.Optional(CONF_PRESSURE): {
sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1).extend({ cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
cv.Optional(CONF_OVERSAMPLING, default='16X'): OVERSAMPLING_OPTIONS, upper=True
cv.enum(OVERSAMPLING_OPTIONS, upper=True), ),
}), }
cv.Optional(CONF_HUMIDITY): ),
sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1).extend({ cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
cv.Optional(CONF_OVERSAMPLING, default='16X'): UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE
cv.enum(OVERSAMPLING_OPTIONS, upper=True), ).extend(
}), {
cv.Optional(CONF_IIR_FILTER, default='OFF'): cv.enum(IIR_FILTER_OPTIONS, upper=True), cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x77)) OVERSAMPLING_OPTIONS, upper=True
),
}
),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
OVERSAMPLING_OPTIONS, upper=True
),
}
),
cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
IIR_FILTER_OPTIONS, upper=True
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x77))
)
def to_code(config): def to_code(config):

View file

@ -2,64 +2,116 @@ import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import core from esphome import core
from esphome.components import i2c, sensor from esphome.components import i2c, sensor
from esphome.const import CONF_DURATION, CONF_GAS_RESISTANCE, CONF_HEATER, \ from esphome.const import (
CONF_HUMIDITY, CONF_ID, CONF_IIR_FILTER, CONF_OVERSAMPLING, CONF_PRESSURE, \ CONF_DURATION,
CONF_TEMPERATURE, UNIT_OHM, ICON_GAS_CYLINDER, UNIT_CELSIUS, \ CONF_GAS_RESISTANCE,
ICON_THERMOMETER, UNIT_HECTOPASCAL, ICON_GAUGE, ICON_WATER_PERCENT, UNIT_PERCENT CONF_HEATER,
CONF_HUMIDITY,
CONF_ID,
CONF_IIR_FILTER,
CONF_OVERSAMPLING,
CONF_PRESSURE,
CONF_TEMPERATURE,
DEVICE_CLASS_EMPTY,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
UNIT_OHM,
ICON_GAS_CYLINDER,
UNIT_CELSIUS,
ICON_EMPTY,
UNIT_HECTOPASCAL,
UNIT_PERCENT,
)
DEPENDENCIES = ['i2c'] DEPENDENCIES = ["i2c"]
bme680_ns = cg.esphome_ns.namespace('bme680') bme680_ns = cg.esphome_ns.namespace("bme680")
BME680Oversampling = bme680_ns.enum('BME680Oversampling') BME680Oversampling = bme680_ns.enum("BME680Oversampling")
OVERSAMPLING_OPTIONS = { OVERSAMPLING_OPTIONS = {
'NONE': BME680Oversampling.BME680_OVERSAMPLING_NONE, "NONE": BME680Oversampling.BME680_OVERSAMPLING_NONE,
'1X': BME680Oversampling.BME680_OVERSAMPLING_1X, "1X": BME680Oversampling.BME680_OVERSAMPLING_1X,
'2X': BME680Oversampling.BME680_OVERSAMPLING_2X, "2X": BME680Oversampling.BME680_OVERSAMPLING_2X,
'4X': BME680Oversampling.BME680_OVERSAMPLING_4X, "4X": BME680Oversampling.BME680_OVERSAMPLING_4X,
'8X': BME680Oversampling.BME680_OVERSAMPLING_8X, "8X": BME680Oversampling.BME680_OVERSAMPLING_8X,
'16X': BME680Oversampling.BME680_OVERSAMPLING_16X, "16X": BME680Oversampling.BME680_OVERSAMPLING_16X,
} }
BME680IIRFilter = bme680_ns.enum('BME680IIRFilter') BME680IIRFilter = bme680_ns.enum("BME680IIRFilter")
IIR_FILTER_OPTIONS = { IIR_FILTER_OPTIONS = {
'OFF': BME680IIRFilter.BME680_IIR_FILTER_OFF, "OFF": BME680IIRFilter.BME680_IIR_FILTER_OFF,
'1X': BME680IIRFilter.BME680_IIR_FILTER_1X, "1X": BME680IIRFilter.BME680_IIR_FILTER_1X,
'3X': BME680IIRFilter.BME680_IIR_FILTER_3X, "3X": BME680IIRFilter.BME680_IIR_FILTER_3X,
'7X': BME680IIRFilter.BME680_IIR_FILTER_7X, "7X": BME680IIRFilter.BME680_IIR_FILTER_7X,
'15X': BME680IIRFilter.BME680_IIR_FILTER_15X, "15X": BME680IIRFilter.BME680_IIR_FILTER_15X,
'31X': BME680IIRFilter.BME680_IIR_FILTER_31X, "31X": BME680IIRFilter.BME680_IIR_FILTER_31X,
'63X': BME680IIRFilter.BME680_IIR_FILTER_63X, "63X": BME680IIRFilter.BME680_IIR_FILTER_63X,
'127X': BME680IIRFilter.BME680_IIR_FILTER_127X, "127X": BME680IIRFilter.BME680_IIR_FILTER_127X,
} }
BME680Component = bme680_ns.class_('BME680Component', cg.PollingComponent, i2c.I2CDevice) BME680Component = bme680_ns.class_(
"BME680Component", cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(BME680Component), cv.Schema(
cv.Optional(CONF_TEMPERATURE): {
sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ cv.GenerateID(): cv.declare_id(BME680Component),
cv.Optional(CONF_OVERSAMPLING, default='16X'): cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
cv.enum(OVERSAMPLING_OPTIONS, upper=True), UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
}), ).extend(
cv.Optional(CONF_PRESSURE): {
sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1).extend({ cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
cv.Optional(CONF_OVERSAMPLING, default='16X'): OVERSAMPLING_OPTIONS, upper=True
cv.enum(OVERSAMPLING_OPTIONS, upper=True), ),
}), }
cv.Optional(CONF_HUMIDITY): ),
sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1).extend({ cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
cv.Optional(CONF_OVERSAMPLING, default='16X'): UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE
cv.enum(OVERSAMPLING_OPTIONS, upper=True), ).extend(
}), {
cv.Optional(CONF_GAS_RESISTANCE): cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
sensor.sensor_schema(UNIT_OHM, ICON_GAS_CYLINDER, 1), OVERSAMPLING_OPTIONS, upper=True
cv.Optional(CONF_IIR_FILTER, default='OFF'): cv.enum(IIR_FILTER_OPTIONS, upper=True), ),
cv.Optional(CONF_HEATER): cv.Any(None, cv.All(cv.Schema({ }
cv.Optional(CONF_TEMPERATURE, default=320): cv.int_range(min=200, max=400), ),
cv.Optional(CONF_DURATION, default='150ms'): cv.All( cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
cv.positive_time_period_milliseconds, cv.Range(max=core.TimePeriod(milliseconds=4032))) UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY
}), cv.has_at_least_one_key(CONF_TEMPERATURE, CONF_DURATION))), ).extend(
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x76)) {
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
OVERSAMPLING_OPTIONS, upper=True
),
}
),
cv.Optional(CONF_GAS_RESISTANCE): sensor.sensor_schema(
UNIT_OHM, ICON_GAS_CYLINDER, 1, DEVICE_CLASS_EMPTY
),
cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
IIR_FILTER_OPTIONS, upper=True
),
cv.Optional(CONF_HEATER): cv.Any(
None,
cv.All(
cv.Schema(
{
cv.Optional(CONF_TEMPERATURE, default=320): cv.int_range(
min=200, max=400
),
cv.Optional(CONF_DURATION, default="150ms"): cv.All(
cv.positive_time_period_milliseconds,
cv.Range(max=core.TimePeriod(milliseconds=4032)),
),
}
),
cv.has_at_least_one_key(CONF_TEMPERATURE, CONF_DURATION),
),
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x76))
)
def to_code(config): def to_code(config):

View file

@ -1,19 +1,39 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import i2c, sensor from esphome.components import i2c, sensor
from esphome.const import CONF_ID, CONF_PRESSURE, CONF_TEMPERATURE, \ from esphome.const import (
UNIT_CELSIUS, ICON_THERMOMETER, ICON_GAUGE, UNIT_HECTOPASCAL CONF_ID,
CONF_PRESSURE,
CONF_TEMPERATURE,
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
UNIT_CELSIUS,
ICON_EMPTY,
UNIT_HECTOPASCAL,
)
DEPENDENCIES = ['i2c'] DEPENDENCIES = ["i2c"]
bmp085_ns = cg.esphome_ns.namespace('bmp085') bmp085_ns = cg.esphome_ns.namespace("bmp085")
BMP085Component = bmp085_ns.class_('BMP085Component', cg.PollingComponent, i2c.I2CDevice) BMP085Component = bmp085_ns.class_(
"BMP085Component", cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(BMP085Component), cv.Schema(
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), {
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1), cv.GenerateID(): cv.declare_id(BMP085Component),
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x77)) cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x77))
)
def to_code(config): def to_code(config):

View file

@ -1,44 +1,75 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import i2c, sensor from esphome.components import i2c, sensor
from esphome.const import CONF_ID, CONF_PRESSURE, CONF_TEMPERATURE, \ from esphome.const import (
UNIT_CELSIUS, ICON_THERMOMETER, ICON_GAUGE, UNIT_HECTOPASCAL, \ CONF_ID,
CONF_IIR_FILTER, CONF_OVERSAMPLING CONF_PRESSURE,
CONF_TEMPERATURE,
DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TEMPERATURE,
UNIT_CELSIUS,
ICON_EMPTY,
UNIT_HECTOPASCAL,
CONF_IIR_FILTER,
CONF_OVERSAMPLING,
)
DEPENDENCIES = ['i2c'] DEPENDENCIES = ["i2c"]
bmp280_ns = cg.esphome_ns.namespace('bmp280') bmp280_ns = cg.esphome_ns.namespace("bmp280")
BMP280Oversampling = bmp280_ns.enum('BMP280Oversampling') BMP280Oversampling = bmp280_ns.enum("BMP280Oversampling")
OVERSAMPLING_OPTIONS = { OVERSAMPLING_OPTIONS = {
'NONE': BMP280Oversampling.BMP280_OVERSAMPLING_NONE, "NONE": BMP280Oversampling.BMP280_OVERSAMPLING_NONE,
'1X': BMP280Oversampling.BMP280_OVERSAMPLING_1X, "1X": BMP280Oversampling.BMP280_OVERSAMPLING_1X,
'2X': BMP280Oversampling.BMP280_OVERSAMPLING_2X, "2X": BMP280Oversampling.BMP280_OVERSAMPLING_2X,
'4X': BMP280Oversampling.BMP280_OVERSAMPLING_4X, "4X": BMP280Oversampling.BMP280_OVERSAMPLING_4X,
'8X': BMP280Oversampling.BMP280_OVERSAMPLING_8X, "8X": BMP280Oversampling.BMP280_OVERSAMPLING_8X,
'16X': BMP280Oversampling.BMP280_OVERSAMPLING_16X, "16X": BMP280Oversampling.BMP280_OVERSAMPLING_16X,
} }
BMP280IIRFilter = bmp280_ns.enum('BMP280IIRFilter') BMP280IIRFilter = bmp280_ns.enum("BMP280IIRFilter")
IIR_FILTER_OPTIONS = { IIR_FILTER_OPTIONS = {
'OFF': BMP280IIRFilter.BMP280_IIR_FILTER_OFF, "OFF": BMP280IIRFilter.BMP280_IIR_FILTER_OFF,
'2X': BMP280IIRFilter.BMP280_IIR_FILTER_2X, "2X": BMP280IIRFilter.BMP280_IIR_FILTER_2X,
'4X': BMP280IIRFilter.BMP280_IIR_FILTER_4X, "4X": BMP280IIRFilter.BMP280_IIR_FILTER_4X,
'8X': BMP280IIRFilter.BMP280_IIR_FILTER_8X, "8X": BMP280IIRFilter.BMP280_IIR_FILTER_8X,
'16X': BMP280IIRFilter.BMP280_IIR_FILTER_16X, "16X": BMP280IIRFilter.BMP280_IIR_FILTER_16X,
} }
BMP280Component = bmp280_ns.class_('BMP280Component', cg.PollingComponent, i2c.I2CDevice) BMP280Component = bmp280_ns.class_(
"BMP280Component", cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(BMP280Component), cv.Schema(
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ {
cv.Optional(CONF_OVERSAMPLING, default='16X'): cv.enum(OVERSAMPLING_OPTIONS, upper=True), cv.GenerateID(): cv.declare_id(BMP280Component),
}), cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1).extend({ UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
cv.Optional(CONF_OVERSAMPLING, default='16X'): cv.enum(OVERSAMPLING_OPTIONS, upper=True), ).extend(
}), {
cv.Optional(CONF_IIR_FILTER, default='OFF'): cv.enum(IIR_FILTER_OPTIONS, upper=True), cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x77)) OVERSAMPLING_OPTIONS, upper=True
),
}
),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
OVERSAMPLING_OPTIONS, upper=True
),
}
),
cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
IIR_FILTER_OPTIONS, upper=True
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x77))
)
def to_code(config): def to_code(config):

View file

@ -4,68 +4,82 @@ from esphome import automation
from esphome.core import CORE, coroutine from esphome.core import CORE, coroutine
from esphome.const import CONF_ID, CONF_TRIGGER_ID, CONF_DATA from esphome.const import CONF_ID, CONF_TRIGGER_ID, CONF_DATA
CODEOWNERS = ['@mvturnho', '@danielschramm'] CODEOWNERS = ["@mvturnho", "@danielschramm"]
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
CONF_CAN_ID = 'can_id' CONF_CAN_ID = "can_id"
CONF_USE_EXTENDED_ID = 'use_extended_id' CONF_USE_EXTENDED_ID = "use_extended_id"
CONF_CANBUS_ID = 'canbus_id' CONF_CANBUS_ID = "canbus_id"
CONF_BIT_RATE = 'bit_rate' CONF_BIT_RATE = "bit_rate"
CONF_ON_FRAME = 'on_frame' CONF_ON_FRAME = "on_frame"
CONF_CANBUS_SEND = 'canbus.send'
def validate_id(id_value, id_ext): def validate_id(id_value, id_ext):
if not id_ext: if not id_ext:
if id_value > 0x7ff: if id_value > 0x7FF:
raise cv.Invalid("Standard IDs must be 11 Bit (0x000-0x7ff / 0-2047)") raise cv.Invalid("Standard IDs must be 11 Bit (0x000-0x7ff / 0-2047)")
def validate_raw_data(value): def validate_raw_data(value):
if isinstance(value, str): if isinstance(value, str):
return value.encode('utf-8') return value.encode("utf-8")
if isinstance(value, list): if isinstance(value, list):
return cv.Schema([cv.hex_uint8_t])(value) return cv.Schema([cv.hex_uint8_t])(value)
raise cv.Invalid("data must either be a string wrapped in quotes or a list of bytes") raise cv.Invalid(
"data must either be a string wrapped in quotes or a list of bytes"
)
canbus_ns = cg.esphome_ns.namespace('canbus') canbus_ns = cg.esphome_ns.namespace("canbus")
CanbusComponent = canbus_ns.class_('CanbusComponent', cg.Component) CanbusComponent = canbus_ns.class_("CanbusComponent", cg.Component)
CanbusTrigger = canbus_ns.class_('CanbusTrigger', CanbusTrigger = canbus_ns.class_(
automation.Trigger.template(cg.std_vector.template(cg.uint8)), "CanbusTrigger",
cg.Component) automation.Trigger.template(cg.std_vector.template(cg.uint8)),
CanSpeed = canbus_ns.enum('CAN_SPEED') cg.Component,
)
CanSpeed = canbus_ns.enum("CAN_SPEED")
CAN_SPEEDS = { CAN_SPEEDS = {
'5KBPS': CanSpeed.CAN_5KBPS, "5KBPS": CanSpeed.CAN_5KBPS,
'10KBPS': CanSpeed.CAN_10KBPS, "10KBPS": CanSpeed.CAN_10KBPS,
'20KBPS': CanSpeed.CAN_20KBPS, "20KBPS": CanSpeed.CAN_20KBPS,
'31K25BPS': CanSpeed.CAN_31K25BPS, "31K25BPS": CanSpeed.CAN_31K25BPS,
'33KBPS': CanSpeed.CAN_33KBPS, "33KBPS": CanSpeed.CAN_33KBPS,
'40KBPS': CanSpeed.CAN_40KBPS, "40KBPS": CanSpeed.CAN_40KBPS,
'50KBPS': CanSpeed.CAN_50KBPS, "50KBPS": CanSpeed.CAN_50KBPS,
'80KBPS': CanSpeed.CAN_80KBPS, "80KBPS": CanSpeed.CAN_80KBPS,
'83K3BPS': CanSpeed.CAN_83K3BPS, "83K3BPS": CanSpeed.CAN_83K3BPS,
'95KBPS': CanSpeed.CAN_95KBPS, "95KBPS": CanSpeed.CAN_95KBPS,
'100KBPS': CanSpeed.CAN_100KBPS, "100KBPS": CanSpeed.CAN_100KBPS,
'125KBPS': CanSpeed.CAN_125KBPS, "125KBPS": CanSpeed.CAN_125KBPS,
'200KBPS': CanSpeed.CAN_200KBPS, "200KBPS": CanSpeed.CAN_200KBPS,
'250KBPS': CanSpeed.CAN_250KBPS, "250KBPS": CanSpeed.CAN_250KBPS,
'500KBPS': CanSpeed.CAN_500KBPS, "500KBPS": CanSpeed.CAN_500KBPS,
'1000KBPS': CanSpeed.CAN_1000KBPS, "1000KBPS": CanSpeed.CAN_1000KBPS,
} }
CONFIG_SCHEMA = cv.Schema({ CANBUS_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(CanbusComponent), {
cv.Required(CONF_CAN_ID): cv.int_range(min=0, max=0x1fffffff), cv.GenerateID(): cv.declare_id(CanbusComponent),
cv.Optional(CONF_BIT_RATE, default='125KBPS'): cv.enum(CAN_SPEEDS, upper=True), cv.Required(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF),
cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean, cv.Optional(CONF_BIT_RATE, default="125KBPS"): cv.enum(CAN_SPEEDS, upper=True),
cv.Optional(CONF_ON_FRAME): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CanbusTrigger),
cv.GenerateID(CONF_CAN_ID): cv.int_range(min=0, max=0x1fffffff),
cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean, cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean,
}), cv.Optional(CONF_ON_FRAME): automation.validate_automation(
}).extend(cv.COMPONENT_SCHEMA) {
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CanbusTrigger),
cv.GenerateID(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF),
cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean,
cv.Optional(CONF_ON_FRAME): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(CanbusTrigger),
cv.GenerateID(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF),
cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean,
}
),
}
),
}
).extend(cv.COMPONENT_SCHEMA)
@coroutine @coroutine
@ -82,7 +96,9 @@ def setup_canbus_core_(var, config):
validate_id(can_id, ext_id) validate_id(can_id, ext_id)
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, can_id, ext_id) trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, can_id, ext_id)
yield cg.register_component(trigger, conf) yield cg.register_component(trigger, conf)
yield automation.build_automation(trigger, [(cg.std_vector.template(cg.uint8), 'x')], conf) yield automation.build_automation(
trigger, [(cg.std_vector.template(cg.uint8), "x")], conf
)
@coroutine @coroutine
@ -93,14 +109,19 @@ def register_canbus(var, config):
# Actions # Actions
@automation.register_action(CONF_CANBUS_SEND, @automation.register_action(
canbus_ns.class_('CanbusSendAction', automation.Action), "canbus.send",
cv.maybe_simple_value({ canbus_ns.class_("CanbusSendAction", automation.Action),
cv.GenerateID(CONF_CANBUS_ID): cv.use_id(CanbusComponent), cv.maybe_simple_value(
cv.Optional(CONF_CAN_ID): cv.int_range(min=0, max=0x1fffffff), {
cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean, cv.GenerateID(CONF_CANBUS_ID): cv.use_id(CanbusComponent),
cv.Required(CONF_DATA): cv.templatable(validate_raw_data), cv.Optional(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF),
}, key=CONF_DATA)) cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean,
cv.Required(CONF_DATA): cv.templatable(validate_raw_data),
},
key=CONF_DATA,
),
)
def canbus_action_to_code(config, action_id, template_arg, args): def canbus_action_to_code(config, action_id, template_arg, args):
validate_id(config[CONF_CAN_ID], config[CONF_USE_EXTENDED_ID]) validate_id(config[CONF_CAN_ID], config[CONF_USE_EXTENDED_ID])
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
@ -110,7 +131,9 @@ def canbus_action_to_code(config, action_id, template_arg, args):
can_id = yield cg.templatable(config[CONF_CAN_ID], args, cg.uint32) can_id = yield cg.templatable(config[CONF_CAN_ID], args, cg.uint32)
cg.add(var.set_can_id(can_id)) cg.add(var.set_can_id(can_id))
use_extended_id = yield cg.templatable(config[CONF_USE_EXTENDED_ID], args, cg.uint32) use_extended_id = yield cg.templatable(
config[CONF_USE_EXTENDED_ID], args, cg.uint32
)
cg.add(var.set_use_extended_id(use_extended_id)) cg.add(var.set_use_extended_id(use_extended_id))
data = config[CONF_DATA] data = config[CONF_DATA]

View file

@ -5,17 +5,21 @@ from esphome.components.web_server_base import CONF_WEB_SERVER_BASE_ID
from esphome.const import CONF_ID from esphome.const import CONF_ID
from esphome.core import coroutine_with_priority from esphome.core import coroutine_with_priority
AUTO_LOAD = ['web_server_base'] AUTO_LOAD = ["web_server_base"]
DEPENDENCIES = ['wifi'] DEPENDENCIES = ["wifi"]
CODEOWNERS = ['@OttoWinter'] CODEOWNERS = ["@OttoWinter"]
captive_portal_ns = cg.esphome_ns.namespace('captive_portal') captive_portal_ns = cg.esphome_ns.namespace("captive_portal")
CaptivePortal = captive_portal_ns.class_('CaptivePortal', cg.Component) CaptivePortal = captive_portal_ns.class_("CaptivePortal", cg.Component)
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(CaptivePortal), {
cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id(web_server_base.WebServerBase), cv.GenerateID(): cv.declare_id(CaptivePortal),
}).extend(cv.COMPONENT_SCHEMA) cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id(
web_server_base.WebServerBase
),
}
).extend(cv.COMPONENT_SCHEMA)
@coroutine_with_priority(64.0) @coroutine_with_priority(64.0)
@ -24,4 +28,4 @@ def to_code(config):
var = cg.new_Pvariable(config[CONF_ID], paren) var = cg.new_Pvariable(config[CONF_ID], paren)
yield cg.register_component(var, config) yield cg.register_component(var, config)
cg.add_define('USE_CAPTIVE_PORTAL') cg.add_define("USE_CAPTIVE_PORTAL")

View file

@ -1,28 +1,46 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import i2c, sensor from esphome.components import i2c, sensor
from esphome.const import CONF_ID, ICON_RADIATOR, UNIT_PARTS_PER_MILLION, \ from esphome.const import (
UNIT_PARTS_PER_BILLION, CONF_TEMPERATURE, CONF_HUMIDITY, ICON_MOLECULE_CO2 CONF_ID,
DEVICE_CLASS_EMPTY,
ICON_RADIATOR,
UNIT_PARTS_PER_MILLION,
UNIT_PARTS_PER_BILLION,
CONF_TEMPERATURE,
CONF_TVOC,
CONF_HUMIDITY,
ICON_MOLECULE_CO2,
)
DEPENDENCIES = ['i2c'] DEPENDENCIES = ["i2c"]
ccs811_ns = cg.esphome_ns.namespace('ccs811') ccs811_ns = cg.esphome_ns.namespace("ccs811")
CCS811Component = ccs811_ns.class_('CCS811Component', cg.PollingComponent, i2c.I2CDevice) CCS811Component = ccs811_ns.class_(
"CCS811Component", cg.PollingComponent, i2c.I2CDevice
)
CONF_ECO2 = 'eco2' CONF_ECO2 = "eco2"
CONF_TVOC = 'tvoc' CONF_BASELINE = "baseline"
CONF_BASELINE = 'baseline'
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(CCS811Component), cv.Schema(
cv.Required(CONF_ECO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, {
0), cv.GenerateID(): cv.declare_id(CCS811Component),
cv.Required(CONF_TVOC): sensor.sensor_schema(UNIT_PARTS_PER_BILLION, ICON_RADIATOR, 0), cv.Required(CONF_ECO2): sensor.sensor_schema(
UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0, DEVICE_CLASS_EMPTY
cv.Optional(CONF_BASELINE): cv.hex_uint16_t, ),
cv.Optional(CONF_TEMPERATURE): cv.use_id(sensor.Sensor), cv.Required(CONF_TVOC): sensor.sensor_schema(
cv.Optional(CONF_HUMIDITY): cv.use_id(sensor.Sensor), UNIT_PARTS_PER_BILLION, ICON_RADIATOR, 0, DEVICE_CLASS_EMPTY
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x5A)) ),
cv.Optional(CONF_BASELINE): cv.hex_uint16_t,
cv.Optional(CONF_TEMPERATURE): cv.use_id(sensor.Sensor),
cv.Optional(CONF_HUMIDITY): cv.use_id(sensor.Sensor),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x5A))
)
def to_code(config): def to_code(config):

View file

@ -2,70 +2,87 @@ import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import automation from esphome import automation
from esphome.components import mqtt from esphome.components import mqtt
from esphome.const import CONF_AWAY, CONF_ID, CONF_INTERNAL, CONF_MAX_TEMPERATURE, \ from esphome.const import (
CONF_MIN_TEMPERATURE, CONF_MODE, CONF_TARGET_TEMPERATURE, \ CONF_AWAY,
CONF_TARGET_TEMPERATURE_HIGH, CONF_TARGET_TEMPERATURE_LOW, CONF_TEMPERATURE_STEP, CONF_VISUAL, \ CONF_ID,
CONF_MQTT_ID, CONF_NAME, CONF_FAN_MODE, CONF_SWING_MODE CONF_INTERNAL,
CONF_MAX_TEMPERATURE,
CONF_MIN_TEMPERATURE,
CONF_MODE,
CONF_TARGET_TEMPERATURE,
CONF_TARGET_TEMPERATURE_HIGH,
CONF_TARGET_TEMPERATURE_LOW,
CONF_TEMPERATURE_STEP,
CONF_VISUAL,
CONF_MQTT_ID,
CONF_NAME,
CONF_FAN_MODE,
CONF_SWING_MODE,
)
from esphome.core import CORE, coroutine, coroutine_with_priority from esphome.core import CORE, coroutine, coroutine_with_priority
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
CODEOWNERS = ['@esphome/core'] CODEOWNERS = ["@esphome/core"]
climate_ns = cg.esphome_ns.namespace('climate') climate_ns = cg.esphome_ns.namespace("climate")
Climate = climate_ns.class_('Climate', cg.Nameable) Climate = climate_ns.class_("Climate", cg.Nameable)
ClimateCall = climate_ns.class_('ClimateCall') ClimateCall = climate_ns.class_("ClimateCall")
ClimateTraits = climate_ns.class_('ClimateTraits') ClimateTraits = climate_ns.class_("ClimateTraits")
ClimateMode = climate_ns.enum('ClimateMode') ClimateMode = climate_ns.enum("ClimateMode")
CLIMATE_MODES = { CLIMATE_MODES = {
'OFF': ClimateMode.CLIMATE_MODE_OFF, "OFF": ClimateMode.CLIMATE_MODE_OFF,
'AUTO': ClimateMode.CLIMATE_MODE_AUTO, "AUTO": ClimateMode.CLIMATE_MODE_AUTO,
'COOL': ClimateMode.CLIMATE_MODE_COOL, "COOL": ClimateMode.CLIMATE_MODE_COOL,
'HEAT': ClimateMode.CLIMATE_MODE_HEAT, "HEAT": ClimateMode.CLIMATE_MODE_HEAT,
'DRY': ClimateMode.CLIMATE_MODE_DRY, "DRY": ClimateMode.CLIMATE_MODE_DRY,
'FAN_ONLY': ClimateMode.CLIMATE_MODE_FAN_ONLY, "FAN_ONLY": ClimateMode.CLIMATE_MODE_FAN_ONLY,
} }
validate_climate_mode = cv.enum(CLIMATE_MODES, upper=True) validate_climate_mode = cv.enum(CLIMATE_MODES, upper=True)
ClimateFanMode = climate_ns.enum('ClimateFanMode') ClimateFanMode = climate_ns.enum("ClimateFanMode")
CLIMATE_FAN_MODES = { CLIMATE_FAN_MODES = {
'ON': ClimateFanMode.CLIMATE_FAN_ON, "ON": ClimateFanMode.CLIMATE_FAN_ON,
'OFF': ClimateFanMode.CLIMATE_FAN_OFF, "OFF": ClimateFanMode.CLIMATE_FAN_OFF,
'AUTO': ClimateFanMode.CLIMATE_FAN_AUTO, "AUTO": ClimateFanMode.CLIMATE_FAN_AUTO,
'LOW': ClimateFanMode.CLIMATE_FAN_LOW, "LOW": ClimateFanMode.CLIMATE_FAN_LOW,
'MEDIUM': ClimateFanMode.CLIMATE_FAN_MEDIUM, "MEDIUM": ClimateFanMode.CLIMATE_FAN_MEDIUM,
'HIGH': ClimateFanMode.CLIMATE_FAN_HIGH, "HIGH": ClimateFanMode.CLIMATE_FAN_HIGH,
'MIDDLE': ClimateFanMode.CLIMATE_FAN_MIDDLE, "MIDDLE": ClimateFanMode.CLIMATE_FAN_MIDDLE,
'FOCUS': ClimateFanMode.CLIMATE_FAN_FOCUS, "FOCUS": ClimateFanMode.CLIMATE_FAN_FOCUS,
'DIFFUSE': ClimateFanMode.CLIMATE_FAN_DIFFUSE, "DIFFUSE": ClimateFanMode.CLIMATE_FAN_DIFFUSE,
} }
validate_climate_fan_mode = cv.enum(CLIMATE_FAN_MODES, upper=True) validate_climate_fan_mode = cv.enum(CLIMATE_FAN_MODES, upper=True)
ClimateSwingMode = climate_ns.enum('ClimateSwingMode') ClimateSwingMode = climate_ns.enum("ClimateSwingMode")
CLIMATE_SWING_MODES = { CLIMATE_SWING_MODES = {
'OFF': ClimateSwingMode.CLIMATE_SWING_OFF, "OFF": ClimateSwingMode.CLIMATE_SWING_OFF,
'BOTH': ClimateSwingMode.CLIMATE_SWING_BOTH, "BOTH": ClimateSwingMode.CLIMATE_SWING_BOTH,
'VERTICAL': ClimateSwingMode.CLIMATE_SWING_VERTICAL, "VERTICAL": ClimateSwingMode.CLIMATE_SWING_VERTICAL,
'HORIZONTAL': ClimateSwingMode.CLIMATE_SWING_HORIZONTAL, "HORIZONTAL": ClimateSwingMode.CLIMATE_SWING_HORIZONTAL,
} }
validate_climate_swing_mode = cv.enum(CLIMATE_SWING_MODES, upper=True) validate_climate_swing_mode = cv.enum(CLIMATE_SWING_MODES, upper=True)
# Actions # Actions
ControlAction = climate_ns.class_('ControlAction', automation.Action) ControlAction = climate_ns.class_("ControlAction", automation.Action)
CLIMATE_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ CLIMATE_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend(
cv.GenerateID(): cv.declare_id(Climate), {
cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTClimateComponent), cv.GenerateID(): cv.declare_id(Climate),
cv.Optional(CONF_VISUAL, default={}): cv.Schema({ cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTClimateComponent),
cv.Optional(CONF_MIN_TEMPERATURE): cv.temperature, cv.Optional(CONF_VISUAL, default={}): cv.Schema(
cv.Optional(CONF_MAX_TEMPERATURE): cv.temperature, {
cv.Optional(CONF_TEMPERATURE_STEP): cv.temperature, cv.Optional(CONF_MIN_TEMPERATURE): cv.temperature,
}), cv.Optional(CONF_MAX_TEMPERATURE): cv.temperature,
# TODO: MQTT topic options cv.Optional(CONF_TEMPERATURE_STEP): cv.temperature,
}) }
),
# TODO: MQTT topic options
}
)
@coroutine @coroutine
@ -94,19 +111,23 @@ def register_climate(var, config):
yield setup_climate_core_(var, config) yield setup_climate_core_(var, config)
CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema({ CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema(
cv.Required(CONF_ID): cv.use_id(Climate), {
cv.Optional(CONF_MODE): cv.templatable(validate_climate_mode), cv.Required(CONF_ID): cv.use_id(Climate),
cv.Optional(CONF_TARGET_TEMPERATURE): cv.templatable(cv.temperature), cv.Optional(CONF_MODE): cv.templatable(validate_climate_mode),
cv.Optional(CONF_TARGET_TEMPERATURE_LOW): cv.templatable(cv.temperature), cv.Optional(CONF_TARGET_TEMPERATURE): cv.templatable(cv.temperature),
cv.Optional(CONF_TARGET_TEMPERATURE_HIGH): cv.templatable(cv.temperature), cv.Optional(CONF_TARGET_TEMPERATURE_LOW): cv.templatable(cv.temperature),
cv.Optional(CONF_AWAY): cv.templatable(cv.boolean), cv.Optional(CONF_TARGET_TEMPERATURE_HIGH): cv.templatable(cv.temperature),
cv.Optional(CONF_FAN_MODE): cv.templatable(validate_climate_fan_mode), cv.Optional(CONF_AWAY): cv.templatable(cv.boolean),
cv.Optional(CONF_SWING_MODE): cv.templatable(validate_climate_swing_mode), cv.Optional(CONF_FAN_MODE): cv.templatable(validate_climate_fan_mode),
}) cv.Optional(CONF_SWING_MODE): cv.templatable(validate_climate_swing_mode),
}
)
@automation.register_action('climate.control', ControlAction, CLIMATE_CONTROL_ACTION_SCHEMA) @automation.register_action(
"climate.control", ControlAction, CLIMATE_CONTROL_ACTION_SCHEMA
)
def climate_control_to_code(config, action_id, template_arg, args): def climate_control_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID]) paren = yield cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren) var = cg.new_Pvariable(action_id, template_arg, paren)
@ -117,10 +138,14 @@ def climate_control_to_code(config, action_id, template_arg, args):
template_ = yield cg.templatable(config[CONF_TARGET_TEMPERATURE], args, float) template_ = yield cg.templatable(config[CONF_TARGET_TEMPERATURE], args, float)
cg.add(var.set_target_temperature(template_)) cg.add(var.set_target_temperature(template_))
if CONF_TARGET_TEMPERATURE_LOW in config: if CONF_TARGET_TEMPERATURE_LOW in config:
template_ = yield cg.templatable(config[CONF_TARGET_TEMPERATURE_LOW], args, float) template_ = yield cg.templatable(
config[CONF_TARGET_TEMPERATURE_LOW], args, float
)
cg.add(var.set_target_temperature_low(template_)) cg.add(var.set_target_temperature_low(template_))
if CONF_TARGET_TEMPERATURE_HIGH in config: if CONF_TARGET_TEMPERATURE_HIGH in config:
template_ = yield cg.templatable(config[CONF_TARGET_TEMPERATURE_HIGH], args, float) template_ = yield cg.templatable(
config[CONF_TARGET_TEMPERATURE_HIGH], args, float
)
cg.add(var.set_target_temperature_high(template_)) cg.add(var.set_target_temperature_high(template_))
if CONF_AWAY in config: if CONF_AWAY in config:
template_ = yield cg.templatable(config[CONF_AWAY], args, bool) template_ = yield cg.templatable(config[CONF_AWAY], args, bool)
@ -129,12 +154,14 @@ def climate_control_to_code(config, action_id, template_arg, args):
template_ = yield cg.templatable(config[CONF_FAN_MODE], args, ClimateFanMode) template_ = yield cg.templatable(config[CONF_FAN_MODE], args, ClimateFanMode)
cg.add(var.set_fan_mode(template_)) cg.add(var.set_fan_mode(template_))
if CONF_SWING_MODE in config: if CONF_SWING_MODE in config:
template_ = yield cg.templatable(config[CONF_SWING_MODE], args, ClimateSwingMode) template_ = yield cg.templatable(
config[CONF_SWING_MODE], args, ClimateSwingMode
)
cg.add(var.set_swing_mode(template_)) cg.add(var.set_swing_mode(template_))
yield var yield var
@coroutine_with_priority(100.0) @coroutine_with_priority(100.0)
def to_code(config): def to_code(config):
cg.add_define('USE_CLIMATE') cg.add_define("USE_CLIMATE")
cg.add_global(climate_ns.using) cg.add_global(climate_ns.using)

View file

@ -1,27 +1,42 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import climate, remote_transmitter, remote_receiver, sensor, remote_base from esphome.components import (
climate,
remote_transmitter,
remote_receiver,
sensor,
remote_base,
)
from esphome.components.remote_base import CONF_RECEIVER_ID, CONF_TRANSMITTER_ID from esphome.components.remote_base import CONF_RECEIVER_ID, CONF_TRANSMITTER_ID
from esphome.const import CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT, CONF_SENSOR from esphome.const import CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT, CONF_SENSOR
from esphome.core import coroutine from esphome.core import coroutine
AUTO_LOAD = ['sensor', 'remote_base'] AUTO_LOAD = ["sensor", "remote_base"]
CODEOWNERS = ['@glmnet'] CODEOWNERS = ["@glmnet"]
climate_ir_ns = cg.esphome_ns.namespace('climate_ir') climate_ir_ns = cg.esphome_ns.namespace("climate_ir")
ClimateIR = climate_ir_ns.class_('ClimateIR', climate.Climate, cg.Component, ClimateIR = climate_ir_ns.class_(
remote_base.RemoteReceiverListener) "ClimateIR", climate.Climate, cg.Component, remote_base.RemoteReceiverListener
)
CLIMATE_IR_SCHEMA = climate.CLIMATE_SCHEMA.extend({ CLIMATE_IR_SCHEMA = climate.CLIMATE_SCHEMA.extend(
cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(remote_transmitter.RemoteTransmitterComponent), {
cv.Optional(CONF_SUPPORTS_COOL, default=True): cv.boolean, cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(
cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean, remote_transmitter.RemoteTransmitterComponent
cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor), ),
}).extend(cv.COMPONENT_SCHEMA) cv.Optional(CONF_SUPPORTS_COOL, default=True): cv.boolean,
cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean,
cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor),
}
).extend(cv.COMPONENT_SCHEMA)
CLIMATE_IR_WITH_RECEIVER_SCHEMA = CLIMATE_IR_SCHEMA.extend({ CLIMATE_IR_WITH_RECEIVER_SCHEMA = CLIMATE_IR_SCHEMA.extend(
cv.Optional(CONF_RECEIVER_ID): cv.use_id(remote_receiver.RemoteReceiverComponent), {
}) cv.Optional(CONF_RECEIVER_ID): cv.use_id(
remote_receiver.RemoteReceiverComponent
),
}
)
@coroutine @coroutine

View file

@ -3,16 +3,45 @@ import esphome.config_validation as cv
from esphome.components import climate_ir from esphome.components import climate_ir
from esphome.const import CONF_ID from esphome.const import CONF_ID
AUTO_LOAD = ['climate_ir'] AUTO_LOAD = ["climate_ir"]
climate_ir_lg_ns = cg.esphome_ns.namespace('climate_ir_lg') climate_ir_lg_ns = cg.esphome_ns.namespace("climate_ir_lg")
LgIrClimate = climate_ir_lg_ns.class_('LgIrClimate', climate_ir.ClimateIR) LgIrClimate = climate_ir_lg_ns.class_("LgIrClimate", climate_ir.ClimateIR)
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend({ CONF_HEADER_HIGH = "header_high"
cv.GenerateID(): cv.declare_id(LgIrClimate), CONF_HEADER_LOW = "header_low"
}) CONF_BIT_HIGH = "bit_high"
CONF_BIT_ONE_LOW = "bit_one_low"
CONF_BIT_ZERO_LOW = "bit_zero_low"
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(LgIrClimate),
cv.Optional(
CONF_HEADER_HIGH, default="8000us"
): cv.positive_time_period_microseconds,
cv.Optional(
CONF_HEADER_LOW, default="4000us"
): cv.positive_time_period_microseconds,
cv.Optional(
CONF_BIT_HIGH, default="600us"
): cv.positive_time_period_microseconds,
cv.Optional(
CONF_BIT_ONE_LOW, default="1600us"
): cv.positive_time_period_microseconds,
cv.Optional(
CONF_BIT_ZERO_LOW, default="550us"
): cv.positive_time_period_microseconds,
}
)
def to_code(config): def to_code(config):
var = cg.new_Pvariable(config[CONF_ID]) var = cg.new_Pvariable(config[CONF_ID])
yield climate_ir.register_climate_ir(var, config) yield climate_ir.register_climate_ir(var, config)
cg.add(var.set_header_high(config[CONF_HEADER_HIGH]))
cg.add(var.set_header_low(config[CONF_HEADER_LOW]))
cg.add(var.set_bit_high(config[CONF_BIT_HIGH]))
cg.add(var.set_bit_one_low(config[CONF_BIT_ONE_LOW]))
cg.add(var.set_bit_zero_low(config[CONF_BIT_ZERO_LOW]))

View file

@ -9,6 +9,7 @@ static const char *TAG = "climate.climate_ir_lg";
const uint32_t COMMAND_ON = 0x00000; const uint32_t COMMAND_ON = 0x00000;
const uint32_t COMMAND_ON_AI = 0x03000; const uint32_t COMMAND_ON_AI = 0x03000;
const uint32_t COMMAND_COOL = 0x08000; const uint32_t COMMAND_COOL = 0x08000;
const uint32_t COMMAND_HEAT = 0x0C000;
const uint32_t COMMAND_OFF = 0xC0000; const uint32_t COMMAND_OFF = 0xC0000;
const uint32_t COMMAND_SWING = 0x10000; const uint32_t COMMAND_SWING = 0x10000;
// On, 25C, Mode: Auto, Fan: Auto, Zone Follow: Off, Sensor Temp: Ignore. // On, 25C, Mode: Auto, Fan: Auto, Zone Follow: Off, Sensor Temp: Ignore.
@ -28,13 +29,6 @@ const uint8_t TEMP_RANGE = TEMP_MAX - TEMP_MIN + 1;
const uint32_t TEMP_MASK = 0XF00; const uint32_t TEMP_MASK = 0XF00;
const uint32_t TEMP_SHIFT = 8; const uint32_t TEMP_SHIFT = 8;
// Constants
static const uint32_t HEADER_HIGH_US = 8000;
static const uint32_t HEADER_LOW_US = 4000;
static const uint32_t BIT_HIGH_US = 600;
static const uint32_t BIT_ONE_LOW_US = 1600;
static const uint32_t BIT_ZERO_LOW_US = 550;
const uint16_t BITS = 28; const uint16_t BITS = 28;
void LgIrClimate::transmit_state() { void LgIrClimate::transmit_state() {
@ -55,6 +49,9 @@ void LgIrClimate::transmit_state() {
case climate::CLIMATE_MODE_COOL: case climate::CLIMATE_MODE_COOL:
remote_state |= COMMAND_COOL; remote_state |= COMMAND_COOL;
break; break;
case climate::CLIMATE_MODE_HEAT:
remote_state |= COMMAND_HEAT;
break;
case climate::CLIMATE_MODE_AUTO: case climate::CLIMATE_MODE_AUTO:
remote_state |= COMMAND_AUTO; remote_state |= COMMAND_AUTO;
break; break;
@ -73,7 +70,8 @@ void LgIrClimate::transmit_state() {
if (this->mode == climate::CLIMATE_MODE_OFF) { if (this->mode == climate::CLIMATE_MODE_OFF) {
remote_state |= FAN_AUTO; remote_state |= FAN_AUTO;
} else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_DRY) { } else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_DRY ||
this->mode == climate::CLIMATE_MODE_HEAT) {
switch (this->fan_mode) { switch (this->fan_mode) {
case climate::CLIMATE_FAN_HIGH: case climate::CLIMATE_FAN_HIGH:
remote_state |= FAN_MAX; remote_state |= FAN_MAX;
@ -95,7 +93,7 @@ void LgIrClimate::transmit_state() {
this->fan_mode = climate::CLIMATE_FAN_AUTO; this->fan_mode = climate::CLIMATE_FAN_AUTO;
// remote_state |= FAN_MODE_AUTO_DRY; // remote_state |= FAN_MODE_AUTO_DRY;
} }
if (this->mode == climate::CLIMATE_MODE_COOL) { 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); remote_state |= ((temp - 15) << TEMP_SHIFT);
} }
@ -108,13 +106,13 @@ bool LgIrClimate::on_receive(remote_base::RemoteReceiveData data) {
uint8_t nbits = 0; uint8_t nbits = 0;
uint32_t remote_state = 0; uint32_t remote_state = 0;
if (!data.expect_item(HEADER_HIGH_US, HEADER_LOW_US)) if (!data.expect_item(this->header_high_, this->header_low_))
return false; return false;
for (nbits = 0; nbits < 32; nbits++) { for (nbits = 0; nbits < 32; nbits++) {
if (data.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) { if (data.expect_item(this->bit_high_, this->bit_one_low_)) {
remote_state = (remote_state << 1) | 1; remote_state = (remote_state << 1) | 1;
} else if (data.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) { } else if (data.expect_item(this->bit_high_, this->bit_zero_low_)) {
remote_state = (remote_state << 1) | 0; remote_state = (remote_state << 1) | 0;
} else if (nbits == BITS) { } else if (nbits == BITS) {
break; break;
@ -141,29 +139,32 @@ bool LgIrClimate::on_receive(remote_base::RemoteReceiveData data) {
} else { } else {
if ((remote_state & COMMAND_MASK) == COMMAND_AUTO) if ((remote_state & COMMAND_MASK) == COMMAND_AUTO)
this->mode = climate::CLIMATE_MODE_AUTO; this->mode = climate::CLIMATE_MODE_AUTO;
else if ((remote_state & COMMAND_MASK) == COMMAND_DRY_FAN) { else if ((remote_state & COMMAND_MASK) == COMMAND_DRY_FAN)
this->mode = climate::CLIMATE_MODE_DRY; this->mode = climate::CLIMATE_MODE_DRY;
else if ((remote_state & COMMAND_MASK) == COMMAND_HEAT) {
this->mode = climate::CLIMATE_MODE_HEAT;
} else { } else {
this->mode = climate::CLIMATE_MODE_COOL; this->mode = climate::CLIMATE_MODE_COOL;
} }
}
// Temperature // Temperature
if (this->mode == climate::CLIMATE_MODE_COOL) if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_HEAT)
this->target_temperature = ((remote_state & TEMP_MASK) >> TEMP_SHIFT) + 15; this->target_temperature = ((remote_state & TEMP_MASK) >> TEMP_SHIFT) + 15;
// Fan Speed // Fan Speed
if (this->mode == climate::CLIMATE_MODE_AUTO) { if (this->mode == climate::CLIMATE_MODE_AUTO) {
this->fan_mode = climate::CLIMATE_FAN_AUTO;
} else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_DRY) {
if ((remote_state & FAN_MASK) == FAN_AUTO)
this->fan_mode = climate::CLIMATE_FAN_AUTO; this->fan_mode = climate::CLIMATE_FAN_AUTO;
else if ((remote_state & FAN_MASK) == FAN_MIN) } else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_HEAT ||
this->fan_mode = climate::CLIMATE_FAN_LOW; this->mode == climate::CLIMATE_MODE_DRY) {
else if ((remote_state & FAN_MASK) == FAN_MED) if ((remote_state & FAN_MASK) == FAN_AUTO)
this->fan_mode = climate::CLIMATE_FAN_MEDIUM; this->fan_mode = climate::CLIMATE_FAN_AUTO;
else if ((remote_state & FAN_MASK) == FAN_MAX) else if ((remote_state & FAN_MASK) == FAN_MIN)
this->fan_mode = climate::CLIMATE_FAN_HIGH; this->fan_mode = climate::CLIMATE_FAN_LOW;
else if ((remote_state & FAN_MASK) == FAN_MED)
this->fan_mode = climate::CLIMATE_FAN_MEDIUM;
else if ((remote_state & FAN_MASK) == FAN_MAX)
this->fan_mode = climate::CLIMATE_FAN_HIGH;
}
} }
this->publish_state(); this->publish_state();
@ -179,15 +180,16 @@ void LgIrClimate::transmit_(uint32_t value) {
data->set_carrier_frequency(38000); data->set_carrier_frequency(38000);
data->reserve(2 + BITS * 2u); data->reserve(2 + BITS * 2u);
data->item(HEADER_HIGH_US, HEADER_LOW_US); data->item(this->header_high_, this->header_low_);
for (uint32_t mask = 1UL << (BITS - 1); mask != 0; mask >>= 1) { for (uint32_t mask = 1UL << (BITS - 1); mask != 0; mask >>= 1) {
if (value & mask) if (value & mask) {
data->item(BIT_HIGH_US, BIT_ONE_LOW_US); data->item(this->bit_high_, this->bit_one_low_);
else } else {
data->item(BIT_HIGH_US, BIT_ZERO_LOW_US); data->item(this->bit_high_, this->bit_zero_low_);
}
} }
data->mark(BIT_HIGH_US); data->mark(this->bit_high_);
transmit.perform(); transmit.perform();
} }
void LgIrClimate::calc_checksum_(uint32_t &value) { void LgIrClimate::calc_checksum_(uint32_t &value) {

View file

@ -25,6 +25,11 @@ class LgIrClimate : public climate_ir::ClimateIR {
this->swing_mode = climate::CLIMATE_SWING_OFF; this->swing_mode = climate::CLIMATE_SWING_OFF;
climate_ir::ClimateIR::control(call); climate_ir::ClimateIR::control(call);
} }
void set_header_high(uint32_t header_high) { this->header_high_ = header_high; }
void set_header_low(uint32_t header_low) { this->header_low_ = header_low; }
void set_bit_high(uint32_t bit_high) { this->bit_high_ = bit_high; }
void set_bit_one_low(uint32_t bit_one_low) { this->bit_one_low_ = bit_one_low; }
void set_bit_zero_low(uint32_t bit_zero_low) { this->bit_zero_low_ = bit_zero_low; }
protected: protected:
/// Transmit via IR the state of this climate controller. /// Transmit via IR the state of this climate controller.
@ -37,6 +42,12 @@ class LgIrClimate : public climate_ir::ClimateIR {
void calc_checksum_(uint32_t &value); void calc_checksum_(uint32_t &value);
void transmit_(uint32_t value); void transmit_(uint32_t value);
uint32_t header_high_;
uint32_t header_low_;
uint32_t bit_high_;
uint32_t bit_one_low_;
uint32_t bit_zero_low_;
climate::ClimateMode mode_before_{climate::CLIMATE_MODE_OFF}; climate::ClimateMode mode_before_{climate::CLIMATE_MODE_OFF};
}; };

View file

@ -2,22 +2,56 @@ from esphome import config_validation as cv
from esphome import codegen as cg from esphome import codegen as cg
from esphome.const import CONF_BLUE, CONF_GREEN, CONF_ID, CONF_RED, CONF_WHITE from esphome.const import CONF_BLUE, CONF_GREEN, CONF_ID, CONF_RED, CONF_WHITE
ColorStruct = cg.esphome_ns.struct('Color') ColorStruct = cg.esphome_ns.struct("Color")
MULTI_CONF = True MULTI_CONF = True
CONFIG_SCHEMA = cv.Schema({
cv.Required(CONF_ID): cv.declare_id(ColorStruct), CONF_RED_INT = "red_int"
cv.Optional(CONF_RED, default=0.0): cv.percentage, CONF_GREEN_INT = "green_int"
cv.Optional(CONF_GREEN, default=0.0): cv.percentage, CONF_BLUE_INT = "blue_int"
cv.Optional(CONF_BLUE, default=0.0): cv.percentage, CONF_WHITE_INT = "white_int"
cv.Optional(CONF_WHITE, default=0.0): cv.percentage,
}).extend(cv.COMPONENT_SCHEMA) CONFIG_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.declare_id(ColorStruct),
cv.Exclusive(CONF_RED, "red"): cv.percentage,
cv.Exclusive(CONF_RED_INT, "red"): cv.uint8_t,
cv.Exclusive(CONF_GREEN, "green"): cv.percentage,
cv.Exclusive(CONF_GREEN_INT, "green"): cv.uint8_t,
cv.Exclusive(CONF_BLUE, "blue"): cv.percentage,
cv.Exclusive(CONF_BLUE_INT, "blue"): cv.uint8_t,
cv.Exclusive(CONF_WHITE, "white"): cv.percentage,
cv.Exclusive(CONF_WHITE_INT, "white"): cv.uint8_t,
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config): def to_code(config):
cg.variable(config[CONF_ID], cg.StructInitializer( r = 0
ColorStruct, if CONF_RED in config:
('r', config[CONF_RED]), r = int(config[CONF_RED] * 255)
('g', config[CONF_GREEN]), elif CONF_RED_INT in config:
('b', config[CONF_BLUE]), r = config[CONF_RED_INT]
('w', config[CONF_WHITE])))
g = 0
if CONF_GREEN in config:
g = int(config[CONF_GREEN] * 255)
elif CONF_GREEN_INT in config:
g = config[CONF_GREEN_INT]
b = 0
if CONF_BLUE in config:
b = int(config[CONF_BLUE] * 255)
elif CONF_BLUE_INT in config:
b = config[CONF_BLUE_INT]
w = 0
if CONF_WHITE in config:
w = int(config[CONF_WHITE] * 255)
elif CONF_WHITE_INT in config:
w = config[CONF_WHITE_INT]
cg.new_variable(
config[CONF_ID],
cg.StructInitializer(ColorStruct, ("r", r), ("g", g), ("b", b), ("w", w)),
)

View file

@ -3,15 +3,17 @@ import esphome.config_validation as cv
from esphome.components import climate_ir from esphome.components import climate_ir
from esphome.const import CONF_ID from esphome.const import CONF_ID
AUTO_LOAD = ['climate_ir'] AUTO_LOAD = ["climate_ir"]
CODEOWNERS = ['@glmnet'] CODEOWNERS = ["@glmnet"]
coolix_ns = cg.esphome_ns.namespace('coolix') coolix_ns = cg.esphome_ns.namespace("coolix")
CoolixClimate = coolix_ns.class_('CoolixClimate', climate_ir.ClimateIR) CoolixClimate = coolix_ns.class_("CoolixClimate", climate_ir.ClimateIR)
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend({ CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
cv.GenerateID(): cv.declare_id(CoolixClimate), {
}) cv.GenerateID(): cv.declare_id(CoolixClimate),
}
)
def to_code(config): def to_code(config):

View file

@ -3,54 +3,74 @@ import esphome.config_validation as cv
from esphome import automation from esphome import automation
from esphome.automation import maybe_simple_id, Condition from esphome.automation import maybe_simple_id, Condition
from esphome.components import mqtt from esphome.components import mqtt
from esphome.const import CONF_ID, CONF_INTERNAL, CONF_DEVICE_CLASS, CONF_STATE, \ from esphome.const import (
CONF_POSITION, CONF_TILT, CONF_STOP, CONF_MQTT_ID, CONF_NAME CONF_ID,
CONF_INTERNAL,
CONF_DEVICE_CLASS,
CONF_STATE,
CONF_POSITION,
CONF_TILT,
CONF_STOP,
CONF_MQTT_ID,
CONF_NAME,
)
from esphome.core import CORE, coroutine, coroutine_with_priority from esphome.core import CORE, coroutine, coroutine_with_priority
IS_PLATFORM_COMPONENT = True IS_PLATFORM_COMPONENT = True
CODEOWNERS = ['@esphome/core'] CODEOWNERS = ["@esphome/core"]
DEVICE_CLASSES = [ DEVICE_CLASSES = [
'', 'awning', 'blind', 'curtain', 'damper', 'door', 'garage', "",
'gate', 'shade', 'shutter', 'window' "awning",
"blind",
"curtain",
"damper",
"door",
"garage",
"gate",
"shade",
"shutter",
"window",
] ]
cover_ns = cg.esphome_ns.namespace('cover') cover_ns = cg.esphome_ns.namespace("cover")
Cover = cover_ns.class_('Cover', cg.Nameable) Cover = cover_ns.class_("Cover", cg.Nameable)
COVER_OPEN = cover_ns.COVER_OPEN COVER_OPEN = cover_ns.COVER_OPEN
COVER_CLOSED = cover_ns.COVER_CLOSED COVER_CLOSED = cover_ns.COVER_CLOSED
COVER_STATES = { COVER_STATES = {
'OPEN': COVER_OPEN, "OPEN": COVER_OPEN,
'CLOSED': COVER_CLOSED, "CLOSED": COVER_CLOSED,
} }
validate_cover_state = cv.enum(COVER_STATES, upper=True) validate_cover_state = cv.enum(COVER_STATES, upper=True)
CoverOperation = cover_ns.enum('CoverOperation') CoverOperation = cover_ns.enum("CoverOperation")
COVER_OPERATIONS = { COVER_OPERATIONS = {
'IDLE': CoverOperation.COVER_OPERATION_IDLE, "IDLE": CoverOperation.COVER_OPERATION_IDLE,
'OPENING': CoverOperation.COVER_OPERATION_OPENING, "OPENING": CoverOperation.COVER_OPERATION_OPENING,
'CLOSING': CoverOperation.COVER_OPERATION_CLOSING, "CLOSING": CoverOperation.COVER_OPERATION_CLOSING,
} }
validate_cover_operation = cv.enum(COVER_OPERATIONS, upper=True) validate_cover_operation = cv.enum(COVER_OPERATIONS, upper=True)
# Actions # Actions
OpenAction = cover_ns.class_('OpenAction', automation.Action) OpenAction = cover_ns.class_("OpenAction", automation.Action)
CloseAction = cover_ns.class_('CloseAction', automation.Action) CloseAction = cover_ns.class_("CloseAction", automation.Action)
StopAction = cover_ns.class_('StopAction', automation.Action) StopAction = cover_ns.class_("StopAction", automation.Action)
ControlAction = cover_ns.class_('ControlAction', automation.Action) ControlAction = cover_ns.class_("ControlAction", automation.Action)
CoverPublishAction = cover_ns.class_('CoverPublishAction', automation.Action) CoverPublishAction = cover_ns.class_("CoverPublishAction", automation.Action)
CoverIsOpenCondition = cover_ns.class_('CoverIsOpenCondition', Condition) CoverIsOpenCondition = cover_ns.class_("CoverIsOpenCondition", Condition)
CoverIsClosedCondition = cover_ns.class_('CoverIsClosedCondition', Condition) CoverIsClosedCondition = cover_ns.class_("CoverIsClosedCondition", Condition)
COVER_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ COVER_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend(
cv.GenerateID(): cv.declare_id(Cover), {
cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTCoverComponent), cv.GenerateID(): cv.declare_id(Cover),
cv.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True), cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTCoverComponent),
# TODO: MQTT topic options cv.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True),
}) # TODO: MQTT topic options
}
)
@coroutine @coroutine
@ -74,39 +94,43 @@ def register_cover(var, config):
yield setup_cover_core_(var, config) yield setup_cover_core_(var, config)
COVER_ACTION_SCHEMA = maybe_simple_id({ COVER_ACTION_SCHEMA = maybe_simple_id(
cv.Required(CONF_ID): cv.use_id(Cover), {
}) cv.Required(CONF_ID): cv.use_id(Cover),
}
)
@automation.register_action('cover.open', OpenAction, COVER_ACTION_SCHEMA) @automation.register_action("cover.open", OpenAction, COVER_ACTION_SCHEMA)
def cover_open_to_code(config, action_id, template_arg, args): def cover_open_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID]) paren = yield cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(action_id, template_arg, paren) yield cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_action('cover.close', CloseAction, COVER_ACTION_SCHEMA) @automation.register_action("cover.close", CloseAction, COVER_ACTION_SCHEMA)
def cover_close_to_code(config, action_id, template_arg, args): def cover_close_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID]) paren = yield cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(action_id, template_arg, paren) yield cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_action('cover.stop', StopAction, COVER_ACTION_SCHEMA) @automation.register_action("cover.stop", StopAction, COVER_ACTION_SCHEMA)
def cover_stop_to_code(config, action_id, template_arg, args): def cover_stop_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID]) paren = yield cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(action_id, template_arg, paren) yield cg.new_Pvariable(action_id, template_arg, paren)
COVER_CONTROL_ACTION_SCHEMA = cv.Schema({ COVER_CONTROL_ACTION_SCHEMA = cv.Schema(
cv.Required(CONF_ID): cv.use_id(Cover), {
cv.Optional(CONF_STOP): cv.templatable(cv.boolean), cv.Required(CONF_ID): cv.use_id(Cover),
cv.Exclusive(CONF_STATE, 'pos'): cv.templatable(validate_cover_state), cv.Optional(CONF_STOP): cv.templatable(cv.boolean),
cv.Exclusive(CONF_POSITION, 'pos'): cv.templatable(cv.percentage), cv.Exclusive(CONF_STATE, "pos"): cv.templatable(validate_cover_state),
cv.Optional(CONF_TILT): cv.templatable(cv.percentage), cv.Exclusive(CONF_POSITION, "pos"): cv.templatable(cv.percentage),
}) cv.Optional(CONF_TILT): cv.templatable(cv.percentage),
}
)
@automation.register_action('cover.control', ControlAction, COVER_CONTROL_ACTION_SCHEMA) @automation.register_action("cover.control", ControlAction, COVER_CONTROL_ACTION_SCHEMA)
def cover_control_to_code(config, action_id, template_arg, args): def cover_control_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID]) paren = yield cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren) var = cg.new_Pvariable(action_id, template_arg, paren)
@ -127,5 +151,5 @@ def cover_control_to_code(config, action_id, template_arg, args):
@coroutine_with_priority(100.0) @coroutine_with_priority(100.0)
def to_code(config): def to_code(config):
cg.add_define('USE_COVER') cg.add_define("USE_COVER")
cg.add_global(cover_ns.using) cg.add_global(cover_ns.using)

View file

@ -1,21 +1,45 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor, uart from esphome.components import sensor, uart
from esphome.const import CONF_CURRENT, CONF_ID, CONF_POWER, CONF_VOLTAGE, \ from esphome.const import (
UNIT_VOLT, ICON_FLASH, UNIT_AMPERE, UNIT_WATT CONF_CURRENT,
CONF_ID,
CONF_POWER,
CONF_VOLTAGE,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
UNIT_VOLT,
UNIT_AMPERE,
UNIT_WATT,
)
DEPENDENCIES = ['uart'] DEPENDENCIES = ["uart"]
cse7766_ns = cg.esphome_ns.namespace('cse7766') cse7766_ns = cg.esphome_ns.namespace("cse7766")
CSE7766Component = cse7766_ns.class_('CSE7766Component', cg.PollingComponent, uart.UARTDevice) CSE7766Component = cse7766_ns.class_(
"CSE7766Component", cg.PollingComponent, uart.UARTDevice
)
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(CSE7766Component), cv.Schema(
{
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1), cv.GenerateID(): cv.declare_id(CSE7766Component),
cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2), cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 1), UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE
}).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA) ),
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT
),
cv.Optional(CONF_POWER): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(uart.UART_DEVICE_SCHEMA)
)
def to_code(config): def to_code(config):

View file

@ -1,21 +1,35 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor, voltage_sampler from esphome.components import sensor, voltage_sampler
from esphome.const import CONF_SENSOR, CONF_ID, ICON_FLASH, UNIT_AMPERE from esphome.const import (
CONF_SENSOR,
CONF_ID,
DEVICE_CLASS_CURRENT,
ICON_EMPTY,
UNIT_AMPERE,
)
AUTO_LOAD = ['voltage_sampler'] AUTO_LOAD = ["voltage_sampler"]
CODEOWNERS = ['@jesserockz'] CODEOWNERS = ["@jesserockz"]
CONF_SAMPLE_DURATION = 'sample_duration' CONF_SAMPLE_DURATION = "sample_duration"
ct_clamp_ns = cg.esphome_ns.namespace('ct_clamp') ct_clamp_ns = cg.esphome_ns.namespace("ct_clamp")
CTClampSensor = ct_clamp_ns.class_('CTClampSensor', sensor.Sensor, cg.PollingComponent) CTClampSensor = ct_clamp_ns.class_("CTClampSensor", sensor.Sensor, cg.PollingComponent)
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2).extend({ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(CTClampSensor), sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT)
cv.Required(CONF_SENSOR): cv.use_id(voltage_sampler.VoltageSampler), .extend(
cv.Optional(CONF_SAMPLE_DURATION, default='200ms'): cv.positive_time_period_milliseconds, {
}).extend(cv.polling_component_schema('60s')) cv.GenerateID(): cv.declare_id(CTClampSensor),
cv.Required(CONF_SENSOR): cv.use_id(voltage_sampler.VoltageSampler),
cv.Optional(
CONF_SAMPLE_DURATION, default="200ms"
): cv.positive_time_period_milliseconds,
}
)
.extend(cv.polling_component_schema("60s"))
)
def to_code(config): def to_code(config):

View file

@ -1,3 +1,3 @@
import esphome.codegen as cg import esphome.codegen as cg
custom_ns = cg.esphome_ns.namespace('custom') custom_ns = cg.esphome_ns.namespace("custom")

View file

@ -4,18 +4,25 @@ from esphome.components import binary_sensor
from esphome.const import CONF_BINARY_SENSORS, CONF_ID, CONF_LAMBDA from esphome.const import CONF_BINARY_SENSORS, CONF_ID, CONF_LAMBDA
from .. import custom_ns from .. import custom_ns
CustomBinarySensorConstructor = custom_ns.class_('CustomBinarySensorConstructor') CustomBinarySensorConstructor = custom_ns.class_("CustomBinarySensorConstructor")
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(CustomBinarySensorConstructor), {
cv.Required(CONF_LAMBDA): cv.returning_lambda, cv.GenerateID(): cv.declare_id(CustomBinarySensorConstructor),
cv.Required(CONF_BINARY_SENSORS): cv.ensure_list(binary_sensor.BINARY_SENSOR_SCHEMA), cv.Required(CONF_LAMBDA): cv.returning_lambda,
}) cv.Required(CONF_BINARY_SENSORS): cv.ensure_list(
binary_sensor.BINARY_SENSOR_SCHEMA
),
}
)
def to_code(config): def to_code(config):
template_ = yield cg.process_lambda( template_ = yield cg.process_lambda(
config[CONF_LAMBDA], [], return_type=cg.std_vector.template(binary_sensor.BinarySensorPtr)) config[CONF_LAMBDA],
[],
return_type=cg.std_vector.template(binary_sensor.BinarySensorPtr),
)
rhs = CustomBinarySensorConstructor(template_) rhs = CustomBinarySensorConstructor(template_)
custom = cg.variable(config[CONF_ID], rhs) custom = cg.variable(config[CONF_ID], rhs)

View file

@ -4,20 +4,24 @@ from esphome.components import climate
from esphome.const import CONF_ID, CONF_LAMBDA from esphome.const import CONF_ID, CONF_LAMBDA
from .. import custom_ns from .. import custom_ns
CustomClimateConstructor = custom_ns.class_('CustomClimateConstructor') CustomClimateConstructor = custom_ns.class_("CustomClimateConstructor")
CONF_CLIMATES = 'climates' CONF_CLIMATES = "climates"
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(CustomClimateConstructor), {
cv.Required(CONF_LAMBDA): cv.returning_lambda, cv.GenerateID(): cv.declare_id(CustomClimateConstructor),
cv.Required(CONF_CLIMATES): cv.ensure_list(climate.CLIMATE_SCHEMA), cv.Required(CONF_LAMBDA): cv.returning_lambda,
}) cv.Required(CONF_CLIMATES): cv.ensure_list(climate.CLIMATE_SCHEMA),
}
)
def to_code(config): def to_code(config):
template_ = yield cg.process_lambda( template_ = yield cg.process_lambda(
config[CONF_LAMBDA], [], config[CONF_LAMBDA],
return_type=cg.std_vector.template(climate.Climate.operator('ptr'))) [],
return_type=cg.std_vector.template(climate.Climate.operator("ptr")),
)
rhs = CustomClimateConstructor(template_) rhs = CustomClimateConstructor(template_)
custom = cg.variable(config[CONF_ID], rhs) custom = cg.variable(config[CONF_ID], rhs)

View file

@ -4,20 +4,24 @@ from esphome.components import cover
from esphome.const import CONF_ID, CONF_LAMBDA from esphome.const import CONF_ID, CONF_LAMBDA
from .. import custom_ns from .. import custom_ns
CustomCoverConstructor = custom_ns.class_('CustomCoverConstructor') CustomCoverConstructor = custom_ns.class_("CustomCoverConstructor")
CONF_COVERS = 'covers' CONF_COVERS = "covers"
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(CustomCoverConstructor), {
cv.Required(CONF_LAMBDA): cv.returning_lambda, cv.GenerateID(): cv.declare_id(CustomCoverConstructor),
cv.Required(CONF_COVERS): cv.ensure_list(cover.COVER_SCHEMA), cv.Required(CONF_LAMBDA): cv.returning_lambda,
}) cv.Required(CONF_COVERS): cv.ensure_list(cover.COVER_SCHEMA),
}
)
def to_code(config): def to_code(config):
template_ = yield cg.process_lambda( template_ = yield cg.process_lambda(
config[CONF_LAMBDA], [], config[CONF_LAMBDA],
return_type=cg.std_vector.template(cover.Cover.operator('ptr'))) [],
return_type=cg.std_vector.template(cover.Cover.operator("ptr")),
)
rhs = CustomCoverConstructor(template_) rhs = CustomCoverConstructor(template_)
custom = cg.variable(config[CONF_ID], rhs) custom = cg.variable(config[CONF_ID], rhs)

View file

@ -4,20 +4,24 @@ from esphome.components import light
from esphome.const import CONF_ID, CONF_LAMBDA from esphome.const import CONF_ID, CONF_LAMBDA
from .. import custom_ns from .. import custom_ns
CustomLightOutputConstructor = custom_ns.class_('CustomLightOutputConstructor') CustomLightOutputConstructor = custom_ns.class_("CustomLightOutputConstructor")
CONF_LIGHTS = 'lights' CONF_LIGHTS = "lights"
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(CustomLightOutputConstructor), {
cv.Required(CONF_LAMBDA): cv.returning_lambda, cv.GenerateID(): cv.declare_id(CustomLightOutputConstructor),
cv.Required(CONF_LIGHTS): cv.ensure_list(light.ADDRESSABLE_LIGHT_SCHEMA), cv.Required(CONF_LAMBDA): cv.returning_lambda,
}) cv.Required(CONF_LIGHTS): cv.ensure_list(light.ADDRESSABLE_LIGHT_SCHEMA),
}
)
def to_code(config): def to_code(config):
template_ = yield cg.process_lambda( template_ = yield cg.process_lambda(
config[CONF_LAMBDA], [], config[CONF_LAMBDA],
return_type=cg.std_vector.template(light.LightOutput.operator('ptr'))) [],
return_type=cg.std_vector.template(light.LightOutput.operator("ptr")),
)
rhs = CustomLightOutputConstructor(template_) rhs = CustomLightOutputConstructor(template_)
custom = cg.variable(config[CONF_ID], rhs) custom = cg.variable(config[CONF_ID], rhs)

View file

@ -4,41 +4,55 @@ from esphome.components import output
from esphome.const import CONF_ID, CONF_LAMBDA, CONF_OUTPUTS, CONF_TYPE, CONF_BINARY from esphome.const import CONF_ID, CONF_LAMBDA, CONF_OUTPUTS, CONF_TYPE, CONF_BINARY
from .. import custom_ns from .. import custom_ns
CustomBinaryOutputConstructor = custom_ns.class_('CustomBinaryOutputConstructor') CustomBinaryOutputConstructor = custom_ns.class_("CustomBinaryOutputConstructor")
CustomFloatOutputConstructor = custom_ns.class_('CustomFloatOutputConstructor') CustomFloatOutputConstructor = custom_ns.class_("CustomFloatOutputConstructor")
CONF_FLOAT = 'float' CONF_FLOAT = "float"
CONFIG_SCHEMA = cv.typed_schema({ CONFIG_SCHEMA = cv.typed_schema(
CONF_BINARY: cv.Schema({ {
cv.GenerateID(): cv.declare_id(CustomBinaryOutputConstructor), CONF_BINARY: cv.Schema(
cv.Required(CONF_LAMBDA): cv.returning_lambda, {
cv.Required(CONF_OUTPUTS): cv.GenerateID(): cv.declare_id(CustomBinaryOutputConstructor),
cv.ensure_list(output.BINARY_OUTPUT_SCHEMA.extend({ cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.GenerateID(): cv.declare_id(output.BinaryOutput), cv.Required(CONF_OUTPUTS): cv.ensure_list(
})), output.BINARY_OUTPUT_SCHEMA.extend(
}), {
CONF_FLOAT: cv.Schema({ cv.GenerateID(): cv.declare_id(output.BinaryOutput),
cv.GenerateID(): cv.declare_id(CustomFloatOutputConstructor), }
cv.Required(CONF_LAMBDA): cv.returning_lambda, )
cv.Required(CONF_OUTPUTS): ),
cv.ensure_list(output.FLOAT_OUTPUT_SCHEMA.extend({ }
cv.GenerateID(): cv.declare_id(output.FloatOutput), ),
})), CONF_FLOAT: cv.Schema(
}) {
}, lower=True) cv.GenerateID(): cv.declare_id(CustomFloatOutputConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_OUTPUTS): cv.ensure_list(
output.FLOAT_OUTPUT_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(output.FloatOutput),
}
)
),
}
),
},
lower=True,
)
def to_code(config): def to_code(config):
type = config[CONF_TYPE] type = config[CONF_TYPE]
if type == 'binary': if type == "binary":
ret_type = output.BinaryOutputPtr ret_type = output.BinaryOutputPtr
klass = CustomBinaryOutputConstructor klass = CustomBinaryOutputConstructor
else: else:
ret_type = output.FloatOutputPtr ret_type = output.FloatOutputPtr
klass = CustomFloatOutputConstructor klass = CustomFloatOutputConstructor
template_ = yield cg.process_lambda(config[CONF_LAMBDA], [], template_ = yield cg.process_lambda(
return_type=cg.std_vector.template(ret_type)) config[CONF_LAMBDA], [], return_type=cg.std_vector.template(ret_type)
)
rhs = klass(template_) rhs = klass(template_)
custom = cg.variable(config[CONF_ID], rhs) custom = cg.variable(config[CONF_ID], rhs)

View file

@ -4,18 +4,21 @@ from esphome.components import sensor
from esphome.const import CONF_ID, CONF_LAMBDA, CONF_SENSORS from esphome.const import CONF_ID, CONF_LAMBDA, CONF_SENSORS
from .. import custom_ns from .. import custom_ns
CustomSensorConstructor = custom_ns.class_('CustomSensorConstructor') CustomSensorConstructor = custom_ns.class_("CustomSensorConstructor")
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(CustomSensorConstructor), {
cv.Required(CONF_LAMBDA): cv.returning_lambda, cv.GenerateID(): cv.declare_id(CustomSensorConstructor),
cv.Required(CONF_SENSORS): cv.ensure_list(sensor.SENSOR_SCHEMA), cv.Required(CONF_LAMBDA): cv.returning_lambda,
}) cv.Required(CONF_SENSORS): cv.ensure_list(sensor.SENSOR_SCHEMA),
}
)
def to_code(config): def to_code(config):
template_ = yield cg.process_lambda( template_ = yield cg.process_lambda(
config[CONF_LAMBDA], [], return_type=cg.std_vector.template(sensor.SensorPtr)) config[CONF_LAMBDA], [], return_type=cg.std_vector.template(sensor.SensorPtr)
)
rhs = CustomSensorConstructor(template_) rhs = CustomSensorConstructor(template_)
var = cg.variable(config[CONF_ID], rhs) var = cg.variable(config[CONF_ID], rhs)

View file

@ -4,21 +4,27 @@ from esphome.components import switch
from esphome.const import CONF_ID, CONF_LAMBDA, CONF_SWITCHES from esphome.const import CONF_ID, CONF_LAMBDA, CONF_SWITCHES
from .. import custom_ns from .. import custom_ns
CustomSwitchConstructor = custom_ns.class_('CustomSwitchConstructor') CustomSwitchConstructor = custom_ns.class_("CustomSwitchConstructor")
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(CustomSwitchConstructor), {
cv.Required(CONF_LAMBDA): cv.returning_lambda, cv.GenerateID(): cv.declare_id(CustomSwitchConstructor),
cv.Required(CONF_SWITCHES): cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.ensure_list(switch.SWITCH_SCHEMA.extend({ cv.Required(CONF_SWITCHES): cv.ensure_list(
cv.GenerateID(): cv.declare_id(switch.Switch), switch.SWITCH_SCHEMA.extend(
})), {
}) cv.GenerateID(): cv.declare_id(switch.Switch),
}
)
),
}
)
def to_code(config): def to_code(config):
template_ = yield cg.process_lambda( template_ = yield cg.process_lambda(
config[CONF_LAMBDA], [], return_type=cg.std_vector.template(switch.SwitchPtr)) config[CONF_LAMBDA], [], return_type=cg.std_vector.template(switch.SwitchPtr)
)
rhs = CustomSwitchConstructor(template_) rhs = CustomSwitchConstructor(template_)
var = cg.variable(config[CONF_ID], rhs) var = cg.variable(config[CONF_ID], rhs)

View file

@ -4,21 +4,29 @@ from esphome.components import text_sensor
from esphome.const import CONF_ID, CONF_LAMBDA, CONF_TEXT_SENSORS from esphome.const import CONF_ID, CONF_LAMBDA, CONF_TEXT_SENSORS
from .. import custom_ns from .. import custom_ns
CustomTextSensorConstructor = custom_ns.class_('CustomTextSensorConstructor') CustomTextSensorConstructor = custom_ns.class_("CustomTextSensorConstructor")
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(CustomTextSensorConstructor), {
cv.Required(CONF_LAMBDA): cv.returning_lambda, cv.GenerateID(): cv.declare_id(CustomTextSensorConstructor),
cv.Required(CONF_TEXT_SENSORS): cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.ensure_list(text_sensor.TEXT_SENSOR_SCHEMA.extend({ cv.Required(CONF_TEXT_SENSORS): cv.ensure_list(
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor), text_sensor.TEXT_SENSOR_SCHEMA.extend(
})), {
}) cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
)
),
}
)
def to_code(config): def to_code(config):
template_ = yield cg.process_lambda( template_ = yield cg.process_lambda(
config[CONF_LAMBDA], [], return_type=cg.std_vector.template(text_sensor.TextSensorPtr)) config[CONF_LAMBDA],
[],
return_type=cg.std_vector.template(text_sensor.TextSensorPtr),
)
rhs = CustomTextSensorConstructor(template_) rhs = CustomTextSensorConstructor(template_)
var = cg.variable(config[CONF_ID], rhs) var = cg.variable(config[CONF_ID], rhs)

View file

@ -2,22 +2,27 @@ import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import CONF_COMPONENTS, CONF_ID, CONF_LAMBDA from esphome.const import CONF_COMPONENTS, CONF_ID, CONF_LAMBDA
custom_component_ns = cg.esphome_ns.namespace('custom_component') custom_component_ns = cg.esphome_ns.namespace("custom_component")
CustomComponentConstructor = custom_component_ns.class_('CustomComponentConstructor') CustomComponentConstructor = custom_component_ns.class_("CustomComponentConstructor")
MULTI_CONF = True MULTI_CONF = True
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(CustomComponentConstructor), {
cv.Required(CONF_LAMBDA): cv.returning_lambda, cv.GenerateID(): cv.declare_id(CustomComponentConstructor),
cv.Optional(CONF_COMPONENTS): cv.ensure_list(cv.Schema({ cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.GenerateID(): cv.declare_id(cg.Component) cv.Optional(CONF_COMPONENTS): cv.ensure_list(
}).extend(cv.COMPONENT_SCHEMA)), cv.Schema({cv.GenerateID(): cv.declare_id(cg.Component)}).extend(
}) cv.COMPONENT_SCHEMA
)
),
}
)
def to_code(config): def to_code(config):
template_ = yield cg.process_lambda( template_ = yield cg.process_lambda(
config[CONF_LAMBDA], [], return_type=cg.std_vector.template(cg.ComponentPtr)) config[CONF_LAMBDA], [], return_type=cg.std_vector.template(cg.ComponentPtr)
)
rhs = CustomComponentConstructor(template_) rhs = CustomComponentConstructor(template_)
var = cg.variable(config[CONF_ID], rhs) var = cg.variable(config[CONF_ID], rhs)

View file

@ -1,22 +1,29 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import light, output from esphome.components import light, output
from esphome.const import CONF_OUTPUT_ID, CONF_COLD_WHITE, CONF_WARM_WHITE, \ from esphome.const import (
CONF_COLD_WHITE_COLOR_TEMPERATURE, CONF_WARM_WHITE_COLOR_TEMPERATURE CONF_OUTPUT_ID,
CONF_COLD_WHITE,
CONF_WARM_WHITE,
CONF_COLD_WHITE_COLOR_TEMPERATURE,
CONF_WARM_WHITE_COLOR_TEMPERATURE,
)
cwww_ns = cg.esphome_ns.namespace('cwww') cwww_ns = cg.esphome_ns.namespace("cwww")
CWWWLightOutput = cwww_ns.class_('CWWWLightOutput', light.LightOutput) CWWWLightOutput = cwww_ns.class_("CWWWLightOutput", light.LightOutput)
CONF_CONSTANT_BRIGHTNESS = 'constant_brightness' CONF_CONSTANT_BRIGHTNESS = "constant_brightness"
CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend({ 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.GenerateID(CONF_OUTPUT_ID): cv.declare_id(CWWWLightOutput),
cv.Required(CONF_WARM_WHITE): cv.use_id(output.FloatOutput), cv.Required(CONF_COLD_WHITE): cv.use_id(output.FloatOutput),
cv.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature, cv.Required(CONF_WARM_WHITE): cv.use_id(output.FloatOutput),
cv.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): cv.color_temperature, cv.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature,
cv.Optional(CONF_CONSTANT_BRIGHTNESS, default=False): cv.boolean, cv.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): cv.color_temperature,
}) cv.Optional(CONF_CONSTANT_BRIGHTNESS, default=False): cv.boolean,
}
)
def to_code(config): def to_code(config):

View file

@ -3,14 +3,16 @@ import esphome.config_validation as cv
from esphome.components import climate_ir from esphome.components import climate_ir
from esphome.const import CONF_ID from esphome.const import CONF_ID
AUTO_LOAD = ['climate_ir'] AUTO_LOAD = ["climate_ir"]
daikin_ns = cg.esphome_ns.namespace('daikin') daikin_ns = cg.esphome_ns.namespace("daikin")
DaikinClimate = daikin_ns.class_('DaikinClimate', climate_ir.ClimateIR) DaikinClimate = daikin_ns.class_("DaikinClimate", climate_ir.ClimateIR)
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend({ CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
cv.GenerateID(): cv.declare_id(DaikinClimate), {
}) cv.GenerateID(): cv.declare_id(DaikinClimate),
}
)
def to_code(config): def to_code(config):

View file

@ -4,18 +4,20 @@ from esphome import pins
from esphome.const import CONF_ID, CONF_PIN from esphome.const import CONF_ID, CONF_PIN
MULTI_CONF = True MULTI_CONF = True
AUTO_LOAD = ['sensor'] AUTO_LOAD = ["sensor"]
CONF_ONE_WIRE_ID = 'one_wire_id' CONF_ONE_WIRE_ID = "one_wire_id"
dallas_ns = cg.esphome_ns.namespace('dallas') dallas_ns = cg.esphome_ns.namespace("dallas")
DallasComponent = dallas_ns.class_('DallasComponent', cg.PollingComponent) DallasComponent = dallas_ns.class_("DallasComponent", cg.PollingComponent)
ESPOneWire = dallas_ns.class_('ESPOneWire') ESPOneWire = dallas_ns.class_("ESPOneWire")
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(DallasComponent), {
cv.GenerateID(CONF_ONE_WIRE_ID): cv.declare_id(ESPOneWire), cv.GenerateID(): cv.declare_id(DallasComponent),
cv.Required(CONF_PIN): pins.gpio_input_pin_schema, cv.GenerateID(CONF_ONE_WIRE_ID): cv.declare_id(ESPOneWire),
}).extend(cv.polling_component_schema('60s')) cv.Required(CONF_PIN): pins.gpio_input_pin_schema,
}
).extend(cv.polling_component_schema("60s"))
def to_code(config): def to_code(config):

View file

@ -1,20 +1,32 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import sensor from esphome.components import sensor
from esphome.const import CONF_ADDRESS, CONF_DALLAS_ID, CONF_INDEX, CONF_RESOLUTION, UNIT_CELSIUS, \ from esphome.const import (
ICON_THERMOMETER, CONF_ID CONF_ADDRESS,
CONF_DALLAS_ID,
CONF_INDEX,
CONF_RESOLUTION,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
UNIT_CELSIUS,
CONF_ID,
)
from . import DallasComponent, dallas_ns from . import DallasComponent, dallas_ns
DallasTemperatureSensor = dallas_ns.class_('DallasTemperatureSensor', sensor.Sensor) DallasTemperatureSensor = dallas_ns.class_("DallasTemperatureSensor", sensor.Sensor)
CONFIG_SCHEMA = cv.All(sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({ CONFIG_SCHEMA = cv.All(
cv.GenerateID(): cv.declare_id(DallasTemperatureSensor), sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE).extend(
cv.GenerateID(CONF_DALLAS_ID): cv.use_id(DallasComponent), {
cv.GenerateID(): cv.declare_id(DallasTemperatureSensor),
cv.Optional(CONF_ADDRESS): cv.hex_int, cv.GenerateID(CONF_DALLAS_ID): cv.use_id(DallasComponent),
cv.Optional(CONF_INDEX): cv.positive_int, cv.Optional(CONF_ADDRESS): cv.hex_int,
cv.Optional(CONF_RESOLUTION, default=12): cv.int_range(min=9, max=12), cv.Optional(CONF_INDEX): cv.positive_int,
}), cv.has_exactly_one_key(CONF_ADDRESS, CONF_INDEX)) cv.Optional(CONF_RESOLUTION, default=12): cv.int_range(min=9, max=12),
}
),
cv.has_exactly_one_key(CONF_ADDRESS, CONF_INDEX),
)
def to_code(config): def to_code(config):

View file

@ -2,14 +2,16 @@ import esphome.config_validation as cv
import esphome.codegen as cg import esphome.codegen as cg
from esphome.const import CONF_ID from esphome.const import CONF_ID
CODEOWNERS = ['@OttoWinter'] CODEOWNERS = ["@OttoWinter"]
DEPENDENCIES = ['logger'] DEPENDENCIES = ["logger"]
debug_ns = cg.esphome_ns.namespace('debug') debug_ns = cg.esphome_ns.namespace("debug")
DebugComponent = debug_ns.class_('DebugComponent', cg.Component) DebugComponent = debug_ns.class_("DebugComponent", cg.Component)
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(DebugComponent), {
}).extend(cv.COMPONENT_SCHEMA) cv.GenerateID(): cv.declare_id(DebugComponent),
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config): def to_code(config):

View file

@ -1,58 +1,81 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import pins, automation from esphome import pins, automation
from esphome.const import CONF_ID, CONF_MODE, CONF_NUMBER, CONF_PINS, CONF_RUN_CYCLES, \ from esphome.const import (
CONF_RUN_DURATION, CONF_SLEEP_DURATION, CONF_WAKEUP_PIN CONF_ID,
CONF_MODE,
CONF_NUMBER,
CONF_PINS,
CONF_RUN_CYCLES,
CONF_RUN_DURATION,
CONF_SLEEP_DURATION,
CONF_WAKEUP_PIN,
)
def validate_pin_number(value): def validate_pin_number(value):
valid_pins = [0, 2, 4, 12, 13, 14, 15, 25, 26, 27, 32, 33, 34, 35, 36, 37, 38, 39] valid_pins = [0, 2, 4, 12, 13, 14, 15, 25, 26, 27, 32, 33, 34, 35, 36, 37, 38, 39]
if value[CONF_NUMBER] not in valid_pins: if value[CONF_NUMBER] not in valid_pins:
raise cv.Invalid("Only pins {} support wakeup" raise cv.Invalid(
"".format(', '.join(str(x) for x in valid_pins))) "Only pins {} support wakeup"
"".format(", ".join(str(x) for x in valid_pins))
)
return value return value
deep_sleep_ns = cg.esphome_ns.namespace('deep_sleep') deep_sleep_ns = cg.esphome_ns.namespace("deep_sleep")
DeepSleepComponent = deep_sleep_ns.class_('DeepSleepComponent', cg.Component) DeepSleepComponent = deep_sleep_ns.class_("DeepSleepComponent", cg.Component)
EnterDeepSleepAction = deep_sleep_ns.class_('EnterDeepSleepAction', automation.Action) EnterDeepSleepAction = deep_sleep_ns.class_("EnterDeepSleepAction", automation.Action)
PreventDeepSleepAction = deep_sleep_ns.class_('PreventDeepSleepAction', automation.Action) PreventDeepSleepAction = deep_sleep_ns.class_(
"PreventDeepSleepAction", automation.Action
)
WakeupPinMode = deep_sleep_ns.enum('WakeupPinMode') WakeupPinMode = deep_sleep_ns.enum("WakeupPinMode")
WAKEUP_PIN_MODES = { WAKEUP_PIN_MODES = {
'IGNORE': WakeupPinMode.WAKEUP_PIN_MODE_IGNORE, "IGNORE": WakeupPinMode.WAKEUP_PIN_MODE_IGNORE,
'KEEP_AWAKE': WakeupPinMode.WAKEUP_PIN_MODE_KEEP_AWAKE, "KEEP_AWAKE": WakeupPinMode.WAKEUP_PIN_MODE_KEEP_AWAKE,
'INVERT_WAKEUP': WakeupPinMode.WAKEUP_PIN_MODE_INVERT_WAKEUP, "INVERT_WAKEUP": WakeupPinMode.WAKEUP_PIN_MODE_INVERT_WAKEUP,
} }
esp_sleep_ext1_wakeup_mode_t = cg.global_ns.enum('esp_sleep_ext1_wakeup_mode_t') esp_sleep_ext1_wakeup_mode_t = cg.global_ns.enum("esp_sleep_ext1_wakeup_mode_t")
Ext1Wakeup = deep_sleep_ns.struct('Ext1Wakeup') Ext1Wakeup = deep_sleep_ns.struct("Ext1Wakeup")
EXT1_WAKEUP_MODES = { EXT1_WAKEUP_MODES = {
'ALL_LOW': esp_sleep_ext1_wakeup_mode_t.ESP_EXT1_WAKEUP_ALL_LOW, "ALL_LOW": esp_sleep_ext1_wakeup_mode_t.ESP_EXT1_WAKEUP_ALL_LOW,
'ANY_HIGH': esp_sleep_ext1_wakeup_mode_t.ESP_EXT1_WAKEUP_ANY_HIGH, "ANY_HIGH": esp_sleep_ext1_wakeup_mode_t.ESP_EXT1_WAKEUP_ANY_HIGH,
} }
CONF_WAKEUP_PIN_MODE = 'wakeup_pin_mode' CONF_WAKEUP_PIN_MODE = "wakeup_pin_mode"
CONF_ESP32_EXT1_WAKEUP = 'esp32_ext1_wakeup' CONF_ESP32_EXT1_WAKEUP = "esp32_ext1_wakeup"
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(DeepSleepComponent), {
cv.Optional(CONF_RUN_DURATION): cv.positive_time_period_milliseconds, cv.GenerateID(): cv.declare_id(DeepSleepComponent),
cv.Optional(CONF_RUN_DURATION): cv.positive_time_period_milliseconds,
cv.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds, cv.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds,
cv.Optional(CONF_WAKEUP_PIN): cv.All(cv.only_on_esp32, pins.internal_gpio_input_pin_schema, cv.Optional(CONF_WAKEUP_PIN): cv.All(
validate_pin_number), cv.only_on_esp32, pins.internal_gpio_input_pin_schema, validate_pin_number
cv.Optional(CONF_WAKEUP_PIN_MODE): cv.All(cv.only_on_esp32, ),
cv.enum(WAKEUP_PIN_MODES), upper=True), cv.Optional(CONF_WAKEUP_PIN_MODE): cv.All(
cv.Optional(CONF_ESP32_EXT1_WAKEUP): cv.All(cv.only_on_esp32, cv.Schema({ cv.only_on_esp32, cv.enum(WAKEUP_PIN_MODES), upper=True
cv.Required(CONF_PINS): cv.ensure_list(pins.shorthand_input_pin, validate_pin_number), ),
cv.Required(CONF_MODE): cv.enum(EXT1_WAKEUP_MODES, upper=True), cv.Optional(CONF_ESP32_EXT1_WAKEUP): cv.All(
})), cv.only_on_esp32,
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." cv.Required(CONF_PINS): cv.ensure_list(
"Please use run_duration now.") pins.shorthand_input_pin, validate_pin_number
}).extend(cv.COMPONENT_SCHEMA) ),
cv.Required(CONF_MODE): cv.enum(EXT1_WAKEUP_MODES, upper=True),
}
),
),
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)
def to_code(config): def to_code(config):
@ -75,27 +98,43 @@ def to_code(config):
for pin in conf[CONF_PINS]: for pin in conf[CONF_PINS]:
mask |= 1 << pin[CONF_NUMBER] mask |= 1 << pin[CONF_NUMBER]
struct = cg.StructInitializer( struct = cg.StructInitializer(
Ext1Wakeup, Ext1Wakeup, ("mask", mask), ("wakeup_mode", conf[CONF_MODE])
('mask', mask),
('wakeup_mode', conf[CONF_MODE])
) )
cg.add(var.set_ext1_wakeup(struct)) cg.add(var.set_ext1_wakeup(struct))
cg.add_define('USE_DEEP_SLEEP') cg.add_define("USE_DEEP_SLEEP")
DEEP_SLEEP_ACTION_SCHEMA = automation.maybe_simple_id({ DEEP_SLEEP_ENTER_SCHEMA = automation.maybe_simple_id(
cv.GenerateID(): cv.use_id(DeepSleepComponent), {
}) cv.GenerateID(): cv.use_id(DeepSleepComponent),
cv.Optional(CONF_SLEEP_DURATION): cv.positive_time_period_milliseconds,
}
)
@automation.register_action('deep_sleep.enter', EnterDeepSleepAction, DEEP_SLEEP_ACTION_SCHEMA) DEEP_SLEEP_PREVENT_SCHEMA = automation.maybe_simple_id(
{
cv.GenerateID(): cv.use_id(DeepSleepComponent),
}
)
@automation.register_action(
"deep_sleep.enter", EnterDeepSleepAction, DEEP_SLEEP_ENTER_SCHEMA
)
def deep_sleep_enter_to_code(config, action_id, template_arg, args): def deep_sleep_enter_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID]) paren = yield cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(action_id, template_arg, paren) var = cg.new_Pvariable(action_id, template_arg, paren)
if CONF_SLEEP_DURATION in config:
template_ = yield cg.templatable(config[CONF_SLEEP_DURATION], args, cg.int32)
cg.add(var.set_sleep_duration(template_))
yield var
@automation.register_action('deep_sleep.prevent', PreventDeepSleepAction, DEEP_SLEEP_ACTION_SCHEMA) @automation.register_action(
"deep_sleep.prevent", PreventDeepSleepAction, DEEP_SLEEP_PREVENT_SCHEMA
)
def deep_sleep_prevent_to_code(config, action_id, template_arg, args): def deep_sleep_prevent_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID]) paren = yield cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(action_id, template_arg, paren) yield cg.new_Pvariable(action_id, template_arg, paren)

View file

@ -84,8 +84,14 @@ extern bool global_has_deep_sleep;
template<typename... Ts> class EnterDeepSleepAction : public Action<Ts...> { template<typename... Ts> class EnterDeepSleepAction : public Action<Ts...> {
public: public:
EnterDeepSleepAction(DeepSleepComponent *deep_sleep) : deep_sleep_(deep_sleep) {} EnterDeepSleepAction(DeepSleepComponent *deep_sleep) : deep_sleep_(deep_sleep) {}
TEMPLATABLE_VALUE(uint32_t, sleep_duration);
void play(Ts... x) override { this->deep_sleep_->begin_sleep(true); } void play(Ts... x) override {
if (this->sleep_duration_.has_value()) {
this->deep_sleep_->set_sleep_duration(this->sleep_duration_.value(x...));
}
this->deep_sleep_->begin_sleep(true);
}
protected: protected:
DeepSleepComponent *deep_sleep_; DeepSleepComponent *deep_sleep_;

View file

@ -4,57 +4,68 @@ from esphome import automation
from esphome.const import CONF_ID, CONF_TRIGGER_ID, CONF_FILE, CONF_DEVICE from esphome.const import CONF_ID, CONF_TRIGGER_ID, CONF_FILE, CONF_DEVICE
from esphome.components import uart from esphome.components import uart
DEPENDENCIES = ['uart'] DEPENDENCIES = ["uart"]
CODEOWNERS = ['@glmnet'] CODEOWNERS = ["@glmnet"]
dfplayer_ns = cg.esphome_ns.namespace('dfplayer') dfplayer_ns = cg.esphome_ns.namespace("dfplayer")
DFPlayer = dfplayer_ns.class_('DFPlayer', cg.Component) DFPlayer = dfplayer_ns.class_("DFPlayer", cg.Component)
DFPlayerFinishedPlaybackTrigger = dfplayer_ns.class_('DFPlayerFinishedPlaybackTrigger', DFPlayerFinishedPlaybackTrigger = dfplayer_ns.class_(
automation.Trigger.template()) "DFPlayerFinishedPlaybackTrigger", automation.Trigger.template()
DFPlayerIsPlayingCondition = dfplayer_ns.class_('DFPlayerIsPlayingCondition', automation.Condition) )
DFPlayerIsPlayingCondition = dfplayer_ns.class_(
"DFPlayerIsPlayingCondition", automation.Condition
)
MULTI_CONF = True MULTI_CONF = True
CONF_FOLDER = 'folder' CONF_FOLDER = "folder"
CONF_LOOP = 'loop' CONF_LOOP = "loop"
CONF_VOLUME = 'volume' CONF_VOLUME = "volume"
CONF_EQ_PRESET = 'eq_preset' CONF_EQ_PRESET = "eq_preset"
CONF_ON_FINISHED_PLAYBACK = 'on_finished_playback' CONF_ON_FINISHED_PLAYBACK = "on_finished_playback"
EqPreset = dfplayer_ns.enum("EqPreset") EqPreset = dfplayer_ns.enum("EqPreset")
EQ_PRESET = { EQ_PRESET = {
'NORMAL': EqPreset.NORMAL, "NORMAL": EqPreset.NORMAL,
'POP': EqPreset.POP, "POP": EqPreset.POP,
'ROCK': EqPreset.ROCK, "ROCK": EqPreset.ROCK,
'JAZZ': EqPreset.JAZZ, "JAZZ": EqPreset.JAZZ,
'CLASSIC': EqPreset.CLASSIC, "CLASSIC": EqPreset.CLASSIC,
'BASS': EqPreset.BASS, "BASS": EqPreset.BASS,
} }
Device = dfplayer_ns.enum("Device") Device = dfplayer_ns.enum("Device")
DEVICE = { DEVICE = {
'USB': Device.USB, "USB": Device.USB,
'TF_CARD': Device.TF_CARD, "TF_CARD": Device.TF_CARD,
} }
NextAction = dfplayer_ns.class_('NextAction', automation.Action) NextAction = dfplayer_ns.class_("NextAction", automation.Action)
PreviousAction = dfplayer_ns.class_('PreviousAction', automation.Action) PreviousAction = dfplayer_ns.class_("PreviousAction", automation.Action)
PlayFileAction = dfplayer_ns.class_('PlayFileAction', automation.Action) PlayFileAction = dfplayer_ns.class_("PlayFileAction", automation.Action)
PlayFolderAction = dfplayer_ns.class_('PlayFolderAction', automation.Action) PlayFolderAction = dfplayer_ns.class_("PlayFolderAction", automation.Action)
SetVolumeAction = dfplayer_ns.class_('SetVolumeAction', automation.Action) SetVolumeAction = dfplayer_ns.class_("SetVolumeAction", automation.Action)
SetEqAction = dfplayer_ns.class_('SetEqAction', automation.Action) SetEqAction = dfplayer_ns.class_("SetEqAction", automation.Action)
SleepAction = dfplayer_ns.class_('SleepAction', automation.Action) SleepAction = dfplayer_ns.class_("SleepAction", automation.Action)
ResetAction = dfplayer_ns.class_('ResetAction', automation.Action) ResetAction = dfplayer_ns.class_("ResetAction", automation.Action)
StartAction = dfplayer_ns.class_('StartAction', automation.Action) StartAction = dfplayer_ns.class_("StartAction", automation.Action)
PauseAction = dfplayer_ns.class_('PauseAction', automation.Action) PauseAction = dfplayer_ns.class_("PauseAction", automation.Action)
StopAction = dfplayer_ns.class_('StopAction', automation.Action) StopAction = dfplayer_ns.class_("StopAction", automation.Action)
RandomAction = dfplayer_ns.class_('RandomAction', automation.Action) RandomAction = dfplayer_ns.class_("RandomAction", automation.Action)
SetDeviceAction = dfplayer_ns.class_('SetDeviceAction', automation.Action) SetDeviceAction = dfplayer_ns.class_("SetDeviceAction", automation.Action)
CONFIG_SCHEMA = cv.All(cv.Schema({ CONFIG_SCHEMA = cv.All(
cv.GenerateID(): cv.declare_id(DFPlayer), cv.Schema(
cv.Optional(CONF_ON_FINISHED_PLAYBACK): automation.validate_automation({ {
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DFPlayerFinishedPlaybackTrigger), cv.GenerateID(): cv.declare_id(DFPlayer),
}), cv.Optional(CONF_ON_FINISHED_PLAYBACK): automation.validate_automation(
}).extend(uart.UART_DEVICE_SCHEMA)) {
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
DFPlayerFinishedPlaybackTrigger
),
}
),
}
).extend(uart.UART_DEVICE_SCHEMA)
)
def to_code(config): def to_code(config):
@ -67,29 +78,48 @@ def to_code(config):
yield automation.build_automation(trigger, [], conf) yield automation.build_automation(trigger, [], conf)
@automation.register_action('dfplayer.play_next', NextAction, cv.Schema({ @automation.register_action(
cv.GenerateID(): cv.use_id(DFPlayer), "dfplayer.play_next",
})) NextAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(DFPlayer),
}
),
)
def dfplayer_next_to_code(config, action_id, template_arg, args): def dfplayer_next_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID]) yield cg.register_parented(var, config[CONF_ID])
yield var yield var
@automation.register_action('dfplayer.play_previous', PreviousAction, cv.Schema({ @automation.register_action(
cv.GenerateID(): cv.use_id(DFPlayer), "dfplayer.play_previous",
})) PreviousAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(DFPlayer),
}
),
)
def dfplayer_previous_to_code(config, action_id, template_arg, args): def dfplayer_previous_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID]) yield cg.register_parented(var, config[CONF_ID])
yield var yield var
@automation.register_action('dfplayer.play', PlayFileAction, cv.maybe_simple_value({ @automation.register_action(
cv.GenerateID(): cv.use_id(DFPlayer), "dfplayer.play",
cv.Required(CONF_FILE): cv.templatable(cv.int_), PlayFileAction,
cv.Optional(CONF_LOOP): cv.templatable(cv.boolean), cv.maybe_simple_value(
}, key=CONF_FILE)) {
cv.GenerateID(): cv.use_id(DFPlayer),
cv.Required(CONF_FILE): cv.templatable(cv.int_),
cv.Optional(CONF_LOOP): cv.templatable(cv.boolean),
},
key=CONF_FILE,
),
)
def dfplayer_play_to_code(config, action_id, template_arg, args): def dfplayer_play_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID]) yield cg.register_parented(var, config[CONF_ID])
@ -101,12 +131,18 @@ def dfplayer_play_to_code(config, action_id, template_arg, args):
yield var yield var
@automation.register_action('dfplayer.play_folder', PlayFolderAction, cv.Schema({ @automation.register_action(
cv.GenerateID(): cv.use_id(DFPlayer), "dfplayer.play_folder",
cv.Required(CONF_FOLDER): cv.templatable(cv.int_), PlayFolderAction,
cv.Optional(CONF_FILE): cv.templatable(cv.int_), cv.Schema(
cv.Optional(CONF_LOOP): cv.templatable(cv.boolean), {
})) cv.GenerateID(): cv.use_id(DFPlayer),
cv.Required(CONF_FOLDER): cv.templatable(cv.int_),
cv.Optional(CONF_FILE): cv.templatable(cv.int_),
cv.Optional(CONF_LOOP): cv.templatable(cv.boolean),
}
),
)
def dfplayer_play_folder_to_code(config, action_id, template_arg, args): def dfplayer_play_folder_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID]) yield cg.register_parented(var, config[CONF_ID])
@ -121,10 +157,17 @@ def dfplayer_play_folder_to_code(config, action_id, template_arg, args):
yield var yield var
@automation.register_action('dfplayer.set_device', SetDeviceAction, cv.maybe_simple_value({ @automation.register_action(
cv.GenerateID(): cv.use_id(DFPlayer), "dfplayer.set_device",
cv.Required(CONF_DEVICE): cv.enum(DEVICE, upper=True), SetDeviceAction,
}, key=CONF_DEVICE)) cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(DFPlayer),
cv.Required(CONF_DEVICE): cv.enum(DEVICE, upper=True),
},
key=CONF_DEVICE,
),
)
def dfplayer_set_device_to_code(config, action_id, template_arg, args): def dfplayer_set_device_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID]) yield cg.register_parented(var, config[CONF_ID])
@ -133,10 +176,17 @@ def dfplayer_set_device_to_code(config, action_id, template_arg, args):
yield var yield var
@automation.register_action('dfplayer.set_volume', SetVolumeAction, cv.maybe_simple_value({ @automation.register_action(
cv.GenerateID(): cv.use_id(DFPlayer), "dfplayer.set_volume",
cv.Required(CONF_VOLUME): cv.templatable(cv.int_), SetVolumeAction,
}, key=CONF_VOLUME)) cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(DFPlayer),
cv.Required(CONF_VOLUME): cv.templatable(cv.int_),
},
key=CONF_VOLUME,
),
)
def dfplayer_set_volume_to_code(config, action_id, template_arg, args): def dfplayer_set_volume_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID]) yield cg.register_parented(var, config[CONF_ID])
@ -145,10 +195,17 @@ def dfplayer_set_volume_to_code(config, action_id, template_arg, args):
yield var yield var
@automation.register_action('dfplayer.set_eq', SetEqAction, cv.maybe_simple_value({ @automation.register_action(
cv.GenerateID(): cv.use_id(DFPlayer), "dfplayer.set_eq",
cv.Required(CONF_EQ_PRESET): cv.templatable(cv.enum(EQ_PRESET, upper=True)), SetEqAction,
}, key=CONF_EQ_PRESET)) cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(DFPlayer),
cv.Required(CONF_EQ_PRESET): cv.templatable(cv.enum(EQ_PRESET, upper=True)),
},
key=CONF_EQ_PRESET,
),
)
def dfplayer_set_eq_to_code(config, action_id, template_arg, args): def dfplayer_set_eq_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID]) yield cg.register_parented(var, config[CONF_ID])
@ -157,63 +214,105 @@ def dfplayer_set_eq_to_code(config, action_id, template_arg, args):
yield var yield var
@automation.register_action('dfplayer.sleep', SleepAction, cv.Schema({ @automation.register_action(
cv.GenerateID(): cv.use_id(DFPlayer), "dfplayer.sleep",
})) SleepAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(DFPlayer),
}
),
)
def dfplayer_sleep_to_code(config, action_id, template_arg, args): def dfplayer_sleep_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID]) yield cg.register_parented(var, config[CONF_ID])
yield var yield var
@automation.register_action('dfplayer.reset', ResetAction, cv.Schema({ @automation.register_action(
cv.GenerateID(): cv.use_id(DFPlayer), "dfplayer.reset",
})) ResetAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(DFPlayer),
}
),
)
def dfplayer_reset_to_code(config, action_id, template_arg, args): def dfplayer_reset_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID]) yield cg.register_parented(var, config[CONF_ID])
yield var yield var
@automation.register_action('dfplayer.start', StartAction, cv.Schema({ @automation.register_action(
cv.GenerateID(): cv.use_id(DFPlayer), "dfplayer.start",
})) StartAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(DFPlayer),
}
),
)
def dfplayer_start_to_code(config, action_id, template_arg, args): def dfplayer_start_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID]) yield cg.register_parented(var, config[CONF_ID])
yield var yield var
@automation.register_action('dfplayer.pause', PauseAction, cv.Schema({ @automation.register_action(
cv.GenerateID(): cv.use_id(DFPlayer), "dfplayer.pause",
})) PauseAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(DFPlayer),
}
),
)
def dfplayer_pause_to_code(config, action_id, template_arg, args): def dfplayer_pause_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID]) yield cg.register_parented(var, config[CONF_ID])
yield var yield var
@automation.register_action('dfplayer.stop', StopAction, cv.Schema({ @automation.register_action(
cv.GenerateID(): cv.use_id(DFPlayer), "dfplayer.stop",
})) StopAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(DFPlayer),
}
),
)
def dfplayer_stop_to_code(config, action_id, template_arg, args): def dfplayer_stop_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID]) yield cg.register_parented(var, config[CONF_ID])
yield var yield var
@automation.register_action('dfplayer.random', RandomAction, cv.Schema({ @automation.register_action(
cv.GenerateID(): cv.use_id(DFPlayer), "dfplayer.random",
})) RandomAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(DFPlayer),
}
),
)
def dfplayer_random_to_code(config, action_id, template_arg, args): def dfplayer_random_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID]) yield cg.register_parented(var, config[CONF_ID])
yield var yield var
@automation.register_condition('dfplayer.is_playing', DFPlayerIsPlayingCondition, cv.Schema({ @automation.register_condition(
cv.GenerateID(): cv.use_id(DFPlayer), "dfplayer.is_playing",
})) DFPlayerIsPlayingCondition,
cv.Schema(
{
cv.GenerateID(): cv.use_id(DFPlayer),
}
),
)
def dfplyaer_is_playing_to_code(config, condition_id, template_arg, args): def dfplyaer_is_playing_to_code(config, condition_id, template_arg, args):
var = cg.new_Pvariable(condition_id, template_arg) var = cg.new_Pvariable(condition_id, template_arg)
yield cg.register_parented(var, config[CONF_ID]) yield cg.register_parented(var, config[CONF_ID])

Some files were not shown because too many files have changed in this diff Show more