1
0
mirror of https://github.com/esphome/esphome.git synced 2026-03-20 02:06:46 +01:00

Merge pull request #1636 from esphome/bump-1.17.0b1

1.17.0b1
This commit is contained in:
Jesse Hills 2021-03-22 20:44:38 +13:00 committed by GitHub
commit 0fe61d9ec7
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>
# 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:
- [ ] The code change is tested and works locally.
- [ ] Tests have been added to verify that the new code works (under `tests/` folder).
If user exposed functionality or configuration variables are added/changed:
- [ ] Documentation added/updated in [esphome-docs](https://github.com/esphome/esphome-docs).

View File

@ -26,7 +26,7 @@ jobs:
- uses: actions/checkout@v2
- name: Set up env variables
run: |
base_version="2.6.0"
base_version="3.0.0"
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
@ -45,7 +45,7 @@ jobs:
run: |
docker pull "${BUILD_TO}:dev" || true
- 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: |
docker build \
--build-arg "BUILD_FROM=${BUILD_FROM}" \

View File

@ -18,15 +18,6 @@ jobs:
container: esphome/esphome-lint:latest
steps:
- 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
# (build flags, libraries etc)
- name: Set up platformio environment
@ -49,15 +40,6 @@ jobs:
split: [1, 2, 3, 4]
steps:
- 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
# (build flags, libraries etc)
- name: Set up platformio environment

View File

@ -15,15 +15,6 @@ jobs:
container: esphome/esphome-lint:latest
steps:
- 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
# (build flags, libraries etc)
- name: Set up platformio environment
@ -46,15 +37,6 @@ jobs:
split: [1, 2, 3, 4]
steps:
- 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
# (build flags, libraries etc)
- name: Set up platformio environment
@ -192,7 +174,7 @@ jobs:
echo "TAG=${TAG}" >> $GITHUB_ENV
- name: Set up env variables
run: |
base_version="2.6.0"
base_version="3.0.0"
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
@ -211,7 +193,7 @@ jobs:
run: |
docker pull "${BUILD_TO}:dev" || true
- 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: |
docker build \
--build-arg "BUILD_FROM=${BUILD_FROM}" \

View File

@ -14,15 +14,6 @@ jobs:
container: esphome/esphome-lint:latest
steps:
- 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
# (build flags, libraries etc)
- name: Set up platformio environment
@ -45,15 +36,6 @@ jobs:
split: [1, 2, 3, 4]
steps:
- 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
# (build flags, libraries etc)
- name: Set up platformio environment
@ -212,7 +194,7 @@ jobs:
echo "TAG=${TAG}" >> $GITHUB_ENV
- name: Set up env variables
run: |
base_version="2.6.0"
base_version="3.0.0"
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
@ -239,7 +221,7 @@ jobs:
run: |
docker pull "${BUILD_TO}:${CACHE_TAG}" || true
- 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: |
docker build \
--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/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.4.0
- repo: https://github.com/ambv/black
rev: 20.8b1
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- id: flake8
- id: black
args:
- --safe
- --quiet
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
esphome/components/ac_dimmer/* @glmnet
esphome/components/adc/* @esphome/core
esphome/components/addressable_light/* @justfalter
esphome/components/animation/* @syndlex
esphome/components/api/* @OttoWinter
esphome/components/async_tcp/* @OttoWinter
@ -37,6 +38,7 @@ esphome/components/globals/* @esphome/core
esphome/components/gpio/* @esphome/core
esphome/components/homeassistant/* @OttoWinter
esphome/components/i2c/* @esphome/core
esphome/components/inkbird_ibsth1_mini/* @fkirill
esphome/components/inkplate6/* @jesserockz
esphome/components/integration/* @OttoWinter
esphome/components/interval/* @esphome/core
@ -44,10 +46,18 @@ esphome/components/json/* @OttoWinter
esphome/components/ledc/* @OttoWinter
esphome/components/light/* @esphome/core
esphome/components/logger/* @esphome/core
esphome/components/mcp23s08/* @SenexCrenshaw
esphome/components/mcp23s17/* @SenexCrenshaw
esphome/components/max7219digit/* @rspaargaren
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/mcp9808/* @k7hpn
esphome/components/midea_ac/* @dudanov
esphome/components/midea_dongle/* @dudanov
esphome/components/network/* @esphome/core
esphome/components/nfc/* @jesserockz
esphome/components/ota/* @esphome/core
@ -57,6 +67,7 @@ esphome/components/pn532/* @OttoWinter @jesserockz
esphome/components/pn532_i2c/* @OttoWinter @jesserockz
esphome/components/pn532_spi/* @OttoWinter @jesserockz
esphome/components/power_supply/* @esphome/core
esphome/components/pulse_meter/* @stevebaxter
esphome/components/rc522/* @glmnet
esphome/components/rc522_i2c/* @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}
# First install requirements to leverage caching when requirements don't change
COPY requirements.txt /
RUN pip3 install --no-cache-dir -r /requirements.txt
COPY requirements.txt docker/platformio_install_deps.py platformio.ini /
RUN \
pip3 install --no-cache-dir -r /requirements.txt \
&& /platformio_install_deps.py /platformio.ini
# Then copy esphome and install
COPY . .

View File

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

View File

@ -2,8 +2,10 @@ ARG BUILD_FROM
FROM ${BUILD_FROM}
# First install requirements to leverage caching when requirements don't change
COPY requirements.txt /
RUN pip3 install --no-cache-dir -r /requirements.txt
COPY requirements.txt docker/platformio_install_deps.py platformio.ini /
RUN \
pip3 install --no-cache-dir -r /requirements.txt \
&& /platformio_install_deps.py /platformio.ini
# Copy root filesystem
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 /
RUN pip3 install --no-cache-dir -r /requirements.txt -r /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 \
&& /platformio_install_deps.py /platformio.ini
VOLUME ["/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
import esphome.codegen as cg
from esphome.config import iter_components, read_config, strip_default_ids
from esphome.const import CONF_BAUD_RATE, CONF_BROKER, CONF_LOGGER, CONF_OTA, \
CONF_PASSWORD, CONF_PORT, CONF_ESPHOME, CONF_PLATFORMIO_OPTIONS
from esphome.const import (
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.helpers import color, indent
from esphome.util import run_external_command, run_external_process, safe_print, list_yaml_files, \
get_serial_ports
from esphome.util import (
run_external_command,
run_external_process,
safe_print,
list_yaml_files,
get_serial_ports,
)
_LOGGER = logging.getLogger(__name__)
def choose_prompt(options):
if not options:
raise EsphomeError("Found no valid options for upload/logging, please make sure relevant "
"sections (ota, api, mqtt, ...) are in your configuration and/or the "
"device is plugged in.")
raise EsphomeError(
"Found no valid options for upload/logging, please make sure relevant "
"sections (ota, api, mqtt, ...) are in your configuration and/or the "
"device is plugged in."
)
if len(options) == 1:
return options[0][1]
@ -32,7 +47,7 @@ def choose_prompt(options):
safe_print(f" [{i+1}] {desc}")
while True:
opt = input('(number): ')
opt = input("(number): ")
if opt in options:
opt = options.index(opt)
break
@ -42,7 +57,7 @@ def choose_prompt(options):
raise ValueError
break
except ValueError:
safe_print(color('red', f"Invalid option: '{opt}'"))
safe_print(color("red", f"Invalid option: '{opt}'"))
return options[opt - 1][1]
@ -50,14 +65,14 @@ def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api
options = []
for port in get_serial_ports():
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))
if default == 'OTA':
if default == "OTA":
return CORE.address
if show_mqtt and 'mqtt' in CORE.config:
options.append(("MQTT ({})".format(CORE.config['mqtt'][CONF_BROKER]), 'MQTT'))
if default == 'OTA':
return 'MQTT'
if show_mqtt and "mqtt" in CORE.config:
options.append(("MQTT ({})".format(CORE.config["mqtt"][CONF_BROKER]), "MQTT"))
if default == "OTA":
return "MQTT"
if default is not None:
return default
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):
if port.startswith('/') or port.startswith('COM'):
return 'SERIAL'
if port == 'MQTT':
return 'MQTT'
return 'NETWORK'
if port.startswith("/") or port.startswith("COM"):
return "SERIAL"
if port == "MQTT":
return "MQTT"
return "NETWORK"
def run_miniterm(config, port):
@ -80,7 +95,7 @@ def run_miniterm(config, port):
if CONF_LOGGER not in config:
_LOGGER.info("Logger is not enabled. Not starting UART logs.")
return
baud_rate = config['logger'][CONF_BAUD_RATE]
baud_rate = config["logger"][CONF_BAUD_RATE]
if baud_rate == 0:
_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)
@ -93,13 +108,18 @@ def run_miniterm(config, port):
except serial.SerialException:
_LOGGER.error("Serial port closed!")
return
line = raw.replace(b'\r', b'').replace(b'\n', b'').decode('utf8', 'backslashreplace')
time = datetime.now().time().strftime('[%H:%M:%S]')
line = (
raw.replace(b"\r", b"")
.replace(b"\n", b"")
.decode("utf8", "backslashreplace")
)
time = datetime.now().time().strftime("[%H:%M:%S]")
message = time + line
safe_print(message)
backtrace_state = platformio_api.process_stacktrace(
config, line, backtrace_state=backtrace_state)
config, line, backtrace_state=backtrace_state
)
def wrap_to_code(name, comp):
@ -111,7 +131,7 @@ def wrap_to_code(name, comp):
cg.add(cg.LineComment(f"{name}:"))
if comp.config_schema is not None:
conf_str = yaml_util.dump(conf)
conf_str = conf_str.replace('//', '')
conf_str = conf_str.replace("//", "")
cg.add(cg.LineComment(indent(conf_str)))
yield coro(conf)
@ -151,15 +171,31 @@ def compile_program(args, config):
def upload_using_esptool(config, port):
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):
cmd = ['esptool.py', '--before', 'default_reset', '--after', 'hard_reset',
'--baud', str(baud_rate),
'--chip', 'esp8266', '--port', port, 'write_flash', '0x0', path]
cmd = [
"esptool.py",
"--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
# pylint: disable=protected-access
return run_external_command(esptool._main, *cmd)
@ -169,14 +205,16 @@ def upload_using_esptool(config, port):
if rc == 0 or first_baudrate == 115200:
return rc
# 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.",
first_baudrate)
_LOGGER.info(
"Upload with baud rate %s failed. Trying again with baud rate 115200.",
first_baudrate,
)
return run_esptool(115200)
def upload_program(config, args, host):
# 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
if CORE.is_esp8266:
@ -186,8 +224,10 @@ def upload_program(config, args, host):
from esphome import espota2
if CONF_OTA not in config:
raise EsphomeError("Cannot upload Over the Air as the config does not include the ota: "
"component")
raise EsphomeError(
"Cannot upload Over the Air as the config does not include the ota: "
"component"
)
ota_conf = config[CONF_OTA]
remote_port = ota_conf[CONF_PORT]
@ -196,19 +236,21 @@ def upload_program(config, args, host):
def show_logs(config, args, port):
if 'logger' not in config:
if "logger" not in config:
raise EsphomeError("Logger is not configured!")
if get_port_type(port) == 'SERIAL':
if get_port_type(port) == "SERIAL":
run_miniterm(config, port)
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
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
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)")
@ -216,7 +258,9 @@ def show_logs(config, args, port):
def clean_mqtt(config, args):
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):
@ -230,27 +274,31 @@ def setup_log(debug=False, quiet=False):
logging.basicConfig(level=log_level)
fmt = "%(levelname)s %(message)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:
import colorama
colorama.init(strip=True)
from colorlog import ColoredFormatter
logging.getLogger().handlers[0].setFormatter(ColoredFormatter(
colorfmt,
datefmt=datefmt,
reset=True,
log_colors={
'DEBUG': 'cyan',
'INFO': 'green',
'WARNING': 'yellow',
'ERROR': 'red',
'CRITICAL': 'red',
}
))
logging.getLogger().handlers[0].setFormatter(
ColoredFormatter(
colorfmt,
datefmt=datefmt,
reset=True,
log_colors={
"DEBUG": "cyan",
"INFO": "green",
"WARNING": "yellow",
"ERROR": "red",
"CRITICAL": "red",
},
)
)
except ImportError:
pass
@ -272,6 +320,8 @@ def command_config(args, config):
def command_vscode(args):
from esphome import vscode
logging.disable(logging.INFO)
logging.disable(logging.WARNING)
CORE.config_path = args.configuration[0]
vscode.read_config(args)
@ -291,8 +341,13 @@ def command_compile(args, config):
def command_upload(args, config):
port = choose_upload_log_host(default=args.upload_port, check_default=None,
show_ota=True, show_mqtt=False, show_api=False)
port = choose_upload_log_host(
default=args.upload_port,
check_default=None,
show_ota=True,
show_mqtt=False,
show_api=False,
)
exit_code = upload_program(config, args, port)
if exit_code != 0:
return exit_code
@ -301,8 +356,13 @@ def command_upload(args, config):
def command_logs(args, config):
port = choose_upload_log_host(default=args.serial_port, check_default=None,
show_ota=False, show_mqtt=True, show_api=True)
port = choose_upload_log_host(
default=args.serial_port,
check_default=None,
show_ota=False,
show_mqtt=True,
show_api=True,
)
return show_logs(config, args, port)
@ -314,16 +374,26 @@ def command_run(args, config):
if exit_code != 0:
return exit_code
_LOGGER.info("Successfully compiled program.")
port = choose_upload_log_host(default=args.upload_port, check_default=None,
show_ota=True, show_mqtt=False, show_api=True)
port = choose_upload_log_host(
default=args.upload_port,
check_default=None,
show_ota=True,
show_mqtt=False,
show_api=True,
)
exit_code = upload_program(config, args, port)
if exit_code != 0:
return exit_code
_LOGGER.info("Successfully uploaded program.")
if args.no_logs:
return 0
port = choose_upload_log_host(default=args.upload_port, check_default=port,
show_ota=False, show_mqtt=True, show_api=True)
port = choose_upload_log_host(
default=args.upload_port,
check_default=port,
show_ota=False,
show_mqtt=True,
show_api=True,
)
return show_logs(config, args, port)
@ -372,137 +442,189 @@ def command_update_all(args):
click.echo(f"{half_line}{middle_text}{half_line}")
for f in files:
print("Updating {}".format(color('cyan', f)))
print('-' * twidth)
print("Updating {}".format(color("cyan", f)))
print("-" * twidth)
print()
rc = run_external_process('esphome', '--dashboard', f, 'run', '--no-logs', '--upload-port',
'OTA')
rc = run_external_process(
"esphome", "--dashboard", f, "run", "--no-logs", "--upload-port", "OTA"
)
if rc == 0:
print_bar("[{}] {}".format(color('bold_green', 'SUCCESS'), f))
print_bar("[{}] {}".format(color("bold_green", "SUCCESS"), f))
success[f] = True
else:
print_bar("[{}] {}".format(color('bold_red', 'ERROR'), f))
print_bar("[{}] {}".format(color("bold_red", "ERROR"), f))
success[f] = False
print()
print()
print()
print_bar('[{}]'.format(color('bold_white', 'SUMMARY')))
print_bar("[{}]".format(color("bold_white", "SUMMARY")))
failed = 0
for f in files:
if success[f]:
print(" - {}: {}".format(f, color('green', 'SUCCESS')))
print(" - {}: {}".format(f, color("green", "SUCCESS")))
else:
print(" - {}: {}".format(f, color('bold_red', 'FAILED')))
print(" - {}: {}".format(f, color("bold_red", "FAILED")))
failed += 1
return failed
PRE_CONFIG_ACTIONS = {
'wizard': command_wizard,
'version': command_version,
'dashboard': command_dashboard,
'vscode': command_vscode,
'update-all': command_update_all,
"wizard": command_wizard,
"version": command_version,
"dashboard": command_dashboard,
"vscode": command_vscode,
"update-all": command_update_all,
}
POST_CONFIG_ACTIONS = {
'config': command_config,
'compile': command_compile,
'upload': command_upload,
'logs': command_logs,
'run': command_run,
'clean-mqtt': command_clean_mqtt,
'mqtt-fingerprint': command_mqtt_fingerprint,
'clean': command_clean,
"config": command_config,
"compile": command_compile,
"upload": command_upload,
"logs": command_logs,
"run": command_run,
"clean-mqtt": command_clean_mqtt,
"mqtt-fingerprint": command_mqtt_fingerprint,
"clean": command_clean,
}
def parse_args(argv):
parser = argparse.ArgumentParser(description=f'ESPHome v{const.__version__}')
parser.add_argument('-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('--dashboard', help=argparse.SUPPRESS, action='store_true')
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='*')
parser = argparse.ArgumentParser(description=f"ESPHome v{const.__version__}")
parser.add_argument(
"-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("--dashboard", help=argparse.SUPPRESS, action="store_true")
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.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',
help='Read the configuration and compile a program.')
parser_compile.add_argument('--only-generate',
help="Only generate source code, do not compile.",
action='store_true')
parser_compile = subparsers.add_parser(
"compile", help="Read the configuration and compile a program."
)
parser_compile.add_argument(
"--only-generate",
help="Only generate source code, do not compile.",
action="store_true",
)
parser_upload = subparsers.add_parser('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 = subparsers.add_parser(
"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_logs = subparsers.add_parser('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('--password', help='Manually set the password.')
parser_logs.add_argument('--client-id', help='Manually set the client id.')
parser_logs.add_argument('--serial-port', help="Manually specify a serial port to use"
"For example /dev/cu.SLAB_USBtoUART.")
parser_logs = subparsers.add_parser(
"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("--password", help="Manually set the password.")
parser_logs.add_argument("--client-id", help="Manually set the client id.")
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, '
'upload it, and start MQTT logs.')
parser_run.add_argument('--upload-port', help="Manually specify the upload port/ip to use. "
"For example /dev/cu.SLAB_USBtoUART.")
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_run = subparsers.add_parser(
"run",
help="Validate the configuration, create a binary, "
"upload it, and start MQTT logs.",
)
parser_run.add_argument(
"--upload-port",
help="Manually specify the upload port/ip to use. "
"For example /dev/cu.SLAB_USBtoUART.",
)
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 "
"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('--password', help='Manually set the password.')
parser_clean.add_argument('--client-id', help='Manually set the client id.')
parser_clean = subparsers.add_parser(
"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("--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 "
"you through setting up esphome.")
subparsers.add_parser(
"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',
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("--username", help="The optional username to require "
"for authentication.",
type=str, default='')
dashboard.add_argument("--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)
dashboard = subparsers.add_parser(
"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(
"--username",
help="The optional username to require " "for authentication.",
type=str,
default="",
)
dashboard.add_argument(
"--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.add_argument('--ace', action='store_true')
vscode = subparsers.add_parser("vscode", help=argparse.SUPPRESS)
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:])
@ -512,13 +634,15 @@ def run_esphome(argv):
CORE.dashboard = args.dashboard
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.")
return 1
if sys.version_info < (3, 6, 0):
_LOGGER.error("You're running ESPHome with Python <3.6. ESPHome is no longer compatible "
"with this Python version. Please reinstall ESPHome with Python 3.6+")
_LOGGER.error(
"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
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:
ip = resolve_ip_address(self._address)
except EsphomeError as err:
_LOGGER.warning("Error resolving IP address of %s. Is it connected to WiFi?",
self._address)
_LOGGER.warning("(If this error persists, please set a static IP address: "
"https://esphome.io/components/wifi.html#manual-ips)")
_LOGGER.warning(
"Error resolving IP address of %s. Is it connected to WiFi?",
self._address,
)
_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
_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()
hello = pb.HelloRequest()
hello.client_info = f'ESPHome v{const.__version__}'
hello.client_info = f"ESPHome v{const.__version__}"
try:
resp = self._send_message_await_response(hello, pb.HelloResponse)
except APIConnectionError as err:
self._fatal_error(err)
raise err
_LOGGER.debug("Successfully connected to %s ('%s' API=%s.%s)", self._address,
resp.server_info, resp.api_version_major, resp.api_version_minor)
_LOGGER.debug(
"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._refresh_ping()
if self.on_connect is not None:
@ -270,7 +279,9 @@ class APIClient(threading.Thread):
req += encoded
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()
responses = []
@ -295,12 +306,15 @@ class APIClient(threading.Thread):
def is_response(msg):
return isinstance(msg, response_type)
return self._send_message_await_response_complex(send_msg, is_response, is_response,
timeout)[0]
return self._send_message_await_response_complex(
send_msg, is_response, is_response, timeout
)[0]
def device_info(self):
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):
self._check_connected()
@ -310,7 +324,9 @@ class APIClient(threading.Thread):
self._check_connected()
try:
self._send_message_await_response(pb.DisconnectRequest(), pb.DisconnectResponse)
self._send_message_await_response(
pb.DisconnectRequest(), pb.DisconnectResponse
)
except APIConnectionError:
pass
self._close_socket()
@ -415,7 +431,7 @@ class APIClient(threading.Thread):
def run_logs(config, address):
conf = config['api']
conf = config["api"]
port = conf[CONF_PORT]
password = conf[CONF_PASSWORD]
_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)
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:
_LOGGER.warning("Initial connection failed. The ESP might not be connected "
"to WiFi yet (%s). Re-Trying in %s seconds",
error, wait_time)
_LOGGER.warning(
"Initial connection failed. The ESP might not be connected "
"to WiFi yet (%s). Re-Trying in %s seconds",
error,
wait_time,
)
else:
_LOGGER.warning("Couldn't connect to API (%s). Trying to reconnect in %s seconds",
error, wait_time)
timer = threading.Timer(wait_time, functools.partial(try_connect, None, tries + 1))
_LOGGER.warning(
"Couldn't connect to API (%s). Trying to reconnect in %s seconds",
error,
wait_time,
)
timer = threading.Timer(
wait_time, functools.partial(try_connect, None, tries + 1)
)
timer.start()
retry_timer.append(timer)
def on_log(msg):
time_ = datetime.now().time().strftime('[%H:%M:%S]')
time_ = datetime.now().time().strftime("[%H:%M:%S]")
text = msg.message
if msg.send_failed:
text = color('white', '(Message skipped because it was too big to fit in '
'TCP buffer - This is only cosmetic)')
text = color(
"white",
"(Message skipped because it was too big to fit in "
"TCP buffer - This is only cosmetic)",
)
safe_print(time_ + text)
def on_login():

View File

@ -1,8 +1,17 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.const import CONF_AUTOMATION_ID, CONF_CONDITION, CONF_ELSE, CONF_ID, CONF_THEN, \
CONF_TRIGGER_ID, CONF_TYPE_ID, CONF_TIME
from esphome.const import (
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.jsonschema import jschema_extractor
from esphome.util import Registry
@ -13,7 +22,12 @@ def maybe_simple_id(*validators):
def maybe_conf(conf, *validators):
validator = cv.All(*validators)
@jschema_extractor("maybe")
def validate(value):
# pylint: disable=comparison-with-callable
if value == jschema_extractor:
return validator
if isinstance(value, dict):
return validator(value)
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)
Action = cg.esphome_ns.class_('Action')
Trigger = cg.esphome_ns.class_('Trigger')
Action = cg.esphome_ns.class_("Action")
Trigger = cg.esphome_ns.class_("Trigger")
ACTION_REGISTRY = Registry()
Condition = cg.esphome_ns.class_('Condition')
Condition = cg.esphome_ns.class_("Condition")
CONDITION_REGISTRY = Registry()
validate_action = cv.validate_registry_entry('action', ACTION_REGISTRY)
validate_action_list = cv.validate_registry('action', ACTION_REGISTRY)
validate_condition = cv.validate_registry_entry('condition', CONDITION_REGISTRY)
validate_condition_list = cv.validate_registry('condition', CONDITION_REGISTRY)
validate_action = cv.validate_registry_entry("action", ACTION_REGISTRY)
validate_action_list = cv.validate_registry("action", ACTION_REGISTRY)
validate_condition = cv.validate_registry_entry("condition", CONDITION_REGISTRY)
validate_condition_list = cv.validate_registry("condition", CONDITION_REGISTRY)
def validate_potentially_and_condition(value):
if isinstance(value, list):
with cv.remove_prepend_path(['and']):
return validate_condition({
'and': value
})
with cv.remove_prepend_path(["and"]):
return validate_condition({"and": value})
return validate_condition(value)
DelayAction = cg.esphome_ns.class_('DelayAction', Action, cg.Component)
LambdaAction = cg.esphome_ns.class_('LambdaAction', Action)
IfAction = cg.esphome_ns.class_('IfAction', Action)
WhileAction = cg.esphome_ns.class_('WhileAction', Action)
WaitUntilAction = cg.esphome_ns.class_('WaitUntilAction', Action, cg.Component)
UpdateComponentAction = cg.esphome_ns.class_('UpdateComponentAction', Action)
Automation = cg.esphome_ns.class_('Automation')
DelayAction = cg.esphome_ns.class_("DelayAction", Action, cg.Component)
LambdaAction = cg.esphome_ns.class_("LambdaAction", Action)
IfAction = cg.esphome_ns.class_("IfAction", Action)
WhileAction = cg.esphome_ns.class_("WhileAction", Action)
WaitUntilAction = cg.esphome_ns.class_("WaitUntilAction", Action, cg.Component)
UpdateComponentAction = cg.esphome_ns.class_("UpdateComponentAction", Action)
Automation = cg.esphome_ns.class_("Automation")
LambdaCondition = cg.esphome_ns.class_('LambdaCondition', Condition)
ForCondition = cg.esphome_ns.class_('ForCondition', Condition, cg.Component)
LambdaCondition = cg.esphome_ns.class_("LambdaCondition", Condition)
ForCondition = cg.esphome_ns.class_("ForCondition", Condition, cg.Component)
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:
return cv.Schema([schema])(value)
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
raise err
if 'Unable to find action' in str(err):
if "Unable to find action" in str(err):
raise err2
raise cv.MultipleInvalid([err, err2])
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.
return [schema(value)]
@jschema_extractor("automation")
def validator(value):
# hack to get the schema
# pylint: disable=comparison-with-callable
if value == jschema_extractor:
return schema
value = validator_(value)
if extra_validators is not None:
value = cv.Schema([extra_validators])(value)
@ -110,47 +128,59 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False):
return validator
AUTOMATION_SCHEMA = cv.Schema({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Trigger),
cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_id(Automation),
cv.Required(CONF_THEN): validate_action_list,
})
AUTOMATION_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Trigger),
cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_id(Automation),
cv.Required(CONF_THEN): validate_action_list,
}
)
AndCondition = cg.esphome_ns.class_('AndCondition', Condition)
OrCondition = cg.esphome_ns.class_('OrCondition', Condition)
NotCondition = cg.esphome_ns.class_('NotCondition', Condition)
AndCondition = cg.esphome_ns.class_("AndCondition", Condition)
OrCondition = cg.esphome_ns.class_("OrCondition", 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):
conditions = yield build_condition_list(config, template_arg, args)
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):
conditions = yield build_condition_list(config, template_arg, args)
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):
condition = yield build_condition(config, template_arg, args)
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):
lambda_ = yield cg.process_lambda(config, args, return_type=bool)
yield cg.new_Pvariable(condition_id, template_arg, lambda_)
@register_condition('for', ForCondition, 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))
@register_condition(
"for",
ForCondition,
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):
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)
yield cg.register_component(var, config)
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
@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):
var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_component(var, {})
@ -167,11 +199,18 @@ def delay_action_to_code(config, action_id, template_arg, args):
yield var
@register_action('if', IfAction, cv.All({
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)))
@register_action(
"if",
IfAction,
cv.All(
{
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):
conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
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
@register_action('while', WhileAction, cv.Schema({
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
cv.Required(CONF_THEN): validate_action_list,
}))
@register_action(
"while",
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):
conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
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):
schema = cv.Schema({
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
})
schema = cv.Schema(
{
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
}
)
if isinstance(value, dict) and CONF_CONDITION in value:
return schema(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):
conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
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
@register_action('lambda', LambdaAction, cv.lambda_)
@register_action("lambda", LambdaAction, cv.lambda_)
def lambda_action_to_code(config, action_id, template_arg, args):
lambda_ = yield cg.process_lambda(config, args, return_type=cg.void)
yield cg.new_Pvariable(action_id, template_arg, lambda_)
@register_action('component.update', UpdateComponentAction, maybe_simple_id({
cv.Required(CONF_ID): cv.use_id(cg.PollingComponent),
}))
@register_action(
"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):
comp = yield cg.get_variable(config[CONF_ID])
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
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]
builder = registry_entry.coroutine_fun
yield builder(config, action_id, template_arg, args)
@ -246,7 +301,9 @@ def build_action_list(config, templ, arg_type):
@coroutine
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]
builder = registry_entry.coroutine_fun
yield builder(config, action_id, template_arg, args)

View File

@ -9,18 +9,71 @@
# pylint: disable=unused-import
from esphome.cpp_generator import ( # noqa
Expression, RawExpression, RawStatement, TemplateArguments,
StructInitializer, ArrayInitializer, safe_exp, Statement, LineComment,
progmem_array, statement, 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)
Expression,
RawExpression,
RawStatement,
TemplateArguments,
StructInitializer,
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
gpio_pin_expression, register_component, build_registry_entry,
build_registry_list, extract_registry_entry_config, register_parented)
gpio_pin_expression,
register_component,
build_registry_entry,
build_registry_list,
extract_registry_entry_config,
register_parented,
)
from esphome.cpp_types import ( # noqa
global_ns, void, nullptr, float_, 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)
global_ns,
void,
nullptr,
float_,
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) {
this->sleep_pin_->setup();
this->sleep_pin_->digital_write(false);
this->sleep_pin_state_ = false;
}
this->step_pin_->setup();
this->step_pin_->digital_write(false);
@ -27,7 +28,12 @@ void A4988::dump_config() {
void A4988::loop() {
bool at_target = this->has_reached_target();
if (this->sleep_pin_ != nullptr) {
bool sleep_rising_edge = !sleep_pin_state_ & !at_target;
this->sleep_pin_->digital_write(!at_target);
this->sleep_pin_state_ = !at_target;
if (sleep_rising_edge) {
delayMicroseconds(1000);
}
}
if (at_target) {
this->high_freq_.stop();

View File

@ -21,6 +21,7 @@ class A4988 : public stepper::Stepper, public Component {
GPIOPin *step_pin_;
GPIOPin *dir_pin_;
GPIOPin *sleep_pin_{nullptr};
bool sleep_pin_state_;
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
a4988_ns = cg.esphome_ns.namespace('a4988')
A4988 = a4988_ns.class_('A4988', stepper.Stepper, cg.Component)
a4988_ns = cg.esphome_ns.namespace("a4988")
A4988 = a4988_ns.class_("A4988", stepper.Stepper, cg.Component)
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_DIR_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_SLEEP_PIN): pins.gpio_output_pin_schema,
}).extend(cv.COMPONENT_SCHEMA)
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_DIR_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_SLEEP_PIN): pins.gpio_output_pin_schema,
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config):

View File

@ -4,28 +4,32 @@ from esphome import pins
from esphome.components import output
from esphome.const import CONF_ID, CONF_MIN_POWER, CONF_METHOD
CODEOWNERS = ['@glmnet']
CODEOWNERS = ["@glmnet"]
ac_dimmer_ns = cg.esphome_ns.namespace('ac_dimmer')
AcDimmer = ac_dimmer_ns.class_('AcDimmer', output.FloatOutput, cg.Component)
ac_dimmer_ns = cg.esphome_ns.namespace("ac_dimmer")
AcDimmer = ac_dimmer_ns.class_("AcDimmer", output.FloatOutput, cg.Component)
DimMethod = ac_dimmer_ns.enum('DimMethod')
DimMethod = ac_dimmer_ns.enum("DimMethod")
DIM_METHODS = {
'LEADING_PULSE': DimMethod.DIM_METHOD_LEADING_PULSE,
'LEADING': DimMethod.DIM_METHOD_LEADING,
'TRAILING': DimMethod.DIM_METHOD_TRAILING,
"LEADING_PULSE": DimMethod.DIM_METHOD_LEADING_PULSE,
"LEADING": DimMethod.DIM_METHOD_LEADING,
"TRAILING": DimMethod.DIM_METHOD_TRAILING,
}
CONF_GATE_PIN = 'gate_pin'
CONF_ZERO_CROSS_PIN = 'zero_cross_pin'
CONF_INIT_WITH_HALF_CYCLE = 'init_with_half_cycle'
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_ZERO_CROSS_PIN): pins.internal_gpio_input_pin_schema,
cv.Optional(CONF_INIT_WITH_HALF_CYCLE, default=True): cv.boolean,
cv.Optional(CONF_METHOD, default='leading pulse'): cv.enum(DIM_METHODS, upper=True, space='_'),
}).extend(cv.COMPONENT_SCHEMA)
CONF_GATE_PIN = "gate_pin"
CONF_ZERO_CROSS_PIN = "zero_cross_pin"
CONF_INIT_WITH_HALF_CYCLE = "init_with_half_cycle"
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_ZERO_CROSS_PIN): pins.internal_gpio_input_pin_schema,
cv.Optional(CONF_INIT_WITH_HALF_CYCLE, default=True): cv.boolean,
cv.Optional(CONF_METHOD, default="leading pulse"): cv.enum(
DIM_METHODS, upper=True, space="_"
),
}
).extend(cv.COMPONENT_SCHEMA)
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.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', uart.UARTDevice, AddressableLightEffect)
"AdalightLightEffect", uart.UARTDevice, AddressableLightEffect
)
CONFIG_SCHEMA = cv.Schema({})
@register_addressable_effect('adalight', AdalightLightEffect, "Adalight", {
cv.GenerateID(CONF_UART_ID): cv.use_id(uart.UARTComponent)
})
@register_addressable_effect(
"adalight",
AdalightLightEffect,
"Adalight",
{cv.GenerateID(CONF_UART_ID): cv.use_id(uart.UARTComponent)},
)
def adalight_light_effect_to_code(config, effect_id):
effect = cg.new_Pvariable(effect_id, config[CONF_NAME])
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) {
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();
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) {
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;

View File

@ -16,7 +16,7 @@ class AdalightLightEffect : public light::AddressableLightEffect, public uart::U
public:
void start() 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:
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() {
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
#ifndef USE_ADC_SENSOR_VCC
GPIOPin(this->pin_, INPUT).setup();
#endif
#ifdef ARDUINO_ARCH_ESP32
analogSetPinAttenuation(this->pin_, this->attenuation_);

View File

@ -2,36 +2,51 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
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 = {
'0db': cg.global_ns.ADC_0db,
'2.5db': cg.global_ns.ADC_2_5db,
'6db': cg.global_ns.ADC_6db,
'11db': cg.global_ns.ADC_11db,
"0db": cg.global_ns.ADC_0db,
"2.5db": cg.global_ns.ADC_2_5db,
"6db": cg.global_ns.ADC_6db,
"11db": cg.global_ns.ADC_11db,
}
def validate_adc_pin(value):
vcc = str(value).upper()
if vcc == 'VCC':
if vcc == "VCC":
return cv.only_on_esp8266(vcc)
return pins.analog_pin(value)
adc_ns = cg.esphome_ns.namespace('adc')
ADCSensor = adc_ns.class_('ADCSensor', sensor.Sensor, cg.PollingComponent,
voltage_sampler.VoltageSampler)
adc_ns = cg.esphome_ns.namespace("adc")
ADCSensor = adc_ns.class_(
"ADCSensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
)
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2).extend({
cv.GenerateID(): cv.declare_id(ADCSensor),
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'))
CONFIG_SCHEMA = (
sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE)
.extend(
{
cv.GenerateID(): cv.declare_id(ADCSensor),
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):
@ -39,8 +54,8 @@ def to_code(config):
yield cg.register_component(var, config)
yield sensor.register_sensor(var, config)
if config[CONF_PIN] == 'VCC':
cg.add_define('USE_ADC_SENSOR_VCC')
if config[CONF_PIN] == "VCC":
cg.add_define("USE_ADC_SENSOR_VCC")
else:
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
from esphome.components import sensor, i2c
from esphome import pins
from esphome.const import CONF_ID, CONF_VOLTAGE, \
UNIT_VOLT, ICON_FLASH, UNIT_AMPERE, UNIT_WATT
from esphome.const import (
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 = ade7953_ns.class_('ADE7953', cg.PollingComponent, i2c.I2CDevice)
ade7953_ns = cg.esphome_ns.namespace("ade7953")
ADE7953 = ade7953_ns.class_("ADE7953", cg.PollingComponent, i2c.I2CDevice)
CONF_IRQ_PIN = 'irq_pin'
CONF_CURRENT_A = 'current_a'
CONF_CURRENT_B = 'current_b'
CONF_ACTIVE_POWER_A = 'active_power_a'
CONF_ACTIVE_POWER_B = 'active_power_b'
CONF_IRQ_PIN = "irq_pin"
CONF_CURRENT_A = "current_a"
CONF_CURRENT_B = "current_b"
CONF_ACTIVE_POWER_A = "active_power_a"
CONF_ACTIVE_POWER_B = "active_power_b"
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(ADE7953),
cv.Optional(CONF_IRQ_PIN): pins.input_pin,
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1),
cv.Optional(CONF_CURRENT_A): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2),
cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2),
cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 1),
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))
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(ADE7953),
cv.Optional(CONF_IRQ_PIN): pins.input_pin,
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE
),
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):
@ -35,10 +60,15 @@ def to_code(config):
if CONF_IRQ_PIN in config:
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,
CONF_ACTIVE_POWER_B]:
for key in [
CONF_VOLTAGE,
CONF_CURRENT_A,
CONF_CURRENT_B,
CONF_ACTIVE_POWER_A,
CONF_ACTIVE_POWER_B,
]:
if key not in config:
continue
conf = config[key]
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.const import CONF_ID
DEPENDENCIES = ['i2c']
AUTO_LOAD = ['sensor', 'voltage_sampler']
DEPENDENCIES = ["i2c"]
AUTO_LOAD = ["sensor", "voltage_sampler"]
MULTI_CONF = True
ads1115_ns = cg.esphome_ns.namespace('ads1115')
ADS1115Component = ads1115_ns.class_('ADS1115Component', cg.Component, i2c.I2CDevice)
ads1115_ns = cg.esphome_ns.namespace("ads1115")
ADS1115Component = ads1115_ns.class_("ADS1115Component", cg.Component, i2c.I2CDevice)
CONF_CONTINUOUS_MODE = 'continuous_mode'
CONFIG_SCHEMA = cv.Schema({
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))
CONF_CONTINUOUS_MODE = "continuous_mode"
CONFIG_SCHEMA = (
cv.Schema(
{
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):

View File

@ -1,53 +1,67 @@
import esphome.codegen as cg
import esphome.config_validation as cv
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
DEPENDENCIES = ['ads1115']
DEPENDENCIES = ["ads1115"]
ADS1115Multiplexer = ads1115_ns.enum('ADS1115Multiplexer')
ADS1115Multiplexer = ads1115_ns.enum("ADS1115Multiplexer")
MUX = {
'A0_A1': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N1,
'A0_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N3,
'A1_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_N3,
'A2_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_N3,
'A0_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_NG,
'A1_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_NG,
'A2_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_NG,
'A3_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P3_NG,
"A0_A1": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N1,
"A0_A3": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N3,
"A1_A3": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_N3,
"A2_A3": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_N3,
"A0_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_NG,
"A1_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_NG,
"A2_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_NG,
"A3_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P3_NG,
}
ADS1115Gain = ads1115_ns.enum('ADS1115Gain')
ADS1115Gain = ads1115_ns.enum("ADS1115Gain")
GAIN = {
'6.144': ADS1115Gain.ADS1115_GAIN_6P144,
'4.096': ADS1115Gain.ADS1115_GAIN_4P096,
'2.048': ADS1115Gain.ADS1115_GAIN_2P048,
'1.024': ADS1115Gain.ADS1115_GAIN_1P024,
'0.512': ADS1115Gain.ADS1115_GAIN_0P512,
'0.256': ADS1115Gain.ADS1115_GAIN_0P256,
"6.144": ADS1115Gain.ADS1115_GAIN_6P144,
"4.096": ADS1115Gain.ADS1115_GAIN_4P096,
"2.048": ADS1115Gain.ADS1115_GAIN_2P048,
"1.024": ADS1115Gain.ADS1115_GAIN_1P024,
"0.512": ADS1115Gain.ADS1115_GAIN_0P512,
"0.256": ADS1115Gain.ADS1115_GAIN_0P256,
}
def validate_gain(value):
if isinstance(value, float):
value = f'{value:0.03f}'
value = f"{value:0.03f}"
elif not isinstance(value, str):
raise cv.Invalid(f'invalid gain "{value}"')
return cv.enum(GAIN)(value)
ADS1115Sensor = ads1115_ns.class_('ADS1115Sensor', sensor.Sensor, cg.PollingComponent,
voltage_sampler.VoltageSampler)
ADS1115Sensor = ads1115_ns.class_(
"ADS1115Sensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
)
CONF_ADS1115_ID = 'ads1115_id'
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 3).extend({
cv.GenerateID(): cv.declare_id(ADS1115Sensor),
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'))
CONF_ADS1115_ID = "ads1115_id"
CONFIG_SCHEMA = (
sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE)
.extend(
{
cv.GenerateID(): cv.declare_id(ADS1115Sensor),
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):

View File

@ -1,19 +1,37 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, \
UNIT_CELSIUS, ICON_THERMOMETER, ICON_WATER_PERCENT, UNIT_PERCENT
from esphome.const import (
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')
AHT10Component = aht10_ns.class_('AHT10Component', cg.PollingComponent, i2c.I2CDevice)
aht10_ns = cg.esphome_ns.namespace("aht10")
AHT10Component = aht10_ns.class_("AHT10Component", cg.PollingComponent, i2c.I2CDevice)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(AHT10Component),
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),
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x38))
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(AHT10Component),
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):

View File

@ -1,19 +1,39 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, \
UNIT_CELSIUS, ICON_THERMOMETER, ICON_WATER_PERCENT, UNIT_PERCENT
from esphome.const import (
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')
AM2320Component = am2320_ns.class_('AM2320Component', cg.PollingComponent, i2c.I2CDevice)
am2320_ns = cg.esphome_ns.namespace("am2320")
AM2320Component = am2320_ns.class_(
"AM2320Component", cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(AM2320Component),
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),
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x5C))
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(AM2320Component),
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):

View File

@ -10,24 +10,28 @@ from esphome.core import CORE, HexInt
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['display']
DEPENDENCIES = ["display"]
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({
cv.Required(CONF_ID): cv.declare_id(Animation_),
cv.Required(CONF_FILE): cv.file_,
cv.Optional(CONF_RESIZE): cv.dimensions,
cv.Optional(CONF_TYPE, default='BINARY'): cv.enum(espImage.IMAGE_TYPE, upper=True),
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
})
ANIMATION_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.declare_id(Animation_),
cv.Required(CONF_FILE): cv.file_,
cv.Optional(CONF_RESIZE): cv.dimensions,
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)
CODEOWNERS = ['@syndlex']
CODEOWNERS = ["@syndlex"]
def to_code(config):
@ -46,26 +50,28 @@ def to_code(config):
width, height = image.size
else:
if width > 500 or height > 500:
_LOGGER.warning("The image you requested is very big. Please consider using"
" the resize parameter.")
_LOGGER.warning(
"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)]
pos = 0
for frameIndex in range(frames):
image.seek(frameIndex)
frame = image.convert('L', dither=Image.NONE)
frame = image.convert("L", dither=Image.NONE)
pixels = list(frame.getdata())
for pix in pixels:
data[pos] = pix
pos += 1
elif config[CONF_TYPE] == 'RGB24':
elif config[CONF_TYPE] == "RGB24":
data = [0 for _ in range(height * width * 3 * frames)]
pos = 0
for frameIndex in range(frames):
image.seek(frameIndex)
frame = image.convert('RGB')
frame = image.convert("RGB")
pixels = list(frame.getdata())
for pix in pixels:
data[pos] = pix[0]
@ -75,12 +81,12 @@ def to_code(config):
data[pos] = pix[2]
pos += 1
elif config[CONF_TYPE] == 'BINARY':
elif config[CONF_TYPE] == "BINARY":
width8 = ((width + 7) // 8) * 8
data = [0 for _ in range((height * width8 // 8) * frames)]
for frameIndex in range(frames):
image.seek(frameIndex)
frame = image.convert('1', dither=Image.NONE)
frame = image.convert("1", dither=Image.NONE)
for y in range(height):
for x in range(width):
if frame.getpixel((x, y)):
@ -90,5 +96,11 @@ def to_code(config):
rhs = [HexInt(x) for x in data]
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
cg.new_Pvariable(config[CONF_ID], prog_arr, width, height, frames,
espImage.IMAGE_TYPE[config[CONF_TYPE]])
cg.new_Pvariable(
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.const import CONF_ID
DEPENDENCIES = ['i2c']
AUTO_LOAD = ['sensor', 'binary_sensor']
DEPENDENCIES = ["i2c"]
AUTO_LOAD = ["sensor", "binary_sensor"]
MULTI_CONF = True
CONF_APDS9960_ID = 'apds9960_id'
CONF_APDS9960_ID = "apds9960_id"
apds9960_nds = cg.esphome_ns.namespace('apds9960')
APDS9960 = apds9960_nds.class_('APDS9960', cg.PollingComponent, i2c.I2CDevice)
apds9960_nds = cg.esphome_ns.namespace("apds9960")
APDS9960 = apds9960_nds.class_("APDS9960", cg.PollingComponent, i2c.I2CDevice)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(APDS9960),
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x39))
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(APDS9960),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x39))
)
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 . import APDS9960, CONF_APDS9960_ID
DEPENDENCIES = ['apds9960']
DEPENDENCIES = ["apds9960"]
DIRECTIONS = {
'UP': 'set_up_direction',
'DOWN': 'set_down_direction',
'LEFT': 'set_left_direction',
'RIGHT': 'set_right_direction',
"UP": "set_up_direction",
"DOWN": "set_down_direction",
"LEFT": "set_left_direction",
"RIGHT": "set_right_direction",
}
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.Optional(CONF_DEVICE_CLASS, default=DEVICE_CLASS_MOVING): binary_sensor.device_class,
})
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.Optional(
CONF_DEVICE_CLASS, default=DEVICE_CLASS_MOVING
): binary_sensor.device_class,
}
)
def to_code(config):

View File

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

View File

@ -2,46 +2,69 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.automation import Condition
from esphome.const import CONF_DATA, 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.const import (
CONF_DATA,
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
DEPENDENCIES = ['network']
AUTO_LOAD = ['async_tcp']
CODEOWNERS = ['@OttoWinter']
DEPENDENCIES = ["network"]
AUTO_LOAD = ["async_tcp"]
CODEOWNERS = ["@OttoWinter"]
api_ns = cg.esphome_ns.namespace('api')
APIServer = api_ns.class_('APIServer', cg.Component, cg.Controller)
HomeAssistantServiceCallAction = api_ns.class_('HomeAssistantServiceCallAction', automation.Action)
APIConnectedCondition = api_ns.class_('APIConnectedCondition', Condition)
api_ns = cg.esphome_ns.namespace("api")
APIServer = api_ns.class_("APIServer", cg.Component, cg.Controller)
HomeAssistantServiceCallAction = api_ns.class_(
"HomeAssistantServiceCallAction", automation.Action
)
APIConnectedCondition = api_ns.class_("APIConnectedCondition", Condition)
UserServiceTrigger = api_ns.class_('UserServiceTrigger', automation.Trigger)
ListEntitiesServicesArgument = api_ns.class_('ListEntitiesServicesArgument')
UserServiceTrigger = api_ns.class_("UserServiceTrigger", automation.Trigger)
ListEntitiesServicesArgument = api_ns.class_("ListEntitiesServicesArgument")
SERVICE_ARG_NATIVE_TYPES = {
'bool': bool,
'int': cg.int32,
'float': float,
'string': cg.std_string,
'bool[]': cg.std_vector.template(bool),
'int[]': cg.std_vector.template(cg.int32),
'float[]': cg.std_vector.template(float),
'string[]': cg.std_vector.template(cg.std_string),
"bool": bool,
"int": cg.int32,
"float": float,
"string": cg.std_string,
"bool[]": cg.std_vector.template(bool),
"int[]": cg.std_vector.template(cg.int32),
"float[]": cg.std_vector.template(float),
"string[]": cg.std_vector.template(cg.std_string),
}
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(APIServer),
cv.Optional(CONF_PORT, default=6053): cv.port,
cv.Optional(CONF_PASSWORD, default=''): cv.string_strict,
cv.Optional(CONF_REBOOT_TIMEOUT, default='15min'): cv.positive_time_period_milliseconds,
cv.Optional(CONF_SERVICES): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UserServiceTrigger),
cv.Required(CONF_SERVICE): cv.valid_name,
cv.Optional(CONF_VARIABLES, default={}): cv.Schema({
cv.validate_id_name: cv.one_of(*SERVICE_ARG_NATIVE_TYPES, lower=True),
}),
}),
}).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(APIServer),
cv.Optional(CONF_PORT, default=6053): cv.port,
cv.Optional(CONF_PASSWORD, default=""): cv.string_strict,
cv.Optional(
CONF_REBOOT_TIMEOUT, default="15min"
): cv.positive_time_period_milliseconds,
cv.Optional(CONF_SERVICES): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UserServiceTrigger),
cv.Required(CONF_SERVICE): cv.valid_name,
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)
@ -63,28 +86,36 @@ def to_code(config):
func_args.append((native, name))
service_arg_names.append(name)
templ = cg.TemplateArguments(*template_args)
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], templ,
conf[CONF_SERVICE], service_arg_names)
trigger = cg.new_Pvariable(
conf[CONF_TRIGGER_ID], templ, conf[CONF_SERVICE], service_arg_names
)
cg.add(var.register_user_service(trigger))
yield automation.build_automation(trigger, func_args, conf)
cg.add_define('USE_API')
cg.add_define("USE_API")
cg.add_global(api_ns.using)
KEY_VALUE_SCHEMA = cv.Schema({cv.string: cv.templatable(cv.string_strict)})
HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema({
cv.GenerateID(): cv.use_id(APIServer),
cv.Required(CONF_SERVICE): cv.templatable(cv.string),
cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA,
cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA,
cv.Optional(CONF_VARIABLES, default={}): cv.Schema({cv.string: cv.returning_lambda}),
})
HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.use_id(APIServer),
cv.Required(CONF_SERVICE): cv.templatable(cv.string),
cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA,
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,
HOMEASSISTANT_SERVICE_ACTION_SCHEMA)
@automation.register_action(
"homeassistant.service",
HomeAssistantServiceCallAction,
HOMEASSISTANT_SERVICE_ACTION_SCHEMA,
)
def homeassistant_service_to_code(config, action_id, template_arg, args):
serv = yield cg.get_variable(config[CONF_ID])
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):
value = cv.string(value)
if not value.startswith('esphome.'):
raise cv.Invalid("ESPHome can only generate Home Assistant events that begin with "
"esphome. For example 'esphome.xyz'")
if not value.startswith("esphome."):
raise cv.Invalid(
"ESPHome can only generate Home Assistant events that begin with "
"esphome. For example 'esphome.xyz'"
)
return value
HOMEASSISTANT_EVENT_ACTION_SCHEMA = cv.Schema({
cv.GenerateID(): cv.use_id(APIServer),
cv.Required(CONF_EVENT): validate_homeassistant_event,
cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA,
cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA,
cv.Optional(CONF_VARIABLES, default={}): KEY_VALUE_SCHEMA,
})
HOMEASSISTANT_EVENT_ACTION_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.use_id(APIServer),
cv.Required(CONF_EVENT): validate_homeassistant_event,
cv.Optional(CONF_DATA, 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,
HOMEASSISTANT_EVENT_ACTION_SCHEMA)
@automation.register_action(
"homeassistant.event",
HomeAssistantServiceCallAction,
HOMEASSISTANT_EVENT_ACTION_SCHEMA,
)
def homeassistant_event_to_code(config, action_id, template_arg, args):
serv = yield cg.get_variable(config[CONF_ID])
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
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value({
cv.GenerateID(): cv.use_id(APIServer),
cv.Required(CONF_TAG): cv.templatable(cv.string_strict),
}, key=CONF_TAG)
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value(
{
cv.GenerateID(): cv.use_id(APIServer),
cv.Required(CONF_TAG): cv.templatable(cv.string_strict),
},
key=CONF_TAG,
)
@automation.register_action('homeassistant.tag_scanned', HomeAssistantServiceCallAction,
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA)
@automation.register_action(
"homeassistant.tag_scanned",
HomeAssistantServiceCallAction,
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA,
)
def homeassistant_tag_scanned_to_code(config, action_id, template_arg, args):
serv = yield cg.get_variable(config[CONF_ID])
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)
cg.add(var.add_data('tag_id', templ))
cg.add(var.add_data("tag_id", templ))
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):
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
// TCP socket with short binary messages encoded in the protocol buffers 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 type of message.
// * The message object encoded as a ProtoBuf message
@ -302,6 +303,7 @@ message ListEntitiesFanResponse {
bool supports_oscillation = 5;
bool supports_speed = 6;
bool supports_direction = 7;
int32 supported_speed_count = 8;
}
enum FanSpeed {
FAN_SPEED_LOW = 0;
@ -321,8 +323,9 @@ message FanStateResponse {
fixed32 key = 1;
bool state = 2;
bool oscillating = 3;
FanSpeed speed = 4;
FanSpeed speed = 4 [deprecated = true];
FanDirection direction = 5;
int32 speed_level = 6;
}
message FanCommandRequest {
option (id) = 31;
@ -333,12 +336,14 @@ message FanCommandRequest {
fixed32 key = 1;
bool has_state = 2;
bool state = 3;
bool has_speed = 4;
FanSpeed speed = 5;
bool has_speed = 4 [deprecated = true];
FanSpeed speed = 5 [deprecated = true];
bool has_oscillating = 6;
bool oscillating = 7;
bool has_direction = 8;
FanDirection direction = 9;
bool has_speed_level = 10;
int32 speed_level = 11;
}
// ==================== LIGHT ====================
@ -418,6 +423,7 @@ message ListEntitiesSensorResponse {
string unit_of_measurement = 6;
int32 accuracy_decimals = 7;
bool force_update = 8;
string device_class = 9;
}
message SensorStateResponse {
option (id) = 25;

View File

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

View File

@ -774,6 +774,10 @@ bool ListEntitiesFanResponse::decode_varint(uint32_t field_id, ProtoVarInt value
this->supports_direction = value.as_bool();
return true;
}
case 8: {
this->supported_speed_count = value.as_int32();
return true;
}
default:
return false;
}
@ -814,6 +818,7 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(5, this->supports_oscillation);
buffer.encode_bool(6, this->supports_speed);
buffer.encode_bool(7, this->supports_direction);
buffer.encode_int32(8, this->supported_speed_count);
}
void ListEntitiesFanResponse::dump_to(std::string &out) const {
char buffer[64];
@ -846,6 +851,11 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const {
out.append(" supports_direction: ");
out.append(YESNO(this->supports_direction));
out.append("\n");
out.append(" supported_speed_count: ");
sprintf(buffer, "%d", this->supported_speed_count);
out.append(buffer);
out.append("\n");
out.append("}");
}
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>();
return true;
}
case 6: {
this->speed_level = value.as_int32();
return true;
}
default:
return false;
}
@ -886,6 +900,7 @@ void FanStateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(3, this->oscillating);
buffer.encode_enum<enums::FanSpeed>(4, this->speed);
buffer.encode_enum<enums::FanDirection>(5, this->direction);
buffer.encode_int32(6, this->speed_level);
}
void FanStateResponse::dump_to(std::string &out) const {
char buffer[64];
@ -910,6 +925,11 @@ void FanStateResponse::dump_to(std::string &out) const {
out.append(" direction: ");
out.append(proto_enum_to_string<enums::FanDirection>(this->direction));
out.append("\n");
out.append(" speed_level: ");
sprintf(buffer, "%d", this->speed_level);
out.append(buffer);
out.append("\n");
out.append("}");
}
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>();
return true;
}
case 10: {
this->has_speed_level = value.as_bool();
return true;
}
case 11: {
this->speed_level = value.as_int32();
return true;
}
default:
return false;
}
@ -970,6 +998,8 @@ void FanCommandRequest::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(7, this->oscillating);
buffer.encode_bool(8, this->has_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 {
char buffer[64];
@ -1010,6 +1040,15 @@ void FanCommandRequest::dump_to(std::string &out) const {
out.append(" direction: ");
out.append(proto_enum_to_string<enums::FanDirection>(this->direction));
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("}");
}
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();
return true;
}
case 9: {
this->device_class = value.as_string();
return true;
}
default:
return false;
}
@ -1517,6 +1560,7 @@ void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(6, this->unit_of_measurement);
buffer.encode_int32(7, this->accuracy_decimals);
buffer.encode_bool(8, this->force_update);
buffer.encode_string(9, this->device_class);
}
void ListEntitiesSensorResponse::dump_to(std::string &out) const {
char buffer[64];
@ -1554,6 +1598,10 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const {
out.append(" force_update: ");
out.append(YESNO(this->force_update));
out.append("\n");
out.append(" device_class: ");
out.append("'").append(this->device_class).append("'");
out.append("\n");
out.append("}");
}
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_speed{false}; // NOLINT
bool supports_direction{false}; // NOLINT
int32_t supported_speed_count{0}; // NOLINT
void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override;
@ -299,6 +300,7 @@ class FanStateResponse : public ProtoMessage {
bool oscillating{false}; // NOLINT
enums::FanSpeed speed{}; // NOLINT
enums::FanDirection direction{}; // NOLINT
int32_t speed_level{0}; // NOLINT
void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override;
@ -317,6 +319,8 @@ class FanCommandRequest : public ProtoMessage {
bool oscillating{false}; // NOLINT
bool has_direction{false}; // NOLINT
enums::FanDirection direction{}; // NOLINT
bool has_speed_level{false}; // NOLINT
int32_t speed_level{0}; // NOLINT
void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override;
@ -403,6 +407,7 @@ class ListEntitiesSensorResponse : public ProtoMessage {
std::string unit_of_measurement{}; // NOLINT
int32_t accuracy_decimals{0}; // NOLINT
bool force_update{false}; // NOLINT
std::string device_class{}; // NOLINT
void encode(ProtoWriteBuffer buffer) const override;
void dump_to(std::string &out) const override;

View File

@ -1,33 +1,43 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.const import CONF_INDOOR, CONF_WATCHDOG_THRESHOLD, \
CONF_NOISE_LEVEL, CONF_SPIKE_REJECTION, CONF_LIGHTNING_THRESHOLD, \
CONF_MASK_DISTURBER, CONF_DIV_RATIO, CONF_CAPACITANCE
from esphome.const import (
CONF_INDOOR,
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
AUTO_LOAD = ['sensor', 'binary_sensor']
AUTO_LOAD = ["sensor", "binary_sensor"]
MULTI_CONF = True
CONF_AS3935_ID = 'as3935_id'
CONF_AS3935_ID = "as3935_id"
as3935_ns = cg.esphome_ns.namespace('as3935')
AS3935 = as3935_ns.class_('AS3935Component', cg.Component)
as3935_ns = cg.esphome_ns.namespace("as3935")
AS3935 = as3935_ns.class_("AS3935Component", cg.Component)
CONF_IRQ_PIN = 'irq_pin'
AS3935_SCHEMA = cv.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_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_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_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),
})
CONF_IRQ_PIN = "irq_pin"
AS3935_SCHEMA = cv.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_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_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_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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,14 +2,14 @@
import esphome.codegen as cg
from esphome.core import CORE, coroutine_with_priority
CODEOWNERS = ['@OttoWinter']
CODEOWNERS = ["@OttoWinter"]
@coroutine_with_priority(200.0)
def to_code(config):
if CORE.is_esp32:
# 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:
# 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.config_validation as cv
from esphome.components import sensor, esp32_ble_tracker
from esphome.const import CONF_BATTERY_LEVEL, CONF_BATTERY_VOLTAGE, CONF_MAC_ADDRESS, \
CONF_HUMIDITY, CONF_TEMPERATURE, CONF_ID, UNIT_CELSIUS, UNIT_PERCENT, UNIT_VOLT, \
ICON_BATTERY, ICON_THERMOMETER, ICON_WATER_PERCENT
from esphome.const import (
CONF_BATTERY_LEVEL,
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')
ATCMiThermometer = atc_mithermometer_ns.class_('ATCMiThermometer',
esp32_ble_tracker.ESPBTDeviceListener,
cg.Component)
atc_mithermometer_ns = cg.esphome_ns.namespace("atc_mithermometer")
ATCMiThermometer = atc_mithermometer_ns.class_(
"ATCMiThermometer", esp32_ble_tracker.ESPBTDeviceListener, cg.Component
)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(ATCMiThermometer),
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1),
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 0),
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(UNIT_PERCENT, ICON_BATTERY, 0),
cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_BATTERY, 3),
}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(ATCMiThermometer),
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
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, 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):

View File

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

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
from esphome import automation
from esphome.components import climate, sensor
from esphome.const import CONF_AWAY_CONFIG, CONF_COOL_ACTION, \
CONF_DEFAULT_TARGET_TEMPERATURE_HIGH, CONF_DEFAULT_TARGET_TEMPERATURE_LOW, CONF_HEAT_ACTION, \
CONF_ID, CONF_IDLE_ACTION, CONF_SENSOR
from esphome.const import (
CONF_AWAY_CONFIG,
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')
BangBangClimate = bang_bang_ns.class_('BangBangClimate', climate.Climate, cg.Component)
BangBangClimateTargetTempConfig = bang_bang_ns.struct('BangBangClimateTargetTempConfig')
bang_bang_ns = cg.esphome_ns.namespace("bang_bang")
BangBangClimate = bang_bang_ns.class_("BangBangClimate", climate.Climate, cg.Component)
BangBangClimateTargetTempConfig = bang_bang_ns.struct("BangBangClimateTargetTempConfig")
CONFIG_SCHEMA = cv.All(climate.CLIMATE_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(BangBangClimate),
cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor),
cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature,
cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_HIGH): cv.temperature,
cv.Required(CONF_IDLE_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_COOL_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_HEAT_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_AWAY_CONFIG): cv.Schema({
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))
CONFIG_SCHEMA = cv.All(
climate.CLIMATE_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(BangBangClimate),
cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor),
cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_LOW): cv.temperature,
cv.Required(CONF_DEFAULT_TARGET_TEMPERATURE_HIGH): cv.temperature,
cv.Required(CONF_IDLE_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_COOL_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_HEAT_ACTION): automation.validate_automation(single=True),
cv.Optional(CONF_AWAY_CONFIG): cv.Schema(
{
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):
@ -35,23 +49,29 @@ def to_code(config):
normal_config = BangBangClimateTargetTempConfig(
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))
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:
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))
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))
if CONF_AWAY_CONFIG in config:
away = config[CONF_AWAY_CONFIG]
away_config = BangBangClimateTargetTempConfig(
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))

View File

@ -1,26 +1,45 @@
import esphome.codegen as cg
import esphome.config_validation as cv
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')
BH1750Resolution = bh1750_ns.enum('BH1750Resolution')
bh1750_ns = cg.esphome_ns.namespace("bh1750")
BH1750Resolution = bh1750_ns.enum("BH1750Resolution")
BH1750_RESOLUTIONS = {
4.0: BH1750Resolution.BH1750_RESOLUTION_4P0_LX,
1.0: BH1750Resolution.BH1750_RESOLUTION_1P0_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'
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_LUX, ICON_BRIGHTNESS_5, 1).extend({
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))
CONF_MEASUREMENT_TIME = "measurement_time"
CONFIG_SCHEMA = (
sensor.sensor_schema(UNIT_LUX, ICON_EMPTY, 1, DEVICE_CLASS_ILLUMINANCE)
.extend(
{
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):

View File

@ -1,3 +1,3 @@
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.config_validation as cv
from esphome.components import fan, output
from esphome.const import CONF_DIRECTION_OUTPUT, CONF_OSCILLATION_OUTPUT, \
CONF_OUTPUT, CONF_OUTPUT_ID
from esphome.const import (
CONF_DIRECTION_OUTPUT,
CONF_OSCILLATION_OUTPUT,
CONF_OUTPUT,
CONF_OUTPUT_ID,
)
from .. import binary_ns
BinaryFan = binary_ns.class_('BinaryFan', cg.Component)
BinaryFan = binary_ns.class_("BinaryFan", cg.Component)
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.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput),
cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput),
}).extend(cv.COMPONENT_SCHEMA)
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.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput),
cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput),
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config):

View File

@ -16,7 +16,7 @@ void binary::BinaryFan::dump_config() {
}
}
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_->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 .. import binary_ns
BinaryLightOutput = binary_ns.class_('BinaryLightOutput', light.LightOutput)
BinaryLightOutput = binary_ns.class_("BinaryLightOutput", light.LightOutput)
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),
})
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),
}
)
def to_code(config):

View File

@ -3,131 +3,214 @@ import esphome.config_validation as cv
from esphome import automation, core
from esphome.automation import Condition, maybe_simple_id
from esphome.components import mqtt
from esphome.const import CONF_DEVICE_CLASS, CONF_FILTERS, \
CONF_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
from esphome.const import (
CONF_DEVICE_CLASS,
CONF_FILTERS,
CONF_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.util import Registry
CODEOWNERS = ['@esphome/core']
CODEOWNERS = ["@esphome/core"]
DEVICE_CLASSES = [
'', 'battery', 'cold', 'connectivity', 'door', 'garage_door', 'gas',
'heat', 'light', 'lock', 'moisture', 'motion', 'moving', 'occupancy',
'opening', 'plug', 'power', 'presence', 'problem', 'safety', 'smoke',
'sound', 'vibration', 'window'
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,
]
IS_PLATFORM_COMPONENT = True
binary_sensor_ns = cg.esphome_ns.namespace('binary_sensor')
BinarySensor = binary_sensor_ns.class_('BinarySensor', cg.Nameable)
BinarySensorInitiallyOff = binary_sensor_ns.class_('BinarySensorInitiallyOff', BinarySensor)
BinarySensorPtr = BinarySensor.operator('ptr')
binary_sensor_ns = cg.esphome_ns.namespace("binary_sensor")
BinarySensor = binary_sensor_ns.class_("BinarySensor", cg.Nameable)
BinarySensorInitiallyOff = binary_sensor_ns.class_(
"BinarySensorInitiallyOff", BinarySensor
)
BinarySensorPtr = BinarySensor.operator("ptr")
# Triggers
PressTrigger = binary_sensor_ns.class_('PressTrigger', automation.Trigger.template())
ReleaseTrigger = binary_sensor_ns.class_('ReleaseTrigger', automation.Trigger.template())
ClickTrigger = binary_sensor_ns.class_('ClickTrigger', automation.Trigger.template())
DoubleClickTrigger = binary_sensor_ns.class_('DoubleClickTrigger', automation.Trigger.template())
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)
PressTrigger = binary_sensor_ns.class_("PressTrigger", automation.Trigger.template())
ReleaseTrigger = binary_sensor_ns.class_(
"ReleaseTrigger", automation.Trigger.template()
)
ClickTrigger = binary_sensor_ns.class_("ClickTrigger", automation.Trigger.template())
DoubleClickTrigger = binary_sensor_ns.class_(
"DoubleClickTrigger", automation.Trigger.template()
)
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
BinarySensorCondition = binary_sensor_ns.class_('BinarySensorCondition', Condition)
BinarySensorCondition = binary_sensor_ns.class_("BinarySensorCondition", Condition)
# Filters
Filter = binary_sensor_ns.class_('Filter')
DelayedOnOffFilter = binary_sensor_ns.class_('DelayedOnOffFilter', Filter, cg.Component)
DelayedOnFilter = binary_sensor_ns.class_('DelayedOnFilter', Filter, cg.Component)
DelayedOffFilter = binary_sensor_ns.class_('DelayedOffFilter', Filter, cg.Component)
InvertFilter = binary_sensor_ns.class_('InvertFilter', Filter)
LambdaFilter = binary_sensor_ns.class_('LambdaFilter', Filter)
Filter = binary_sensor_ns.class_("Filter")
DelayedOnOffFilter = binary_sensor_ns.class_("DelayedOnOffFilter", Filter, cg.Component)
DelayedOnFilter = binary_sensor_ns.class_("DelayedOnFilter", Filter, cg.Component)
DelayedOffFilter = binary_sensor_ns.class_("DelayedOffFilter", Filter, cg.Component)
InvertFilter = binary_sensor_ns.class_("InvertFilter", Filter)
LambdaFilter = binary_sensor_ns.class_("LambdaFilter", Filter)
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):
yield cg.new_Pvariable(filter_id)
@FILTER_REGISTRY.register('delayed_on_off', DelayedOnOffFilter,
cv.positive_time_period_milliseconds)
@FILTER_REGISTRY.register(
"delayed_on_off", DelayedOnOffFilter, cv.positive_time_period_milliseconds
)
def delayed_on_off_filter_to_code(config, filter_id):
var = cg.new_Pvariable(filter_id, config)
yield cg.register_component(var, {})
yield var
@FILTER_REGISTRY.register('delayed_on', DelayedOnFilter,
cv.positive_time_period_milliseconds)
@FILTER_REGISTRY.register(
"delayed_on", DelayedOnFilter, cv.positive_time_period_milliseconds
)
def delayed_on_filter_to_code(config, filter_id):
var = cg.new_Pvariable(filter_id, config)
yield cg.register_component(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):
var = cg.new_Pvariable(filter_id, config)
yield cg.register_component(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):
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_)
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_MAX_LENGTH): cv.positive_time_period_milliseconds,
})
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_MAX_LENGTH): cv.positive_time_period_milliseconds,
}
)
def parse_multi_click_timing_str(value):
if not isinstance(value, str):
return value
parts = value.lower().split(' ')
parts = value.lower().split(" ")
if len(parts) != 5:
raise cv.Invalid("Multi click timing grammar consists of exactly 5 words, not {}"
"".format(len(parts)))
raise cv.Invalid(
"Multi click timing grammar consists of exactly 5 words, not {}"
"".format(len(parts))
)
try:
state = cv.boolean(parts[0])
except cv.Invalid:
# pylint: disable=raise-missing-from
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]))
if parts[2] == 'at':
if parts[3] == 'least':
if parts[2] == "at":
if parts[3] == "least":
key = CONF_MIN_LENGTH
elif parts[3] == 'most':
elif parts[3] == "most":
key = CONF_MAX_LENGTH
else:
raise cv.Invalid("Third word after at must either be 'least' or 'most', got {}"
"".format(parts[3]))
raise cv.Invalid(
"Third word after at must either be 'least' or 'most', got {}"
"".format(parts[3])
)
try:
length = cv.positive_time_period_milliseconds(parts[4])
except cv.Invalid as err:
raise cv.Invalid(f"Multi Click Grammar Parsing length failed: {err}")
return {
CONF_STATE: state,
key: str(length)
}
return {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'")
try:
@ -143,7 +226,7 @@ def parse_multi_click_timing_str(value):
return {
CONF_STATE: state,
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)
if new_state == state:
raise cv.Invalid("Timings must have alternating state. Indices {} and {} have "
"the same state {}".format(i, i + 1, state))
raise cv.Invalid(
"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:
raise cv.Invalid("Max length ({}) must be larger than min length ({})."
"".format(max_length, min_length))
raise cv.Invalid(
"Max length ({}) must be larger than min length ({})."
"".format(max_length, min_length)
)
state = new_state
tim = {
@ -180,46 +267,71 @@ def validate_multi_click_timing(value):
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({
cv.GenerateID(): cv.declare_id(BinarySensor),
cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTBinarySensorComponent),
cv.Optional(CONF_DEVICE_CLASS): device_class,
cv.Optional(CONF_FILTERS): validate_filters,
cv.Optional(CONF_ON_PRESS): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PressTrigger),
}),
cv.Optional(CONF_ON_RELEASE): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReleaseTrigger),
}),
cv.Optional(CONF_ON_CLICK): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClickTrigger),
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.Optional(CONF_ON_DOUBLE_CLICK): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DoubleClickTrigger),
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.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."
),
})
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.Optional(CONF_DEVICE_CLASS): device_class,
cv.Optional(CONF_FILTERS): validate_filters,
cv.Optional(CONF_ON_PRESS): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(PressTrigger),
}
),
cv.Optional(CONF_ON_RELEASE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ReleaseTrigger),
}
),
cv.Optional(CONF_ON_CLICK): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ClickTrigger),
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.Optional(CONF_ON_DOUBLE_CLICK): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DoubleClickTrigger),
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.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
@ -244,24 +356,28 @@ def setup_binary_sensor_core_(var, config):
yield automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_CLICK, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var,
conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH])
trigger = cg.new_Pvariable(
conf[CONF_TRIGGER_ID], var, conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH]
)
yield automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_DOUBLE_CLICK, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var,
conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH])
trigger = cg.new_Pvariable(
conf[CONF_TRIGGER_ID], var, conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH]
)
yield automation.build_automation(trigger, [], conf)
for conf in config.get(CONF_ON_MULTI_CLICK, []):
timings = []
for tim in conf[CONF_TIMING]:
timings.append(cg.StructInitializer(
MultiClickTriggerEvent,
('state', tim[CONF_STATE]),
('min_length', tim[CONF_MIN_LENGTH]),
('max_length', tim.get(CONF_MAX_LENGTH, 4294967294)),
))
timings.append(
cg.StructInitializer(
MultiClickTriggerEvent,
("state", tim[CONF_STATE]),
("min_length", tim[CONF_MIN_LENGTH]),
("max_length", tim.get(CONF_MAX_LENGTH, 4294967294)),
)
)
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, timings)
if CONF_INVALID_COOLDOWN in conf:
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, []):
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:
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
@ -292,22 +408,28 @@ def new_binary_sensor(config):
yield var
BINARY_SENSOR_CONDITION_SCHEMA = maybe_simple_id({
cv.Required(CONF_ID): cv.use_id(BinarySensor),
cv.Optional(CONF_FOR): cv.invalid("This option has been removed in 1.13, please use the "
"'for' condition instead."),
})
BINARY_SENSOR_CONDITION_SCHEMA = maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(BinarySensor),
cv.Optional(CONF_FOR): cv.invalid(
"This option has been removed in 1.13, please use the "
"'for' condition instead."
),
}
)
@automation.register_condition('binary_sensor.is_on', BinarySensorCondition,
BINARY_SENSOR_CONDITION_SCHEMA)
@automation.register_condition(
"binary_sensor.is_on", BinarySensorCondition, BINARY_SENSOR_CONDITION_SCHEMA
)
def binary_sensor_is_on_to_code(config, condition_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(condition_id, template_arg, paren, True)
@automation.register_condition('binary_sensor.is_off', BinarySensorCondition,
BINARY_SENSOR_CONDITION_SCHEMA)
@automation.register_condition(
"binary_sensor.is_off", BinarySensorCondition, BINARY_SENSOR_CONDITION_SCHEMA
)
def binary_sensor_is_off_to_code(config, condition_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID])
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)
def to_code(config):
cg.add_define('USE_BINARY_SENSOR')
cg.add_define("USE_BINARY_SENSOR")
cg.add_global(binary_sensor_ns.using)

View File

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

View File

@ -3,18 +3,28 @@ import esphome.config_validation as cv
from esphome.components import binary_sensor, esp32_ble_tracker
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')
BLEPresenceDevice = ble_presence_ns.class_('BLEPresenceDevice', binary_sensor.BinarySensor,
cg.Component, esp32_ble_tracker.ESPBTDeviceListener)
ble_presence_ns = cg.esphome_ns.namespace("ble_presence")
BLEPresenceDevice = ble_presence_ns.class_(
"BLEPresenceDevice",
binary_sensor.BinarySensor,
cg.Component,
esp32_ble_tracker.ESPBTDeviceListener,
)
CONFIG_SCHEMA = cv.All(binary_sensor.BINARY_SENSOR_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(BLEPresenceDevice),
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))
CONFIG_SCHEMA = cv.All(
binary_sensor.BINARY_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(BLEPresenceDevice),
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):
@ -28,9 +38,17 @@ def to_code(config):
if CONF_SERVICE_UUID in config:
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):
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):
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_SERVICE_UUID])
cg.add(var.set_service_uuid128(uuid128))

View File

@ -1,20 +1,35 @@
import esphome.codegen as cg
import esphome.config_validation as cv
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')
BLERSSISensor = ble_rssi_ns.class_('BLERSSISensor', sensor.Sensor, cg.Component,
esp32_ble_tracker.ESPBTDeviceListener)
ble_rssi_ns = cg.esphome_ns.namespace("ble_rssi")
BLERSSISensor = ble_rssi_ns.class_(
"BLERSSISensor", sensor.Sensor, cg.Component, esp32_ble_tracker.ESPBTDeviceListener
)
CONFIG_SCHEMA = cv.All(sensor.sensor_schema(UNIT_DECIBEL, ICON_SIGNAL, 0).extend({
cv.GenerateID(): cv.declare_id(BLERSSISensor),
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))
CONFIG_SCHEMA = cv.All(
sensor.sensor_schema(UNIT_DECIBEL, ICON_EMPTY, 0, DEVICE_CLASS_SIGNAL_STRENGTH)
.extend(
{
cv.GenerateID(): cv.declare_id(BLERSSISensor),
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):
@ -28,9 +43,17 @@ def to_code(config):
if CONF_SERVICE_UUID in config:
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):
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):
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_SERVICE_UUID])
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.const import CONF_ID
DEPENDENCIES = ['esp32_ble_tracker']
DEPENDENCIES = ["esp32_ble_tracker"]
ble_scanner_ns = cg.esphome_ns.namespace('ble_scanner')
BLEScanner = ble_scanner_ns.class_('BLEScanner', text_sensor.TextSensor, cg.Component,
esp32_ble_tracker.ESPBTDeviceListener)
ble_scanner_ns = cg.esphome_ns.namespace("ble_scanner")
BLEScanner = ble_scanner_ns.class_(
"BLEScanner",
text_sensor.TextSensor,
cg.Component,
esp32_ble_tracker.ESPBTDeviceListener,
)
CONFIG_SCHEMA = cv.All(text_sensor.TEXT_SENSOR_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(BLEScanner),
}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(
cv.COMPONENT_SCHEMA))
CONFIG_SCHEMA = cv.All(
text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(BLEScanner),
}
)
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
.extend(cv.COMPONENT_SCHEMA)
)
def to_code(config):

View File

@ -1,53 +1,87 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_IIR_FILTER, CONF_OVERSAMPLING, \
CONF_PRESSURE, CONF_TEMPERATURE, ICON_THERMOMETER, \
UNIT_CELSIUS, UNIT_HECTOPASCAL, ICON_GAUGE, ICON_WATER_PERCENT, UNIT_PERCENT
from esphome.const import (
CONF_HUMIDITY,
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')
BME280Oversampling = bme280_ns.enum('BME280Oversampling')
bme280_ns = cg.esphome_ns.namespace("bme280")
BME280Oversampling = bme280_ns.enum("BME280Oversampling")
OVERSAMPLING_OPTIONS = {
'NONE': BME280Oversampling.BME280_OVERSAMPLING_NONE,
'1X': BME280Oversampling.BME280_OVERSAMPLING_1X,
'2X': BME280Oversampling.BME280_OVERSAMPLING_2X,
'4X': BME280Oversampling.BME280_OVERSAMPLING_4X,
'8X': BME280Oversampling.BME280_OVERSAMPLING_8X,
'16X': BME280Oversampling.BME280_OVERSAMPLING_16X,
"NONE": BME280Oversampling.BME280_OVERSAMPLING_NONE,
"1X": BME280Oversampling.BME280_OVERSAMPLING_1X,
"2X": BME280Oversampling.BME280_OVERSAMPLING_2X,
"4X": BME280Oversampling.BME280_OVERSAMPLING_4X,
"8X": BME280Oversampling.BME280_OVERSAMPLING_8X,
"16X": BME280Oversampling.BME280_OVERSAMPLING_16X,
}
BME280IIRFilter = bme280_ns.enum('BME280IIRFilter')
BME280IIRFilter = bme280_ns.enum("BME280IIRFilter")
IIR_FILTER_OPTIONS = {
'OFF': BME280IIRFilter.BME280_IIR_FILTER_OFF,
'2X': BME280IIRFilter.BME280_IIR_FILTER_2X,
'4X': BME280IIRFilter.BME280_IIR_FILTER_4X,
'8X': BME280IIRFilter.BME280_IIR_FILTER_8X,
'16X': BME280IIRFilter.BME280_IIR_FILTER_16X,
"OFF": BME280IIRFilter.BME280_IIR_FILTER_OFF,
"2X": BME280IIRFilter.BME280_IIR_FILTER_2X,
"4X": BME280IIRFilter.BME280_IIR_FILTER_4X,
"8X": BME280IIRFilter.BME280_IIR_FILTER_8X,
"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({
cv.GenerateID(): cv.declare_id(BME280Component),
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.Optional(CONF_PRESSURE):
sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1).extend({
cv.Optional(CONF_OVERSAMPLING, default='16X'):
cv.enum(OVERSAMPLING_OPTIONS, upper=True),
}),
cv.Optional(CONF_HUMIDITY):
sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1).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))
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(BME280Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
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_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):

View File

@ -2,64 +2,116 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import core
from esphome.components import i2c, sensor
from esphome.const import CONF_DURATION, CONF_GAS_RESISTANCE, CONF_HEATER, \
CONF_HUMIDITY, CONF_ID, CONF_IIR_FILTER, CONF_OVERSAMPLING, CONF_PRESSURE, \
CONF_TEMPERATURE, UNIT_OHM, ICON_GAS_CYLINDER, UNIT_CELSIUS, \
ICON_THERMOMETER, UNIT_HECTOPASCAL, ICON_GAUGE, ICON_WATER_PERCENT, UNIT_PERCENT
from esphome.const import (
CONF_DURATION,
CONF_GAS_RESISTANCE,
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')
BME680Oversampling = bme680_ns.enum('BME680Oversampling')
bme680_ns = cg.esphome_ns.namespace("bme680")
BME680Oversampling = bme680_ns.enum("BME680Oversampling")
OVERSAMPLING_OPTIONS = {
'NONE': BME680Oversampling.BME680_OVERSAMPLING_NONE,
'1X': BME680Oversampling.BME680_OVERSAMPLING_1X,
'2X': BME680Oversampling.BME680_OVERSAMPLING_2X,
'4X': BME680Oversampling.BME680_OVERSAMPLING_4X,
'8X': BME680Oversampling.BME680_OVERSAMPLING_8X,
'16X': BME680Oversampling.BME680_OVERSAMPLING_16X,
"NONE": BME680Oversampling.BME680_OVERSAMPLING_NONE,
"1X": BME680Oversampling.BME680_OVERSAMPLING_1X,
"2X": BME680Oversampling.BME680_OVERSAMPLING_2X,
"4X": BME680Oversampling.BME680_OVERSAMPLING_4X,
"8X": BME680Oversampling.BME680_OVERSAMPLING_8X,
"16X": BME680Oversampling.BME680_OVERSAMPLING_16X,
}
BME680IIRFilter = bme680_ns.enum('BME680IIRFilter')
BME680IIRFilter = bme680_ns.enum("BME680IIRFilter")
IIR_FILTER_OPTIONS = {
'OFF': BME680IIRFilter.BME680_IIR_FILTER_OFF,
'1X': BME680IIRFilter.BME680_IIR_FILTER_1X,
'3X': BME680IIRFilter.BME680_IIR_FILTER_3X,
'7X': BME680IIRFilter.BME680_IIR_FILTER_7X,
'15X': BME680IIRFilter.BME680_IIR_FILTER_15X,
'31X': BME680IIRFilter.BME680_IIR_FILTER_31X,
'63X': BME680IIRFilter.BME680_IIR_FILTER_63X,
'127X': BME680IIRFilter.BME680_IIR_FILTER_127X,
"OFF": BME680IIRFilter.BME680_IIR_FILTER_OFF,
"1X": BME680IIRFilter.BME680_IIR_FILTER_1X,
"3X": BME680IIRFilter.BME680_IIR_FILTER_3X,
"7X": BME680IIRFilter.BME680_IIR_FILTER_7X,
"15X": BME680IIRFilter.BME680_IIR_FILTER_15X,
"31X": BME680IIRFilter.BME680_IIR_FILTER_31X,
"63X": BME680IIRFilter.BME680_IIR_FILTER_63X,
"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({
cv.GenerateID(): cv.declare_id(BME680Component),
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.Optional(CONF_PRESSURE):
sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1).extend({
cv.Optional(CONF_OVERSAMPLING, default='16X'):
cv.enum(OVERSAMPLING_OPTIONS, upper=True),
}),
cv.Optional(CONF_HUMIDITY):
sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1).extend({
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),
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))
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(BME680Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
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_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_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):

View File

@ -1,19 +1,39 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import CONF_ID, CONF_PRESSURE, CONF_TEMPERATURE, \
UNIT_CELSIUS, ICON_THERMOMETER, ICON_GAUGE, UNIT_HECTOPASCAL
from esphome.const import (
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')
BMP085Component = bmp085_ns.class_('BMP085Component', cg.PollingComponent, i2c.I2CDevice)
bmp085_ns = cg.esphome_ns.namespace("bmp085")
BMP085Component = bmp085_ns.class_(
"BMP085Component", cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(BMP085Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1),
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1),
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x77))
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(BMP085Component),
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):

View File

@ -1,44 +1,75 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import CONF_ID, CONF_PRESSURE, CONF_TEMPERATURE, \
UNIT_CELSIUS, ICON_THERMOMETER, ICON_GAUGE, UNIT_HECTOPASCAL, \
CONF_IIR_FILTER, CONF_OVERSAMPLING
from esphome.const import (
CONF_ID,
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')
BMP280Oversampling = bmp280_ns.enum('BMP280Oversampling')
bmp280_ns = cg.esphome_ns.namespace("bmp280")
BMP280Oversampling = bmp280_ns.enum("BMP280Oversampling")
OVERSAMPLING_OPTIONS = {
'NONE': BMP280Oversampling.BMP280_OVERSAMPLING_NONE,
'1X': BMP280Oversampling.BMP280_OVERSAMPLING_1X,
'2X': BMP280Oversampling.BMP280_OVERSAMPLING_2X,
'4X': BMP280Oversampling.BMP280_OVERSAMPLING_4X,
'8X': BMP280Oversampling.BMP280_OVERSAMPLING_8X,
'16X': BMP280Oversampling.BMP280_OVERSAMPLING_16X,
"NONE": BMP280Oversampling.BMP280_OVERSAMPLING_NONE,
"1X": BMP280Oversampling.BMP280_OVERSAMPLING_1X,
"2X": BMP280Oversampling.BMP280_OVERSAMPLING_2X,
"4X": BMP280Oversampling.BMP280_OVERSAMPLING_4X,
"8X": BMP280Oversampling.BMP280_OVERSAMPLING_8X,
"16X": BMP280Oversampling.BMP280_OVERSAMPLING_16X,
}
BMP280IIRFilter = bmp280_ns.enum('BMP280IIRFilter')
BMP280IIRFilter = bmp280_ns.enum("BMP280IIRFilter")
IIR_FILTER_OPTIONS = {
'OFF': BMP280IIRFilter.BMP280_IIR_FILTER_OFF,
'2X': BMP280IIRFilter.BMP280_IIR_FILTER_2X,
'4X': BMP280IIRFilter.BMP280_IIR_FILTER_4X,
'8X': BMP280IIRFilter.BMP280_IIR_FILTER_8X,
'16X': BMP280IIRFilter.BMP280_IIR_FILTER_16X,
"OFF": BMP280IIRFilter.BMP280_IIR_FILTER_OFF,
"2X": BMP280IIRFilter.BMP280_IIR_FILTER_2X,
"4X": BMP280IIRFilter.BMP280_IIR_FILTER_4X,
"8X": BMP280IIRFilter.BMP280_IIR_FILTER_8X,
"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({
cv.GenerateID(): cv.declare_id(BMP280Component),
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.Optional(CONF_PRESSURE): sensor.sensor_schema(UNIT_HECTOPASCAL, ICON_GAUGE, 1).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))
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(BMP280Component),
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
).extend(
{
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
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):

View File

@ -4,68 +4,82 @@ from esphome import automation
from esphome.core import CORE, coroutine
from esphome.const import CONF_ID, CONF_TRIGGER_ID, CONF_DATA
CODEOWNERS = ['@mvturnho', '@danielschramm']
CODEOWNERS = ["@mvturnho", "@danielschramm"]
IS_PLATFORM_COMPONENT = True
CONF_CAN_ID = 'can_id'
CONF_USE_EXTENDED_ID = 'use_extended_id'
CONF_CANBUS_ID = 'canbus_id'
CONF_BIT_RATE = 'bit_rate'
CONF_ON_FRAME = 'on_frame'
CONF_CANBUS_SEND = 'canbus.send'
CONF_CAN_ID = "can_id"
CONF_USE_EXTENDED_ID = "use_extended_id"
CONF_CANBUS_ID = "canbus_id"
CONF_BIT_RATE = "bit_rate"
CONF_ON_FRAME = "on_frame"
def validate_id(id_value, 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)")
def validate_raw_data(value):
if isinstance(value, str):
return value.encode('utf-8')
return value.encode("utf-8")
if isinstance(value, list):
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')
CanbusComponent = canbus_ns.class_('CanbusComponent', cg.Component)
CanbusTrigger = canbus_ns.class_('CanbusTrigger',
automation.Trigger.template(cg.std_vector.template(cg.uint8)),
cg.Component)
CanSpeed = canbus_ns.enum('CAN_SPEED')
canbus_ns = cg.esphome_ns.namespace("canbus")
CanbusComponent = canbus_ns.class_("CanbusComponent", cg.Component)
CanbusTrigger = canbus_ns.class_(
"CanbusTrigger",
automation.Trigger.template(cg.std_vector.template(cg.uint8)),
cg.Component,
)
CanSpeed = canbus_ns.enum("CAN_SPEED")
CAN_SPEEDS = {
'5KBPS': CanSpeed.CAN_5KBPS,
'10KBPS': CanSpeed.CAN_10KBPS,
'20KBPS': CanSpeed.CAN_20KBPS,
'31K25BPS': CanSpeed.CAN_31K25BPS,
'33KBPS': CanSpeed.CAN_33KBPS,
'40KBPS': CanSpeed.CAN_40KBPS,
'50KBPS': CanSpeed.CAN_50KBPS,
'80KBPS': CanSpeed.CAN_80KBPS,
'83K3BPS': CanSpeed.CAN_83K3BPS,
'95KBPS': CanSpeed.CAN_95KBPS,
'100KBPS': CanSpeed.CAN_100KBPS,
'125KBPS': CanSpeed.CAN_125KBPS,
'200KBPS': CanSpeed.CAN_200KBPS,
'250KBPS': CanSpeed.CAN_250KBPS,
'500KBPS': CanSpeed.CAN_500KBPS,
'1000KBPS': CanSpeed.CAN_1000KBPS,
"5KBPS": CanSpeed.CAN_5KBPS,
"10KBPS": CanSpeed.CAN_10KBPS,
"20KBPS": CanSpeed.CAN_20KBPS,
"31K25BPS": CanSpeed.CAN_31K25BPS,
"33KBPS": CanSpeed.CAN_33KBPS,
"40KBPS": CanSpeed.CAN_40KBPS,
"50KBPS": CanSpeed.CAN_50KBPS,
"80KBPS": CanSpeed.CAN_80KBPS,
"83K3BPS": CanSpeed.CAN_83K3BPS,
"95KBPS": CanSpeed.CAN_95KBPS,
"100KBPS": CanSpeed.CAN_100KBPS,
"125KBPS": CanSpeed.CAN_125KBPS,
"200KBPS": CanSpeed.CAN_200KBPS,
"250KBPS": CanSpeed.CAN_250KBPS,
"500KBPS": CanSpeed.CAN_500KBPS,
"1000KBPS": CanSpeed.CAN_1000KBPS,
}
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(CanbusComponent),
cv.Required(CONF_CAN_ID): cv.int_range(min=0, max=0x1fffffff),
cv.Optional(CONF_BIT_RATE, default='125KBPS'): cv.enum(CAN_SPEEDS, upper=True),
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),
CANBUS_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CanbusComponent),
cv.Required(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF),
cv.Optional(CONF_BIT_RATE, default="125KBPS"): cv.enum(CAN_SPEEDS, upper=True),
cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean,
}),
}).extend(cv.COMPONENT_SCHEMA)
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_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
@ -82,7 +96,9 @@ def setup_canbus_core_(var, config):
validate_id(can_id, ext_id)
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, can_id, ext_id)
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
@ -93,14 +109,19 @@ def register_canbus(var, config):
# Actions
@automation.register_action(CONF_CANBUS_SEND,
canbus_ns.class_('CanbusSendAction', automation.Action),
cv.maybe_simple_value({
cv.GenerateID(CONF_CANBUS_ID): cv.use_id(CanbusComponent),
cv.Optional(CONF_CAN_ID): cv.int_range(min=0, max=0x1fffffff),
cv.Optional(CONF_USE_EXTENDED_ID, default=False): cv.boolean,
cv.Required(CONF_DATA): cv.templatable(validate_raw_data),
}, key=CONF_DATA))
@automation.register_action(
"canbus.send",
canbus_ns.class_("CanbusSendAction", automation.Action),
cv.maybe_simple_value(
{
cv.GenerateID(CONF_CANBUS_ID): cv.use_id(CanbusComponent),
cv.Optional(CONF_CAN_ID): cv.int_range(min=0, max=0x1FFFFFFF),
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):
validate_id(config[CONF_CAN_ID], config[CONF_USE_EXTENDED_ID])
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)
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))
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.core import coroutine_with_priority
AUTO_LOAD = ['web_server_base']
DEPENDENCIES = ['wifi']
CODEOWNERS = ['@OttoWinter']
AUTO_LOAD = ["web_server_base"]
DEPENDENCIES = ["wifi"]
CODEOWNERS = ["@OttoWinter"]
captive_portal_ns = cg.esphome_ns.namespace('captive_portal')
CaptivePortal = captive_portal_ns.class_('CaptivePortal', cg.Component)
captive_portal_ns = cg.esphome_ns.namespace("captive_portal")
CaptivePortal = captive_portal_ns.class_("CaptivePortal", cg.Component)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(CaptivePortal),
cv.GenerateID(CONF_WEB_SERVER_BASE_ID): cv.use_id(web_server_base.WebServerBase),
}).extend(cv.COMPONENT_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
),
}
).extend(cv.COMPONENT_SCHEMA)
@coroutine_with_priority(64.0)
@ -24,4 +28,4 @@ def to_code(config):
var = cg.new_Pvariable(config[CONF_ID], paren)
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.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import CONF_ID, ICON_RADIATOR, UNIT_PARTS_PER_MILLION, \
UNIT_PARTS_PER_BILLION, CONF_TEMPERATURE, CONF_HUMIDITY, ICON_MOLECULE_CO2
from esphome.const import (
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')
CCS811Component = ccs811_ns.class_('CCS811Component', cg.PollingComponent, i2c.I2CDevice)
ccs811_ns = cg.esphome_ns.namespace("ccs811")
CCS811Component = ccs811_ns.class_(
"CCS811Component", cg.PollingComponent, i2c.I2CDevice
)
CONF_ECO2 = 'eco2'
CONF_TVOC = 'tvoc'
CONF_BASELINE = 'baseline'
CONF_ECO2 = "eco2"
CONF_BASELINE = "baseline"
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(CCS811Component),
cv.Required(CONF_ECO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2,
0),
cv.Required(CONF_TVOC): sensor.sensor_schema(UNIT_PARTS_PER_BILLION, ICON_RADIATOR, 0),
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))
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(CCS811Component),
cv.Required(CONF_ECO2): sensor.sensor_schema(
UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0, DEVICE_CLASS_EMPTY
),
cv.Required(CONF_TVOC): sensor.sensor_schema(
UNIT_PARTS_PER_BILLION, ICON_RADIATOR, 0, DEVICE_CLASS_EMPTY
),
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):

View File

@ -2,70 +2,87 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.components import mqtt
from esphome.const import CONF_AWAY, CONF_ID, 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.const import (
CONF_AWAY,
CONF_ID,
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
IS_PLATFORM_COMPONENT = True
CODEOWNERS = ['@esphome/core']
climate_ns = cg.esphome_ns.namespace('climate')
CODEOWNERS = ["@esphome/core"]
climate_ns = cg.esphome_ns.namespace("climate")
Climate = climate_ns.class_('Climate', cg.Nameable)
ClimateCall = climate_ns.class_('ClimateCall')
ClimateTraits = climate_ns.class_('ClimateTraits')
Climate = climate_ns.class_("Climate", cg.Nameable)
ClimateCall = climate_ns.class_("ClimateCall")
ClimateTraits = climate_ns.class_("ClimateTraits")
ClimateMode = climate_ns.enum('ClimateMode')
ClimateMode = climate_ns.enum("ClimateMode")
CLIMATE_MODES = {
'OFF': ClimateMode.CLIMATE_MODE_OFF,
'AUTO': ClimateMode.CLIMATE_MODE_AUTO,
'COOL': ClimateMode.CLIMATE_MODE_COOL,
'HEAT': ClimateMode.CLIMATE_MODE_HEAT,
'DRY': ClimateMode.CLIMATE_MODE_DRY,
'FAN_ONLY': ClimateMode.CLIMATE_MODE_FAN_ONLY,
"OFF": ClimateMode.CLIMATE_MODE_OFF,
"AUTO": ClimateMode.CLIMATE_MODE_AUTO,
"COOL": ClimateMode.CLIMATE_MODE_COOL,
"HEAT": ClimateMode.CLIMATE_MODE_HEAT,
"DRY": ClimateMode.CLIMATE_MODE_DRY,
"FAN_ONLY": ClimateMode.CLIMATE_MODE_FAN_ONLY,
}
validate_climate_mode = cv.enum(CLIMATE_MODES, upper=True)
ClimateFanMode = climate_ns.enum('ClimateFanMode')
ClimateFanMode = climate_ns.enum("ClimateFanMode")
CLIMATE_FAN_MODES = {
'ON': ClimateFanMode.CLIMATE_FAN_ON,
'OFF': ClimateFanMode.CLIMATE_FAN_OFF,
'AUTO': ClimateFanMode.CLIMATE_FAN_AUTO,
'LOW': ClimateFanMode.CLIMATE_FAN_LOW,
'MEDIUM': ClimateFanMode.CLIMATE_FAN_MEDIUM,
'HIGH': ClimateFanMode.CLIMATE_FAN_HIGH,
'MIDDLE': ClimateFanMode.CLIMATE_FAN_MIDDLE,
'FOCUS': ClimateFanMode.CLIMATE_FAN_FOCUS,
'DIFFUSE': ClimateFanMode.CLIMATE_FAN_DIFFUSE,
"ON": ClimateFanMode.CLIMATE_FAN_ON,
"OFF": ClimateFanMode.CLIMATE_FAN_OFF,
"AUTO": ClimateFanMode.CLIMATE_FAN_AUTO,
"LOW": ClimateFanMode.CLIMATE_FAN_LOW,
"MEDIUM": ClimateFanMode.CLIMATE_FAN_MEDIUM,
"HIGH": ClimateFanMode.CLIMATE_FAN_HIGH,
"MIDDLE": ClimateFanMode.CLIMATE_FAN_MIDDLE,
"FOCUS": ClimateFanMode.CLIMATE_FAN_FOCUS,
"DIFFUSE": ClimateFanMode.CLIMATE_FAN_DIFFUSE,
}
validate_climate_fan_mode = cv.enum(CLIMATE_FAN_MODES, upper=True)
ClimateSwingMode = climate_ns.enum('ClimateSwingMode')
ClimateSwingMode = climate_ns.enum("ClimateSwingMode")
CLIMATE_SWING_MODES = {
'OFF': ClimateSwingMode.CLIMATE_SWING_OFF,
'BOTH': ClimateSwingMode.CLIMATE_SWING_BOTH,
'VERTICAL': ClimateSwingMode.CLIMATE_SWING_VERTICAL,
'HORIZONTAL': ClimateSwingMode.CLIMATE_SWING_HORIZONTAL,
"OFF": ClimateSwingMode.CLIMATE_SWING_OFF,
"BOTH": ClimateSwingMode.CLIMATE_SWING_BOTH,
"VERTICAL": ClimateSwingMode.CLIMATE_SWING_VERTICAL,
"HORIZONTAL": ClimateSwingMode.CLIMATE_SWING_HORIZONTAL,
}
validate_climate_swing_mode = cv.enum(CLIMATE_SWING_MODES, upper=True)
# Actions
ControlAction = climate_ns.class_('ControlAction', automation.Action)
ControlAction = climate_ns.class_("ControlAction", automation.Action)
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.Optional(CONF_VISUAL, default={}): cv.Schema({
cv.Optional(CONF_MIN_TEMPERATURE): cv.temperature,
cv.Optional(CONF_MAX_TEMPERATURE): cv.temperature,
cv.Optional(CONF_TEMPERATURE_STEP): cv.temperature,
}),
# TODO: MQTT topic options
})
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.Optional(CONF_VISUAL, default={}): cv.Schema(
{
cv.Optional(CONF_MIN_TEMPERATURE): cv.temperature,
cv.Optional(CONF_MAX_TEMPERATURE): cv.temperature,
cv.Optional(CONF_TEMPERATURE_STEP): cv.temperature,
}
),
# TODO: MQTT topic options
}
)
@coroutine
@ -94,19 +111,23 @@ def register_climate(var, config):
yield setup_climate_core_(var, config)
CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema({
cv.Required(CONF_ID): cv.use_id(Climate),
cv.Optional(CONF_MODE): cv.templatable(validate_climate_mode),
cv.Optional(CONF_TARGET_TEMPERATURE): cv.templatable(cv.temperature),
cv.Optional(CONF_TARGET_TEMPERATURE_LOW): cv.templatable(cv.temperature),
cv.Optional(CONF_TARGET_TEMPERATURE_HIGH): cv.templatable(cv.temperature),
cv.Optional(CONF_AWAY): cv.templatable(cv.boolean),
cv.Optional(CONF_FAN_MODE): cv.templatable(validate_climate_fan_mode),
cv.Optional(CONF_SWING_MODE): cv.templatable(validate_climate_swing_mode),
})
CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(Climate),
cv.Optional(CONF_MODE): cv.templatable(validate_climate_mode),
cv.Optional(CONF_TARGET_TEMPERATURE): cv.templatable(cv.temperature),
cv.Optional(CONF_TARGET_TEMPERATURE_LOW): cv.templatable(cv.temperature),
cv.Optional(CONF_TARGET_TEMPERATURE_HIGH): cv.templatable(cv.temperature),
cv.Optional(CONF_AWAY): cv.templatable(cv.boolean),
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):
paren = yield cg.get_variable(config[CONF_ID])
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)
cg.add(var.set_target_temperature(template_))
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_))
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_))
if CONF_AWAY in config:
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)
cg.add(var.set_fan_mode(template_))
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_))
yield var
@coroutine_with_priority(100.0)
def to_code(config):
cg.add_define('USE_CLIMATE')
cg.add_define("USE_CLIMATE")
cg.add_global(climate_ns.using)

View File

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

View File

@ -3,16 +3,45 @@ import esphome.config_validation as cv
from esphome.components import climate_ir
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')
LgIrClimate = climate_ir_lg_ns.class_('LgIrClimate', climate_ir.ClimateIR)
climate_ir_lg_ns = cg.esphome_ns.namespace("climate_ir_lg")
LgIrClimate = climate_ir_lg_ns.class_("LgIrClimate", climate_ir.ClimateIR)
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(LgIrClimate),
})
CONF_HEADER_HIGH = "header_high"
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):
var = cg.new_Pvariable(config[CONF_ID])
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_AI = 0x03000;
const uint32_t COMMAND_COOL = 0x08000;
const uint32_t COMMAND_HEAT = 0x0C000;
const uint32_t COMMAND_OFF = 0xC0000;
const uint32_t COMMAND_SWING = 0x10000;
// 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_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;
void LgIrClimate::transmit_state() {
@ -55,6 +49,9 @@ void LgIrClimate::transmit_state() {
case climate::CLIMATE_MODE_COOL:
remote_state |= COMMAND_COOL;
break;
case climate::CLIMATE_MODE_HEAT:
remote_state |= COMMAND_HEAT;
break;
case climate::CLIMATE_MODE_AUTO:
remote_state |= COMMAND_AUTO;
break;
@ -73,7 +70,8 @@ void LgIrClimate::transmit_state() {
if (this->mode == climate::CLIMATE_MODE_OFF) {
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) {
case climate::CLIMATE_FAN_HIGH:
remote_state |= FAN_MAX;
@ -95,7 +93,7 @@ void LgIrClimate::transmit_state() {
this->fan_mode = climate::CLIMATE_FAN_AUTO;
// 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));
remote_state |= ((temp - 15) << TEMP_SHIFT);
}
@ -108,13 +106,13 @@ bool LgIrClimate::on_receive(remote_base::RemoteReceiveData data) {
uint8_t nbits = 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;
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;
} 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;
} else if (nbits == BITS) {
break;
@ -141,29 +139,32 @@ bool LgIrClimate::on_receive(remote_base::RemoteReceiveData data) {
} else {
if ((remote_state & COMMAND_MASK) == COMMAND_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;
else if ((remote_state & COMMAND_MASK) == COMMAND_HEAT) {
this->mode = climate::CLIMATE_MODE_HEAT;
} else {
this->mode = climate::CLIMATE_MODE_COOL;
}
}
// Temperature
if (this->mode == climate::CLIMATE_MODE_COOL)
this->target_temperature = ((remote_state & TEMP_MASK) >> TEMP_SHIFT) + 15;
// Temperature
if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_HEAT)
this->target_temperature = ((remote_state & TEMP_MASK) >> TEMP_SHIFT) + 15;
// Fan Speed
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)
// Fan Speed
if (this->mode == climate::CLIMATE_MODE_AUTO) {
this->fan_mode = climate::CLIMATE_FAN_AUTO;
else if ((remote_state & FAN_MASK) == FAN_MIN)
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;
} else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_HEAT ||
this->mode == climate::CLIMATE_MODE_DRY) {
if ((remote_state & FAN_MASK) == FAN_AUTO)
this->fan_mode = climate::CLIMATE_FAN_AUTO;
else if ((remote_state & FAN_MASK) == FAN_MIN)
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();
@ -179,15 +180,16 @@ void LgIrClimate::transmit_(uint32_t value) {
data->set_carrier_frequency(38000);
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) {
if (value & mask)
data->item(BIT_HIGH_US, BIT_ONE_LOW_US);
else
data->item(BIT_HIGH_US, BIT_ZERO_LOW_US);
if (value & mask) {
data->item(this->bit_high_, this->bit_one_low_);
} else {
data->item(this->bit_high_, this->bit_zero_low_);
}
}
data->mark(BIT_HIGH_US);
data->mark(this->bit_high_);
transmit.perform();
}
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;
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:
/// 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 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};
};

View File

@ -2,22 +2,56 @@ from esphome import config_validation as cv
from esphome import codegen as cg
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
CONFIG_SCHEMA = cv.Schema({
cv.Required(CONF_ID): cv.declare_id(ColorStruct),
cv.Optional(CONF_RED, default=0.0): cv.percentage,
cv.Optional(CONF_GREEN, default=0.0): cv.percentage,
cv.Optional(CONF_BLUE, default=0.0): cv.percentage,
cv.Optional(CONF_WHITE, default=0.0): cv.percentage,
}).extend(cv.COMPONENT_SCHEMA)
CONF_RED_INT = "red_int"
CONF_GREEN_INT = "green_int"
CONF_BLUE_INT = "blue_int"
CONF_WHITE_INT = "white_int"
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):
cg.variable(config[CONF_ID], cg.StructInitializer(
ColorStruct,
('r', config[CONF_RED]),
('g', config[CONF_GREEN]),
('b', config[CONF_BLUE]),
('w', config[CONF_WHITE])))
r = 0
if CONF_RED in config:
r = int(config[CONF_RED] * 255)
elif CONF_RED_INT in config:
r = config[CONF_RED_INT]
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.const import CONF_ID
AUTO_LOAD = ['climate_ir']
CODEOWNERS = ['@glmnet']
AUTO_LOAD = ["climate_ir"]
CODEOWNERS = ["@glmnet"]
coolix_ns = cg.esphome_ns.namespace('coolix')
CoolixClimate = coolix_ns.class_('CoolixClimate', climate_ir.ClimateIR)
coolix_ns = cg.esphome_ns.namespace("coolix")
CoolixClimate = coolix_ns.class_("CoolixClimate", climate_ir.ClimateIR)
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(CoolixClimate),
})
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(CoolixClimate),
}
)
def to_code(config):

View File

@ -3,54 +3,74 @@ import esphome.config_validation as cv
from esphome import automation
from esphome.automation import maybe_simple_id, Condition
from esphome.components import mqtt
from esphome.const import CONF_ID, CONF_INTERNAL, CONF_DEVICE_CLASS, CONF_STATE, \
CONF_POSITION, CONF_TILT, CONF_STOP, CONF_MQTT_ID, CONF_NAME
from esphome.const import (
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
IS_PLATFORM_COMPONENT = True
CODEOWNERS = ['@esphome/core']
CODEOWNERS = ["@esphome/core"]
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_CLOSED = cover_ns.COVER_CLOSED
COVER_STATES = {
'OPEN': COVER_OPEN,
'CLOSED': COVER_CLOSED,
"OPEN": COVER_OPEN,
"CLOSED": COVER_CLOSED,
}
validate_cover_state = cv.enum(COVER_STATES, upper=True)
CoverOperation = cover_ns.enum('CoverOperation')
CoverOperation = cover_ns.enum("CoverOperation")
COVER_OPERATIONS = {
'IDLE': CoverOperation.COVER_OPERATION_IDLE,
'OPENING': CoverOperation.COVER_OPERATION_OPENING,
'CLOSING': CoverOperation.COVER_OPERATION_CLOSING,
"IDLE": CoverOperation.COVER_OPERATION_IDLE,
"OPENING": CoverOperation.COVER_OPERATION_OPENING,
"CLOSING": CoverOperation.COVER_OPERATION_CLOSING,
}
validate_cover_operation = cv.enum(COVER_OPERATIONS, upper=True)
# Actions
OpenAction = cover_ns.class_('OpenAction', automation.Action)
CloseAction = cover_ns.class_('CloseAction', automation.Action)
StopAction = cover_ns.class_('StopAction', automation.Action)
ControlAction = cover_ns.class_('ControlAction', automation.Action)
CoverPublishAction = cover_ns.class_('CoverPublishAction', automation.Action)
CoverIsOpenCondition = cover_ns.class_('CoverIsOpenCondition', Condition)
CoverIsClosedCondition = cover_ns.class_('CoverIsClosedCondition', Condition)
OpenAction = cover_ns.class_("OpenAction", automation.Action)
CloseAction = cover_ns.class_("CloseAction", automation.Action)
StopAction = cover_ns.class_("StopAction", automation.Action)
ControlAction = cover_ns.class_("ControlAction", automation.Action)
CoverPublishAction = cover_ns.class_("CoverPublishAction", automation.Action)
CoverIsOpenCondition = cover_ns.class_("CoverIsOpenCondition", Condition)
CoverIsClosedCondition = cover_ns.class_("CoverIsClosedCondition", Condition)
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.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True),
# TODO: MQTT topic options
})
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.Optional(CONF_DEVICE_CLASS): cv.one_of(*DEVICE_CLASSES, lower=True),
# TODO: MQTT topic options
}
)
@coroutine
@ -74,39 +94,43 @@ def register_cover(var, config):
yield setup_cover_core_(var, config)
COVER_ACTION_SCHEMA = maybe_simple_id({
cv.Required(CONF_ID): cv.use_id(Cover),
})
COVER_ACTION_SCHEMA = maybe_simple_id(
{
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):
paren = yield cg.get_variable(config[CONF_ID])
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):
paren = yield cg.get_variable(config[CONF_ID])
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):
paren = yield cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(action_id, template_arg, paren)
COVER_CONTROL_ACTION_SCHEMA = cv.Schema({
cv.Required(CONF_ID): cv.use_id(Cover),
cv.Optional(CONF_STOP): cv.templatable(cv.boolean),
cv.Exclusive(CONF_STATE, 'pos'): cv.templatable(validate_cover_state),
cv.Exclusive(CONF_POSITION, 'pos'): cv.templatable(cv.percentage),
cv.Optional(CONF_TILT): cv.templatable(cv.percentage),
})
COVER_CONTROL_ACTION_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(Cover),
cv.Optional(CONF_STOP): cv.templatable(cv.boolean),
cv.Exclusive(CONF_STATE, "pos"): cv.templatable(validate_cover_state),
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):
paren = yield cg.get_variable(config[CONF_ID])
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)
def to_code(config):
cg.add_define('USE_COVER')
cg.add_define("USE_COVER")
cg.add_global(cover_ns.using)

View File

@ -1,21 +1,45 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, uart
from esphome.const import CONF_CURRENT, CONF_ID, CONF_POWER, CONF_VOLTAGE, \
UNIT_VOLT, ICON_FLASH, UNIT_AMPERE, UNIT_WATT
from esphome.const import (
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')
CSE7766Component = cse7766_ns.class_('CSE7766Component', cg.PollingComponent, uart.UARTDevice)
cse7766_ns = cg.esphome_ns.namespace("cse7766")
CSE7766Component = cse7766_ns.class_(
"CSE7766Component", cg.PollingComponent, uart.UARTDevice
)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(CSE7766Component),
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1),
cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2),
cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 1),
}).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(CSE7766Component),
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE
),
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):

View File

@ -1,21 +1,35 @@
import esphome.codegen as cg
import esphome.config_validation as cv
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']
CODEOWNERS = ['@jesserockz']
AUTO_LOAD = ["voltage_sampler"]
CODEOWNERS = ["@jesserockz"]
CONF_SAMPLE_DURATION = 'sample_duration'
CONF_SAMPLE_DURATION = "sample_duration"
ct_clamp_ns = cg.esphome_ns.namespace('ct_clamp')
CTClampSensor = ct_clamp_ns.class_('CTClampSensor', sensor.Sensor, cg.PollingComponent)
ct_clamp_ns = cg.esphome_ns.namespace("ct_clamp")
CTClampSensor = ct_clamp_ns.class_("CTClampSensor", sensor.Sensor, cg.PollingComponent)
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2).extend({
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'))
CONFIG_SCHEMA = (
sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT)
.extend(
{
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):

View File

@ -1,3 +1,3 @@
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 .. import custom_ns
CustomBinarySensorConstructor = custom_ns.class_('CustomBinarySensorConstructor')
CustomBinarySensorConstructor = custom_ns.class_("CustomBinarySensorConstructor")
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(CustomBinarySensorConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_BINARY_SENSORS): cv.ensure_list(binary_sensor.BINARY_SENSOR_SCHEMA),
})
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomBinarySensorConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_BINARY_SENSORS): cv.ensure_list(
binary_sensor.BINARY_SENSOR_SCHEMA
),
}
)
def to_code(config):
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_)
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 .. import custom_ns
CustomClimateConstructor = custom_ns.class_('CustomClimateConstructor')
CONF_CLIMATES = 'climates'
CustomClimateConstructor = custom_ns.class_("CustomClimateConstructor")
CONF_CLIMATES = "climates"
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(CustomClimateConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_CLIMATES): cv.ensure_list(climate.CLIMATE_SCHEMA),
})
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomClimateConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_CLIMATES): cv.ensure_list(climate.CLIMATE_SCHEMA),
}
)
def to_code(config):
template_ = yield cg.process_lambda(
config[CONF_LAMBDA], [],
return_type=cg.std_vector.template(climate.Climate.operator('ptr')))
config[CONF_LAMBDA],
[],
return_type=cg.std_vector.template(climate.Climate.operator("ptr")),
)
rhs = CustomClimateConstructor(template_)
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 .. import custom_ns
CustomCoverConstructor = custom_ns.class_('CustomCoverConstructor')
CONF_COVERS = 'covers'
CustomCoverConstructor = custom_ns.class_("CustomCoverConstructor")
CONF_COVERS = "covers"
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(CustomCoverConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_COVERS): cv.ensure_list(cover.COVER_SCHEMA),
})
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomCoverConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_COVERS): cv.ensure_list(cover.COVER_SCHEMA),
}
)
def to_code(config):
template_ = yield cg.process_lambda(
config[CONF_LAMBDA], [],
return_type=cg.std_vector.template(cover.Cover.operator('ptr')))
config[CONF_LAMBDA],
[],
return_type=cg.std_vector.template(cover.Cover.operator("ptr")),
)
rhs = CustomCoverConstructor(template_)
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 .. import custom_ns
CustomLightOutputConstructor = custom_ns.class_('CustomLightOutputConstructor')
CONF_LIGHTS = 'lights'
CustomLightOutputConstructor = custom_ns.class_("CustomLightOutputConstructor")
CONF_LIGHTS = "lights"
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(CustomLightOutputConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_LIGHTS): cv.ensure_list(light.ADDRESSABLE_LIGHT_SCHEMA),
})
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomLightOutputConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_LIGHTS): cv.ensure_list(light.ADDRESSABLE_LIGHT_SCHEMA),
}
)
def to_code(config):
template_ = yield cg.process_lambda(
config[CONF_LAMBDA], [],
return_type=cg.std_vector.template(light.LightOutput.operator('ptr')))
config[CONF_LAMBDA],
[],
return_type=cg.std_vector.template(light.LightOutput.operator("ptr")),
)
rhs = CustomLightOutputConstructor(template_)
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 .. import custom_ns
CustomBinaryOutputConstructor = custom_ns.class_('CustomBinaryOutputConstructor')
CustomFloatOutputConstructor = custom_ns.class_('CustomFloatOutputConstructor')
CustomBinaryOutputConstructor = custom_ns.class_("CustomBinaryOutputConstructor")
CustomFloatOutputConstructor = custom_ns.class_("CustomFloatOutputConstructor")
CONF_FLOAT = 'float'
CONF_FLOAT = "float"
CONFIG_SCHEMA = cv.typed_schema({
CONF_BINARY: cv.Schema({
cv.GenerateID(): cv.declare_id(CustomBinaryOutputConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_OUTPUTS):
cv.ensure_list(output.BINARY_OUTPUT_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(output.BinaryOutput),
})),
}),
CONF_FLOAT: cv.Schema({
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)
CONFIG_SCHEMA = cv.typed_schema(
{
CONF_BINARY: cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomBinaryOutputConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_OUTPUTS): cv.ensure_list(
output.BINARY_OUTPUT_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(output.BinaryOutput),
}
)
),
}
),
CONF_FLOAT: cv.Schema(
{
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):
type = config[CONF_TYPE]
if type == 'binary':
if type == "binary":
ret_type = output.BinaryOutputPtr
klass = CustomBinaryOutputConstructor
else:
ret_type = output.FloatOutputPtr
klass = CustomFloatOutputConstructor
template_ = yield cg.process_lambda(config[CONF_LAMBDA], [],
return_type=cg.std_vector.template(ret_type))
template_ = yield cg.process_lambda(
config[CONF_LAMBDA], [], return_type=cg.std_vector.template(ret_type)
)
rhs = klass(template_)
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 .. import custom_ns
CustomSensorConstructor = custom_ns.class_('CustomSensorConstructor')
CustomSensorConstructor = custom_ns.class_("CustomSensorConstructor")
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(CustomSensorConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_SENSORS): cv.ensure_list(sensor.SENSOR_SCHEMA),
})
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomSensorConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_SENSORS): cv.ensure_list(sensor.SENSOR_SCHEMA),
}
)
def to_code(config):
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_)
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 .. import custom_ns
CustomSwitchConstructor = custom_ns.class_('CustomSwitchConstructor')
CustomSwitchConstructor = custom_ns.class_("CustomSwitchConstructor")
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(CustomSwitchConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_SWITCHES):
cv.ensure_list(switch.SWITCH_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(switch.Switch),
})),
})
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomSwitchConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_SWITCHES): cv.ensure_list(
switch.SWITCH_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(switch.Switch),
}
)
),
}
)
def to_code(config):
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_)
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 .. import custom_ns
CustomTextSensorConstructor = custom_ns.class_('CustomTextSensorConstructor')
CustomTextSensorConstructor = custom_ns.class_("CustomTextSensorConstructor")
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(CustomTextSensorConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_TEXT_SENSORS):
cv.ensure_list(text_sensor.TEXT_SENSOR_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
})),
})
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomTextSensorConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Required(CONF_TEXT_SENSORS): cv.ensure_list(
text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
}
)
),
}
)
def to_code(config):
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_)
var = cg.variable(config[CONF_ID], rhs)

View File

@ -2,22 +2,27 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.const import CONF_COMPONENTS, CONF_ID, CONF_LAMBDA
custom_component_ns = cg.esphome_ns.namespace('custom_component')
CustomComponentConstructor = custom_component_ns.class_('CustomComponentConstructor')
custom_component_ns = cg.esphome_ns.namespace("custom_component")
CustomComponentConstructor = custom_component_ns.class_("CustomComponentConstructor")
MULTI_CONF = True
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(CustomComponentConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Optional(CONF_COMPONENTS): cv.ensure_list(cv.Schema({
cv.GenerateID(): cv.declare_id(cg.Component)
}).extend(cv.COMPONENT_SCHEMA)),
})
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(CustomComponentConstructor),
cv.Required(CONF_LAMBDA): cv.returning_lambda,
cv.Optional(CONF_COMPONENTS): cv.ensure_list(
cv.Schema({cv.GenerateID(): cv.declare_id(cg.Component)}).extend(
cv.COMPONENT_SCHEMA
)
),
}
)
def to_code(config):
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_)
var = cg.variable(config[CONF_ID], rhs)

View File

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

View File

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

View File

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

View File

@ -1,20 +1,32 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import CONF_ADDRESS, CONF_DALLAS_ID, CONF_INDEX, CONF_RESOLUTION, UNIT_CELSIUS, \
ICON_THERMOMETER, CONF_ID
from esphome.const import (
CONF_ADDRESS,
CONF_DALLAS_ID,
CONF_INDEX,
CONF_RESOLUTION,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
UNIT_CELSIUS,
CONF_ID,
)
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({
cv.GenerateID(): cv.declare_id(DallasTemperatureSensor),
cv.GenerateID(CONF_DALLAS_ID): cv.use_id(DallasComponent),
cv.Optional(CONF_ADDRESS): cv.hex_int,
cv.Optional(CONF_INDEX): cv.positive_int,
cv.Optional(CONF_RESOLUTION, default=12): cv.int_range(min=9, max=12),
}), cv.has_exactly_one_key(CONF_ADDRESS, CONF_INDEX))
CONFIG_SCHEMA = cv.All(
sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE).extend(
{
cv.GenerateID(): cv.declare_id(DallasTemperatureSensor),
cv.GenerateID(CONF_DALLAS_ID): cv.use_id(DallasComponent),
cv.Optional(CONF_ADDRESS): cv.hex_int,
cv.Optional(CONF_INDEX): cv.positive_int,
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):

View File

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

View File

@ -1,58 +1,81 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins, automation
from esphome.const import CONF_ID, CONF_MODE, CONF_NUMBER, CONF_PINS, CONF_RUN_CYCLES, \
CONF_RUN_DURATION, CONF_SLEEP_DURATION, CONF_WAKEUP_PIN
from esphome.const import (
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):
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:
raise cv.Invalid("Only pins {} support wakeup"
"".format(', '.join(str(x) for x in valid_pins)))
raise cv.Invalid(
"Only pins {} support wakeup"
"".format(", ".join(str(x) for x in valid_pins))
)
return value
deep_sleep_ns = cg.esphome_ns.namespace('deep_sleep')
DeepSleepComponent = deep_sleep_ns.class_('DeepSleepComponent', cg.Component)
EnterDeepSleepAction = deep_sleep_ns.class_('EnterDeepSleepAction', automation.Action)
PreventDeepSleepAction = deep_sleep_ns.class_('PreventDeepSleepAction', automation.Action)
deep_sleep_ns = cg.esphome_ns.namespace("deep_sleep")
DeepSleepComponent = deep_sleep_ns.class_("DeepSleepComponent", cg.Component)
EnterDeepSleepAction = deep_sleep_ns.class_("EnterDeepSleepAction", 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 = {
'IGNORE': WakeupPinMode.WAKEUP_PIN_MODE_IGNORE,
'KEEP_AWAKE': WakeupPinMode.WAKEUP_PIN_MODE_KEEP_AWAKE,
'INVERT_WAKEUP': WakeupPinMode.WAKEUP_PIN_MODE_INVERT_WAKEUP,
"IGNORE": WakeupPinMode.WAKEUP_PIN_MODE_IGNORE,
"KEEP_AWAKE": WakeupPinMode.WAKEUP_PIN_MODE_KEEP_AWAKE,
"INVERT_WAKEUP": WakeupPinMode.WAKEUP_PIN_MODE_INVERT_WAKEUP,
}
esp_sleep_ext1_wakeup_mode_t = cg.global_ns.enum('esp_sleep_ext1_wakeup_mode_t')
Ext1Wakeup = deep_sleep_ns.struct('Ext1Wakeup')
esp_sleep_ext1_wakeup_mode_t = cg.global_ns.enum("esp_sleep_ext1_wakeup_mode_t")
Ext1Wakeup = deep_sleep_ns.struct("Ext1Wakeup")
EXT1_WAKEUP_MODES = {
'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,
"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,
}
CONF_WAKEUP_PIN_MODE = 'wakeup_pin_mode'
CONF_ESP32_EXT1_WAKEUP = 'esp32_ext1_wakeup'
CONF_WAKEUP_PIN_MODE = "wakeup_pin_mode"
CONF_ESP32_EXT1_WAKEUP = "esp32_ext1_wakeup"
CONFIG_SCHEMA = cv.Schema({
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_WAKEUP_PIN): cv.All(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_ESP32_EXT1_WAKEUP): cv.All(cv.only_on_esp32, cv.Schema({
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_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)
CONFIG_SCHEMA = cv.Schema(
{
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_WAKEUP_PIN): cv.All(
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_ESP32_EXT1_WAKEUP): cv.All(
cv.only_on_esp32,
cv.Schema(
{
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_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):
@ -75,27 +98,43 @@ def to_code(config):
for pin in conf[CONF_PINS]:
mask |= 1 << pin[CONF_NUMBER]
struct = cg.StructInitializer(
Ext1Wakeup,
('mask', mask),
('wakeup_mode', conf[CONF_MODE])
Ext1Wakeup, ("mask", mask), ("wakeup_mode", conf[CONF_MODE])
)
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({
cv.GenerateID(): cv.use_id(DeepSleepComponent),
})
DEEP_SLEEP_ENTER_SCHEMA = automation.maybe_simple_id(
{
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):
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):
paren = yield cg.get_variable(config[CONF_ID])
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...> {
public:
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:
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.components import uart
DEPENDENCIES = ['uart']
CODEOWNERS = ['@glmnet']
DEPENDENCIES = ["uart"]
CODEOWNERS = ["@glmnet"]
dfplayer_ns = cg.esphome_ns.namespace('dfplayer')
DFPlayer = dfplayer_ns.class_('DFPlayer', cg.Component)
DFPlayerFinishedPlaybackTrigger = dfplayer_ns.class_('DFPlayerFinishedPlaybackTrigger',
automation.Trigger.template())
DFPlayerIsPlayingCondition = dfplayer_ns.class_('DFPlayerIsPlayingCondition', automation.Condition)
dfplayer_ns = cg.esphome_ns.namespace("dfplayer")
DFPlayer = dfplayer_ns.class_("DFPlayer", cg.Component)
DFPlayerFinishedPlaybackTrigger = dfplayer_ns.class_(
"DFPlayerFinishedPlaybackTrigger", automation.Trigger.template()
)
DFPlayerIsPlayingCondition = dfplayer_ns.class_(
"DFPlayerIsPlayingCondition", automation.Condition
)
MULTI_CONF = True
CONF_FOLDER = 'folder'
CONF_LOOP = 'loop'
CONF_VOLUME = 'volume'
CONF_EQ_PRESET = 'eq_preset'
CONF_ON_FINISHED_PLAYBACK = 'on_finished_playback'
CONF_FOLDER = "folder"
CONF_LOOP = "loop"
CONF_VOLUME = "volume"
CONF_EQ_PRESET = "eq_preset"
CONF_ON_FINISHED_PLAYBACK = "on_finished_playback"
EqPreset = dfplayer_ns.enum("EqPreset")
EQ_PRESET = {
'NORMAL': EqPreset.NORMAL,
'POP': EqPreset.POP,
'ROCK': EqPreset.ROCK,
'JAZZ': EqPreset.JAZZ,
'CLASSIC': EqPreset.CLASSIC,
'BASS': EqPreset.BASS,
"NORMAL": EqPreset.NORMAL,
"POP": EqPreset.POP,
"ROCK": EqPreset.ROCK,
"JAZZ": EqPreset.JAZZ,
"CLASSIC": EqPreset.CLASSIC,
"BASS": EqPreset.BASS,
}
Device = dfplayer_ns.enum("Device")
DEVICE = {
'USB': Device.USB,
'TF_CARD': Device.TF_CARD,
"USB": Device.USB,
"TF_CARD": Device.TF_CARD,
}
NextAction = dfplayer_ns.class_('NextAction', automation.Action)
PreviousAction = dfplayer_ns.class_('PreviousAction', automation.Action)
PlayFileAction = dfplayer_ns.class_('PlayFileAction', automation.Action)
PlayFolderAction = dfplayer_ns.class_('PlayFolderAction', automation.Action)
SetVolumeAction = dfplayer_ns.class_('SetVolumeAction', automation.Action)
SetEqAction = dfplayer_ns.class_('SetEqAction', automation.Action)
SleepAction = dfplayer_ns.class_('SleepAction', automation.Action)
ResetAction = dfplayer_ns.class_('ResetAction', automation.Action)
StartAction = dfplayer_ns.class_('StartAction', automation.Action)
PauseAction = dfplayer_ns.class_('PauseAction', automation.Action)
StopAction = dfplayer_ns.class_('StopAction', automation.Action)
RandomAction = dfplayer_ns.class_('RandomAction', automation.Action)
SetDeviceAction = dfplayer_ns.class_('SetDeviceAction', automation.Action)
NextAction = dfplayer_ns.class_("NextAction", automation.Action)
PreviousAction = dfplayer_ns.class_("PreviousAction", automation.Action)
PlayFileAction = dfplayer_ns.class_("PlayFileAction", automation.Action)
PlayFolderAction = dfplayer_ns.class_("PlayFolderAction", automation.Action)
SetVolumeAction = dfplayer_ns.class_("SetVolumeAction", automation.Action)
SetEqAction = dfplayer_ns.class_("SetEqAction", automation.Action)
SleepAction = dfplayer_ns.class_("SleepAction", automation.Action)
ResetAction = dfplayer_ns.class_("ResetAction", automation.Action)
StartAction = dfplayer_ns.class_("StartAction", automation.Action)
PauseAction = dfplayer_ns.class_("PauseAction", automation.Action)
StopAction = dfplayer_ns.class_("StopAction", automation.Action)
RandomAction = dfplayer_ns.class_("RandomAction", automation.Action)
SetDeviceAction = dfplayer_ns.class_("SetDeviceAction", automation.Action)
CONFIG_SCHEMA = cv.All(cv.Schema({
cv.GenerateID(): cv.declare_id(DFPlayer),
cv.Optional(CONF_ON_FINISHED_PLAYBACK): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(DFPlayerFinishedPlaybackTrigger),
}),
}).extend(uart.UART_DEVICE_SCHEMA))
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(DFPlayer),
cv.Optional(CONF_ON_FINISHED_PLAYBACK): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
DFPlayerFinishedPlaybackTrigger
),
}
),
}
).extend(uart.UART_DEVICE_SCHEMA)
)
def to_code(config):
@ -67,29 +78,48 @@ def to_code(config):
yield automation.build_automation(trigger, [], conf)
@automation.register_action('dfplayer.play_next', NextAction, cv.Schema({
cv.GenerateID(): cv.use_id(DFPlayer),
}))
@automation.register_action(
"dfplayer.play_next",
NextAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(DFPlayer),
}
),
)
def dfplayer_next_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID])
yield var
@automation.register_action('dfplayer.play_previous', PreviousAction, cv.Schema({
cv.GenerateID(): cv.use_id(DFPlayer),
}))
@automation.register_action(
"dfplayer.play_previous",
PreviousAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(DFPlayer),
}
),
)
def dfplayer_previous_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID])
yield var
@automation.register_action('dfplayer.play', PlayFileAction, cv.maybe_simple_value({
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))
@automation.register_action(
"dfplayer.play",
PlayFileAction,
cv.maybe_simple_value(
{
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):
var = cg.new_Pvariable(action_id, template_arg)
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
@automation.register_action('dfplayer.play_folder', PlayFolderAction, cv.Schema({
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),
}))
@automation.register_action(
"dfplayer.play_folder",
PlayFolderAction,
cv.Schema(
{
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):
var = cg.new_Pvariable(action_id, template_arg)
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
@automation.register_action('dfplayer.set_device', SetDeviceAction, cv.maybe_simple_value({
cv.GenerateID(): cv.use_id(DFPlayer),
cv.Required(CONF_DEVICE): cv.enum(DEVICE, upper=True),
}, key=CONF_DEVICE))
@automation.register_action(
"dfplayer.set_device",
SetDeviceAction,
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):
var = cg.new_Pvariable(action_id, template_arg)
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
@automation.register_action('dfplayer.set_volume', SetVolumeAction, cv.maybe_simple_value({
cv.GenerateID(): cv.use_id(DFPlayer),
cv.Required(CONF_VOLUME): cv.templatable(cv.int_),
}, key=CONF_VOLUME))
@automation.register_action(
"dfplayer.set_volume",
SetVolumeAction,
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):
var = cg.new_Pvariable(action_id, template_arg)
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
@automation.register_action('dfplayer.set_eq', SetEqAction, 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))
@automation.register_action(
"dfplayer.set_eq",
SetEqAction,
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):
var = cg.new_Pvariable(action_id, template_arg)
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
@automation.register_action('dfplayer.sleep', SleepAction, cv.Schema({
cv.GenerateID(): cv.use_id(DFPlayer),
}))
@automation.register_action(
"dfplayer.sleep",
SleepAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(DFPlayer),
}
),
)
def dfplayer_sleep_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID])
yield var
@automation.register_action('dfplayer.reset', ResetAction, cv.Schema({
cv.GenerateID(): cv.use_id(DFPlayer),
}))
@automation.register_action(
"dfplayer.reset",
ResetAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(DFPlayer),
}
),
)
def dfplayer_reset_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID])
yield var
@automation.register_action('dfplayer.start', StartAction, cv.Schema({
cv.GenerateID(): cv.use_id(DFPlayer),
}))
@automation.register_action(
"dfplayer.start",
StartAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(DFPlayer),
}
),
)
def dfplayer_start_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID])
yield var
@automation.register_action('dfplayer.pause', PauseAction, cv.Schema({
cv.GenerateID(): cv.use_id(DFPlayer),
}))
@automation.register_action(
"dfplayer.pause",
PauseAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(DFPlayer),
}
),
)
def dfplayer_pause_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID])
yield var
@automation.register_action('dfplayer.stop', StopAction, cv.Schema({
cv.GenerateID(): cv.use_id(DFPlayer),
}))
@automation.register_action(
"dfplayer.stop",
StopAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(DFPlayer),
}
),
)
def dfplayer_stop_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID])
yield var
@automation.register_action('dfplayer.random', RandomAction, cv.Schema({
cv.GenerateID(): cv.use_id(DFPlayer),
}))
@automation.register_action(
"dfplayer.random",
RandomAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(DFPlayer),
}
),
)
def dfplayer_random_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID])
yield var
@automation.register_condition('dfplayer.is_playing', DFPlayerIsPlayingCondition, cv.Schema({
cv.GenerateID(): cv.use_id(DFPlayer),
}))
@automation.register_condition(
"dfplayer.is_playing",
DFPlayerIsPlayingCondition,
cv.Schema(
{
cv.GenerateID(): cv.use_id(DFPlayer),
}
),
)
def dfplyaer_is_playing_to_code(config, condition_id, template_arg, args):
var = cg.new_Pvariable(condition_id, template_arg)
yield cg.register_parented(var, config[CONF_ID])

View File

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

View File

@ -2,30 +2,49 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import sensor
from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_MODEL, CONF_PIN, CONF_TEMPERATURE, \
ICON_THERMOMETER, UNIT_CELSIUS, ICON_WATER_PERCENT, UNIT_PERCENT
from esphome.const import (
CONF_HUMIDITY,
CONF_ID,
CONF_MODEL,
CONF_PIN,
CONF_TEMPERATURE,
ICON_EMPTY,
UNIT_CELSIUS,
UNIT_PERCENT,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_HUMIDITY,
)
from esphome.cpp_helpers import gpio_pin_expression
dht_ns = cg.esphome_ns.namespace('dht')
DHTModel = dht_ns.enum('DHTModel')
dht_ns = cg.esphome_ns.namespace("dht")
DHTModel = dht_ns.enum("DHTModel")
DHT_MODELS = {
'AUTO_DETECT': DHTModel.DHT_MODEL_AUTO_DETECT,
'DHT11': DHTModel.DHT_MODEL_DHT11,
'DHT22': DHTModel.DHT_MODEL_DHT22,
'AM2302': DHTModel.DHT_MODEL_AM2302,
'RHT03': DHTModel.DHT_MODEL_RHT03,
'SI7021': DHTModel.DHT_MODEL_SI7021,
'DHT22_TYPE2': DHTModel.DHT_MODEL_DHT22_TYPE2,
"AUTO_DETECT": DHTModel.DHT_MODEL_AUTO_DETECT,
"DHT11": DHTModel.DHT_MODEL_DHT11,
"DHT22": DHTModel.DHT_MODEL_DHT22,
"AM2302": DHTModel.DHT_MODEL_AM2302,
"RHT03": DHTModel.DHT_MODEL_RHT03,
"SI7021": DHTModel.DHT_MODEL_SI7021,
"DHT22_TYPE2": DHTModel.DHT_MODEL_DHT22_TYPE2,
}
DHT = dht_ns.class_('DHT', cg.PollingComponent)
DHT = dht_ns.class_("DHT", cg.PollingComponent)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(DHT),
cv.Required(CONF_PIN): pins.gpio_input_pin_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, 0),
cv.Optional(CONF_MODEL, default='auto detect'): cv.enum(DHT_MODELS, upper=True, space='_'),
}).extend(cv.polling_component_schema('60s'))
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(DHT),
cv.Required(CONF_PIN): pins.gpio_input_pin_schema,
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, 0, DEVICE_CLASS_HUMIDITY
),
cv.Optional(CONF_MODEL, default="auto detect"): cv.enum(
DHT_MODELS, upper=True, space="_"
),
}
).extend(cv.polling_component_schema("60s"))
def to_code(config):

View File

@ -1,19 +1,37 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, \
UNIT_CELSIUS, ICON_THERMOMETER, ICON_WATER_PERCENT, UNIT_PERCENT
from esphome.const import (
CONF_HUMIDITY,
CONF_ID,
CONF_TEMPERATURE,
UNIT_CELSIUS,
ICON_EMPTY,
UNIT_PERCENT,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_HUMIDITY,
)
DEPENDENCIES = ['i2c']
DEPENDENCIES = ["i2c"]
dht12_ns = cg.esphome_ns.namespace('dht12')
DHT12Component = dht12_ns.class_('DHT12Component', cg.PollingComponent, i2c.I2CDevice)
dht12_ns = cg.esphome_ns.namespace("dht12")
DHT12Component = dht12_ns.class_("DHT12Component", cg.PollingComponent, i2c.I2CDevice)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(DHT12Component),
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),
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x5C))
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(DHT12Component),
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):

View File

@ -7,14 +7,18 @@ from esphome.core import coroutine, coroutine_with_priority
IS_PLATFORM_COMPONENT = True
display_ns = cg.esphome_ns.namespace('display')
DisplayBuffer = display_ns.class_('DisplayBuffer')
DisplayPage = display_ns.class_('DisplayPage')
DisplayPagePtr = DisplayPage.operator('ptr')
DisplayBufferRef = DisplayBuffer.operator('ref')
DisplayPageShowAction = display_ns.class_('DisplayPageShowAction', automation.Action)
DisplayPageShowNextAction = display_ns.class_('DisplayPageShowNextAction', automation.Action)
DisplayPageShowPrevAction = display_ns.class_('DisplayPageShowPrevAction', automation.Action)
display_ns = cg.esphome_ns.namespace("display")
DisplayBuffer = display_ns.class_("DisplayBuffer")
DisplayPage = display_ns.class_("DisplayPage")
DisplayPagePtr = DisplayPage.operator("ptr")
DisplayBufferRef = DisplayBuffer.operator("ref")
DisplayPageShowAction = display_ns.class_("DisplayPageShowAction", automation.Action)
DisplayPageShowNextAction = display_ns.class_(
"DisplayPageShowNextAction", automation.Action
)
DisplayPageShowPrevAction = display_ns.class_(
"DisplayPageShowPrevAction", automation.Action
)
DISPLAY_ROTATIONS = {
0: display_ns.DISPLAY_ROTATION_0_DEGREES,
@ -31,17 +35,26 @@ def validate_rotation(value):
return cv.enum(DISPLAY_ROTATIONS, int=True)(value)
BASIC_DISPLAY_SCHEMA = cv.Schema({
cv.Optional(CONF_LAMBDA): cv.lambda_,
})
BASIC_DISPLAY_SCHEMA = cv.Schema(
{
cv.Optional(CONF_LAMBDA): cv.lambda_,
}
)
FULL_DISPLAY_SCHEMA = BASIC_DISPLAY_SCHEMA.extend({
cv.Optional(CONF_ROTATION): validate_rotation,
cv.Optional(CONF_PAGES): cv.All(cv.ensure_list({
cv.GenerateID(): cv.declare_id(DisplayPage),
cv.Required(CONF_LAMBDA): cv.lambda_,
}), cv.Length(min=1)),
})
FULL_DISPLAY_SCHEMA = BASIC_DISPLAY_SCHEMA.extend(
{
cv.Optional(CONF_ROTATION): validate_rotation,
cv.Optional(CONF_PAGES): cv.All(
cv.ensure_list(
{
cv.GenerateID(): cv.declare_id(DisplayPage),
cv.Required(CONF_LAMBDA): cv.lambda_,
}
),
cv.Length(min=1),
),
}
)
@coroutine
@ -51,8 +64,9 @@ def setup_display_core_(var, config):
if CONF_PAGES in config:
pages = []
for conf in config[CONF_PAGES]:
lambda_ = yield cg.process_lambda(conf[CONF_LAMBDA], [(DisplayBufferRef, 'it')],
return_type=cg.void)
lambda_ = yield cg.process_lambda(
conf[CONF_LAMBDA], [(DisplayBufferRef, "it")], return_type=cg.void
)
page = cg.new_Pvariable(conf[CONF_ID], lambda_)
pages.append(page)
cg.add(var.set_pages(pages))
@ -63,9 +77,15 @@ def register_display(var, config):
yield setup_display_core_(var, config)
@automation.register_action('display.page.show', DisplayPageShowAction, maybe_simple_id({
cv.Required(CONF_ID): cv.templatable(cv.use_id(DisplayPage)),
}))
@automation.register_action(
"display.page.show",
DisplayPageShowAction,
maybe_simple_id(
{
cv.Required(CONF_ID): cv.templatable(cv.use_id(DisplayPage)),
}
),
)
def display_page_show_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
if isinstance(config[CONF_ID], core.Lambda):
@ -77,18 +97,29 @@ def display_page_show_to_code(config, action_id, template_arg, args):
yield var
@automation.register_action('display.page.show_next', DisplayPageShowNextAction, maybe_simple_id({
cv.Required(CONF_ID): cv.templatable(cv.use_id(DisplayBuffer)),
}))
@automation.register_action(
"display.page.show_next",
DisplayPageShowNextAction,
maybe_simple_id(
{
cv.Required(CONF_ID): cv.templatable(cv.use_id(DisplayBuffer)),
}
),
)
def display_page_show_next_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_action('display.page.show_previous', DisplayPageShowPrevAction,
maybe_simple_id({
cv.Required(CONF_ID): cv.templatable(cv.use_id(DisplayBuffer)),
}))
@automation.register_action(
"display.page.show_previous",
DisplayPageShowPrevAction,
maybe_simple_id(
{
cv.Required(CONF_ID): cv.templatable(cv.use_id(DisplayBuffer)),
}
),
)
def display_page_show_previous_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(action_id, template_arg, paren)

View File

@ -9,7 +9,7 @@ namespace display {
static const char *TAG = "display";
const Color COLOR_OFF(0, 0, 0, 0);
const Color COLOR_ON(1, 1, 1, 1);
const Color COLOR_ON(255, 255, 255, 255);
void DisplayBuffer::init_internal_(uint32_t buffer_length) {
this->buffer_ = new uint8_t[buffer_length];

View File

@ -3,7 +3,7 @@
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/automation.h"
#include "esphome/core/color.h"
#include "display_color_utils.h"
#ifdef USE_TIME
#include "esphome/components/time/real_time_clock.h"

View File

@ -0,0 +1,110 @@
#pragma once
#include "esphome/core/color.h"
namespace esphome {
namespace display {
enum ColorOrder : uint8_t { COLOR_ORDER_RGB = 0, COLOR_ORDER_BGR = 1, COLOR_ORDER_GRB = 2 };
enum ColorBitness : uint8_t { COLOR_BITNESS_888 = 0, COLOR_BITNESS_565 = 1, COLOR_BITNESS_332 = 2 };
inline static uint8_t esp_scale(uint8_t i, uint8_t scale, uint8_t max_value = 255) { return (max_value * i / scale); }
class ColorUtil {
public:
static Color to_color(uint32_t colorcode, ColorOrder color_order,
ColorBitness color_bitness = ColorBitness::COLOR_BITNESS_888, bool right_bit_aligned = true) {
uint8_t first_color, second_color, third_color;
uint8_t first_bits = 0;
uint8_t second_bits = 0;
uint8_t third_bits = 0;
switch (color_bitness) {
case COLOR_BITNESS_888:
first_bits = 8;
second_bits = 8;
third_bits = 8;
break;
case COLOR_BITNESS_565:
first_bits = 5;
second_bits = 6;
third_bits = 5;
break;
case COLOR_BITNESS_332:
first_bits = 3;
second_bits = 3;
third_bits = 2;
break;
}
first_color = right_bit_aligned ? esp_scale(((colorcode >> (second_bits + third_bits)) & ((1 << first_bits) - 1)),
((1 << first_bits) - 1))
: esp_scale(((colorcode >> 16) & 0xFF), (1 << first_bits) - 1);
second_color = right_bit_aligned
? esp_scale(((colorcode >> third_bits) & ((1 << second_bits) - 1)), ((1 << second_bits) - 1))
: esp_scale(((colorcode >> 8) & 0xFF), ((1 << second_bits) - 1));
third_color = (right_bit_aligned ? esp_scale(((colorcode >> 0) & 0xFF), ((1 << third_bits) - 1))
: esp_scale(((colorcode >> 0) & 0xFF), (1 << third_bits) - 1));
Color color_return;
switch (color_order) {
case COLOR_ORDER_RGB:
color_return.r = first_color;
color_return.g = second_color;
color_return.b = third_color;
break;
case COLOR_ORDER_BGR:
color_return.b = first_color;
color_return.g = second_color;
color_return.r = third_color;
break;
case COLOR_ORDER_GRB:
color_return.g = first_color;
color_return.r = second_color;
color_return.b = third_color;
break;
}
return color_return;
}
static uint8_t color_to_332(Color color, ColorOrder color_order = ColorOrder::COLOR_ORDER_RGB) {
uint16_t red_color, green_color, blue_color;
red_color = esp_scale8(color.red, ((1 << 3) - 1));
green_color = esp_scale8(color.green, ((1 << 3) - 1));
blue_color = esp_scale8(color.blue, (1 << 2) - 1);
switch (color_order) {
case COLOR_ORDER_RGB:
return red_color << 5 | green_color << 2 | blue_color;
case COLOR_ORDER_BGR:
return blue_color << 6 | green_color << 3 | red_color;
case COLOR_ORDER_GRB:
return green_color << 5 | red_color << 2 | blue_color;
}
return 0;
}
static uint16_t color_to_565(Color color, ColorOrder color_order = ColorOrder::COLOR_ORDER_RGB) {
uint16_t red_color, green_color, blue_color;
red_color = esp_scale8(color.red, ((1 << 5) - 1));
green_color = esp_scale8(color.green, ((1 << 6) - 1));
blue_color = esp_scale8(color.blue, (1 << 5) - 1);
switch (color_order) {
case COLOR_ORDER_RGB:
return red_color << 11 | green_color << 5 | blue_color;
case COLOR_ORDER_BGR:
return blue_color << 11 | green_color << 5 | red_color;
case COLOR_ORDER_GRB:
return green_color << 10 | red_color << 5 | blue_color;
}
return 0;
}
static uint32_t color_to_grayscale4(Color color) {
uint32_t gs4 = esp_scale8(color.white, 15);
return gs4;
}
};
} // namespace display
} // namespace esphome

View File

@ -5,31 +5,45 @@ from esphome.components import i2c, time
from esphome.const import CONF_ID
CODEOWNERS = ['@badbadc0ffee']
DEPENDENCIES = ['i2c']
ds1307_ns = cg.esphome_ns.namespace('ds1307')
DS1307Component = ds1307_ns.class_('DS1307Component', time.RealTimeClock, i2c.I2CDevice)
WriteAction = ds1307_ns.class_('WriteAction', automation.Action)
ReadAction = ds1307_ns.class_('ReadAction', automation.Action)
CODEOWNERS = ["@badbadc0ffee"]
DEPENDENCIES = ["i2c"]
ds1307_ns = cg.esphome_ns.namespace("ds1307")
DS1307Component = ds1307_ns.class_("DS1307Component", time.RealTimeClock, i2c.I2CDevice)
WriteAction = ds1307_ns.class_("WriteAction", automation.Action)
ReadAction = ds1307_ns.class_("ReadAction", automation.Action)
CONFIG_SCHEMA = time.TIME_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(DS1307Component),
}).extend(i2c.i2c_device_schema(0x68))
CONFIG_SCHEMA = time.TIME_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(DS1307Component),
}
).extend(i2c.i2c_device_schema(0x68))
@automation.register_action('ds1307.write_time', WriteAction, cv.Schema({
cv.GenerateID(): cv.use_id(DS1307Component),
}))
@automation.register_action(
"ds1307.write_time",
WriteAction,
cv.Schema(
{
cv.GenerateID(): cv.use_id(DS1307Component),
}
),
)
def ds1307_write_time_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID])
yield var
@automation.register_action('ds1307.read_time', ReadAction, automation.maybe_simple_id({
cv.GenerateID(): cv.use_id(DS1307Component),
}))
@automation.register_action(
"ds1307.read_time",
ReadAction,
automation.maybe_simple_id(
{
cv.GenerateID(): cv.use_id(DS1307Component),
}
),
)
def ds1307_read_time_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_parented(var, config[CONF_ID])

View File

@ -2,16 +2,31 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import sensor
from esphome.const import CONF_ID, CONF_PIN, UNIT_PERCENT, ICON_PERCENT
from esphome.const import (
CONF_ID,
CONF_PIN,
DEVICE_CLASS_EMPTY,
UNIT_PERCENT,
ICON_PERCENT,
)
duty_cycle_ns = cg.esphome_ns.namespace('duty_cycle')
DutyCycleSensor = duty_cycle_ns.class_('DutyCycleSensor', sensor.Sensor, cg.PollingComponent)
duty_cycle_ns = cg.esphome_ns.namespace("duty_cycle")
DutyCycleSensor = duty_cycle_ns.class_(
"DutyCycleSensor", sensor.Sensor, cg.PollingComponent
)
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PERCENT, ICON_PERCENT, 1).extend({
cv.GenerateID(): cv.declare_id(DutyCycleSensor),
cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema,
pins.validate_has_interrupt),
}).extend(cv.polling_component_schema('60s'))
CONFIG_SCHEMA = (
sensor.sensor_schema(UNIT_PERCENT, ICON_PERCENT, 1, DEVICE_CLASS_EMPTY)
.extend(
{
cv.GenerateID(): cv.declare_id(DutyCycleSensor),
cv.Required(CONF_PIN): cv.All(
pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt
),
}
)
.extend(cv.polling_component_schema("60s"))
)
def to_code(config):

View File

@ -4,28 +4,29 @@ from esphome.components.light.types import AddressableLightEffect
from esphome.components.light.effects import register_addressable_effect
from esphome.const import CONF_ID, CONF_NAME, CONF_METHOD, CONF_CHANNELS
e131_ns = cg.esphome_ns.namespace('e131')
E131AddressableLightEffect = e131_ns.class_('E131AddressableLightEffect', AddressableLightEffect)
E131Component = e131_ns.class_('E131Component', cg.Component)
e131_ns = cg.esphome_ns.namespace("e131")
E131AddressableLightEffect = e131_ns.class_(
"E131AddressableLightEffect", AddressableLightEffect
)
E131Component = e131_ns.class_("E131Component", cg.Component)
METHODS = {
'UNICAST': e131_ns.E131_UNICAST,
'MULTICAST': e131_ns.E131_MULTICAST
}
METHODS = {"UNICAST": e131_ns.E131_UNICAST, "MULTICAST": e131_ns.E131_MULTICAST}
CHANNELS = {
'MONO': e131_ns.E131_MONO,
'RGB': e131_ns.E131_RGB,
'RGBW': e131_ns.E131_RGBW
"MONO": e131_ns.E131_MONO,
"RGB": e131_ns.E131_RGB,
"RGBW": e131_ns.E131_RGBW,
}
CONF_UNIVERSE = 'universe'
CONF_E131_ID = 'e131_id'
CONF_UNIVERSE = "universe"
CONF_E131_ID = "e131_id"
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(E131Component),
cv.Optional(CONF_METHOD, default='MULTICAST'): cv.one_of(*METHODS, upper=True),
})
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(E131Component),
cv.Optional(CONF_METHOD, default="MULTICAST"): cv.one_of(*METHODS, upper=True),
}
)
def to_code(config):
@ -34,11 +35,16 @@ def to_code(config):
cg.add(var.set_method(METHODS[config[CONF_METHOD]]))
@register_addressable_effect('e131', E131AddressableLightEffect, "E1.31", {
cv.GenerateID(CONF_E131_ID): cv.use_id(E131Component),
cv.Required(CONF_UNIVERSE): cv.int_range(min=1, max=512),
cv.Optional(CONF_CHANNELS, default='RGB'): cv.one_of(*CHANNELS, upper=True)
})
@register_addressable_effect(
"e131",
E131AddressableLightEffect,
"E1.31",
{
cv.GenerateID(CONF_E131_ID): cv.use_id(E131Component),
cv.Required(CONF_UNIVERSE): cv.int_range(min=1, max=512),
cv.Optional(CONF_CHANNELS, default="RGB"): cv.one_of(*CHANNELS, upper=True),
},
)
def e131_light_effect_to_code(config, effect_id):
parent = yield cg.get_variable(config[CONF_E131_ID])

View File

@ -40,7 +40,7 @@ void E131AddressableLightEffect::stop() {
AddressableLightEffect::stop();
}
void E131AddressableLightEffect::apply(light::AddressableLight &it, const light::ESPColor &current_color) {
void E131AddressableLightEffect::apply(light::AddressableLight &it, const Color &current_color) {
// ignore, it is run by `E131Component::update()`
}
@ -53,7 +53,8 @@ bool E131AddressableLightEffect::process_(int universe, const E131Packet &packet
int output_offset = (universe - first_universe_) * get_lights_per_universe();
// limit amount of lights per universe and received
int output_end = std::min(it->size(), std::min(output_offset + get_lights_per_universe(), packet.count - 1));
int output_end =
std::min(it->size(), std::min(output_offset + get_lights_per_universe(), output_offset + packet.count - 1));
auto input_data = packet.values + 1;
ESP_LOGV(TAG, "Applying data for '%s' on %d universe, for %d-%d.", get_name().c_str(), universe, output_offset,
@ -63,22 +64,22 @@ bool E131AddressableLightEffect::process_(int universe, const E131Packet &packet
case E131_MONO:
for (; output_offset < output_end; output_offset++, input_data++) {
auto output = (*it)[output_offset];
output.set(light::ESPColor(input_data[0], input_data[0], input_data[0], input_data[0]));
output.set(Color(input_data[0], input_data[0], input_data[0], input_data[0]));
}
break;
case E131_RGB:
for (; output_offset < output_end; output_offset++, input_data += 3) {
auto output = (*it)[output_offset];
output.set(light::ESPColor(input_data[0], input_data[1], input_data[2],
(input_data[0] + input_data[1] + input_data[2]) / 3));
output.set(
Color(input_data[0], input_data[1], input_data[2], (input_data[0] + input_data[1] + input_data[2]) / 3));
}
break;
case E131_RGBW:
for (; output_offset < output_end; output_offset++, input_data += 4) {
auto output = (*it)[output_offset];
output.set(light::ESPColor(input_data[0], input_data[1], input_data[2], input_data[3]));
output.set(Color(input_data[0], input_data[1], input_data[2], input_data[3]));
}
break;
}

View File

@ -18,7 +18,7 @@ class E131AddressableLightEffect : public light::AddressableLightEffect {
public:
void start() 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;
public:
int get_data_per_universe() const;

View File

@ -2,27 +2,34 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.components import binary_sensor, cover
from esphome.const import CONF_CLOSE_ACTION, CONF_CLOSE_DURATION, \
CONF_CLOSE_ENDSTOP, CONF_ID, CONF_OPEN_ACTION, CONF_OPEN_DURATION, \
CONF_OPEN_ENDSTOP, CONF_STOP_ACTION, CONF_MAX_DURATION
from esphome.const import (
CONF_CLOSE_ACTION,
CONF_CLOSE_DURATION,
CONF_CLOSE_ENDSTOP,
CONF_ID,
CONF_OPEN_ACTION,
CONF_OPEN_DURATION,
CONF_OPEN_ENDSTOP,
CONF_STOP_ACTION,
CONF_MAX_DURATION,
)
endstop_ns = cg.esphome_ns.namespace('endstop')
EndstopCover = endstop_ns.class_('EndstopCover', cover.Cover, cg.Component)
endstop_ns = cg.esphome_ns.namespace("endstop")
EndstopCover = endstop_ns.class_("EndstopCover", cover.Cover, cg.Component)
CONFIG_SCHEMA = cover.COVER_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(EndstopCover),
cv.Required(CONF_STOP_ACTION): automation.validate_automation(single=True),
cv.Required(CONF_OPEN_ENDSTOP): cv.use_id(binary_sensor.BinarySensor),
cv.Required(CONF_OPEN_ACTION): automation.validate_automation(single=True),
cv.Required(CONF_OPEN_DURATION): cv.positive_time_period_milliseconds,
cv.Required(CONF_CLOSE_ACTION): automation.validate_automation(single=True),
cv.Required(CONF_CLOSE_ENDSTOP): cv.use_id(binary_sensor.BinarySensor),
cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds,
cv.Optional(CONF_MAX_DURATION): cv.positive_time_period_milliseconds,
}).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = cover.COVER_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(EndstopCover),
cv.Required(CONF_STOP_ACTION): automation.validate_automation(single=True),
cv.Required(CONF_OPEN_ENDSTOP): cv.use_id(binary_sensor.BinarySensor),
cv.Required(CONF_OPEN_ACTION): automation.validate_automation(single=True),
cv.Required(CONF_OPEN_DURATION): cv.positive_time_period_milliseconds,
cv.Required(CONF_CLOSE_ACTION): automation.validate_automation(single=True),
cv.Required(CONF_CLOSE_ENDSTOP): cv.use_id(binary_sensor.BinarySensor),
cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds,
cv.Optional(CONF_MAX_DURATION): cv.positive_time_period_milliseconds,
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config):
@ -30,17 +37,23 @@ def to_code(config):
yield cg.register_component(var, config)
yield cover.register_cover(var, config)
yield automation.build_automation(var.get_stop_trigger(), [], config[CONF_STOP_ACTION])
yield automation.build_automation(
var.get_stop_trigger(), [], config[CONF_STOP_ACTION]
)
bin = yield cg.get_variable(config[CONF_OPEN_ENDSTOP])
cg.add(var.set_open_endstop(bin))
cg.add(var.set_open_duration(config[CONF_OPEN_DURATION]))
yield automation.build_automation(var.get_open_trigger(), [], config[CONF_OPEN_ACTION])
yield automation.build_automation(
var.get_open_trigger(), [], config[CONF_OPEN_ACTION]
)
bin = yield cg.get_variable(config[CONF_CLOSE_ENDSTOP])
cg.add(var.set_close_endstop(bin))
cg.add(var.set_close_duration(config[CONF_CLOSE_DURATION]))
yield automation.build_automation(var.get_close_trigger(), [], config[CONF_CLOSE_ACTION])
yield automation.build_automation(
var.get_close_trigger(), [], config[CONF_CLOSE_ACTION]
)
if CONF_MAX_DURATION in config:
cg.add(var.set_max_duration(config[CONF_MAX_DURATION]))

View File

@ -3,26 +3,30 @@ import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_TYPE, CONF_UUID, ESP_PLATFORM_ESP32
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
CONFLICTS_WITH = ['esp32_ble_tracker']
CONFLICTS_WITH = ["esp32_ble_tracker"]
esp32_ble_beacon_ns = cg.esphome_ns.namespace('esp32_ble_beacon')
ESP32BLEBeacon = esp32_ble_beacon_ns.class_('ESP32BLEBeacon', cg.Component)
esp32_ble_beacon_ns = cg.esphome_ns.namespace("esp32_ble_beacon")
ESP32BLEBeacon = esp32_ble_beacon_ns.class_("ESP32BLEBeacon", cg.Component)
CONF_MAJOR = 'major'
CONF_MINOR = 'minor'
CONF_MAJOR = "major"
CONF_MINOR = "minor"
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(ESP32BLEBeacon),
cv.Required(CONF_TYPE): cv.one_of('IBEACON', upper=True),
cv.Required(CONF_UUID): cv.uuid,
cv.Optional(CONF_MAJOR, default=10167): cv.uint16_t,
cv.Optional(CONF_MINOR, default=61958): cv.uint16_t,
}).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(ESP32BLEBeacon),
cv.Required(CONF_TYPE): cv.one_of("IBEACON", upper=True),
cv.Required(CONF_UUID): cv.uuid,
cv.Optional(CONF_MAJOR, default=10167): cv.uint16_t,
cv.Optional(CONF_MINOR, default=61958): cv.uint16_t,
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config):
uuid = config[CONF_UUID].hex
uuid_arr = [cg.RawExpression('0x{}'.format(uuid[i:i + 2])) for i in range(0, len(uuid), 2)]
uuid_arr = [
cg.RawExpression("0x{}".format(uuid[i : i + 2])) for i in range(0, len(uuid), 2)
]
var = cg.new_Pvariable(config[CONF_ID], uuid_arr)
yield cg.register_component(var, config)
cg.add(var.set_major(config[CONF_MAJOR]))

View File

@ -2,33 +2,46 @@ import re
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.const import CONF_ID, ESP_PLATFORM_ESP32, CONF_INTERVAL, \
CONF_DURATION, CONF_TRIGGER_ID, CONF_MAC_ADDRESS, CONF_SERVICE_UUID, CONF_MANUFACTURER_ID, \
CONF_ON_BLE_ADVERTISE, CONF_ON_BLE_SERVICE_DATA_ADVERTISE, \
CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE
from esphome.const import (
CONF_ID,
ESP_PLATFORM_ESP32,
CONF_INTERVAL,
CONF_DURATION,
CONF_TRIGGER_ID,
CONF_MAC_ADDRESS,
CONF_SERVICE_UUID,
CONF_MANUFACTURER_ID,
CONF_ON_BLE_ADVERTISE,
CONF_ON_BLE_SERVICE_DATA_ADVERTISE,
CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE,
)
from esphome.core import coroutine
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
AUTO_LOAD = ['xiaomi_ble', 'ruuvi_ble']
AUTO_LOAD = ["xiaomi_ble", "ruuvi_ble"]
CONF_ESP32_BLE_ID = 'esp32_ble_id'
CONF_SCAN_PARAMETERS = 'scan_parameters'
CONF_WINDOW = 'window'
CONF_ACTIVE = 'active'
esp32_ble_tracker_ns = cg.esphome_ns.namespace('esp32_ble_tracker')
ESP32BLETracker = esp32_ble_tracker_ns.class_('ESP32BLETracker', cg.Component)
ESPBTDeviceListener = esp32_ble_tracker_ns.class_('ESPBTDeviceListener')
ESPBTDevice = esp32_ble_tracker_ns.class_('ESPBTDevice')
ESPBTDeviceConstRef = ESPBTDevice.operator('ref').operator('const')
CONF_ESP32_BLE_ID = "esp32_ble_id"
CONF_SCAN_PARAMETERS = "scan_parameters"
CONF_WINDOW = "window"
CONF_ACTIVE = "active"
esp32_ble_tracker_ns = cg.esphome_ns.namespace("esp32_ble_tracker")
ESP32BLETracker = esp32_ble_tracker_ns.class_("ESP32BLETracker", cg.Component)
ESPBTDeviceListener = esp32_ble_tracker_ns.class_("ESPBTDeviceListener")
ESPBTDevice = esp32_ble_tracker_ns.class_("ESPBTDevice")
ESPBTDeviceConstRef = ESPBTDevice.operator("ref").operator("const")
adv_data_t = cg.std_vector.template(cg.uint8)
adv_data_t_const_ref = adv_data_t.operator('ref').operator('const')
adv_data_t_const_ref = adv_data_t.operator("ref").operator("const")
# Triggers
ESPBTAdvertiseTrigger = esp32_ble_tracker_ns.class_(
'ESPBTAdvertiseTrigger', automation.Trigger.template(ESPBTDeviceConstRef))
"ESPBTAdvertiseTrigger", automation.Trigger.template(ESPBTDeviceConstRef)
)
BLEServiceDataAdvertiseTrigger = esp32_ble_tracker_ns.class_(
'BLEServiceDataAdvertiseTrigger', automation.Trigger.template(adv_data_t_const_ref))
"BLEServiceDataAdvertiseTrigger", automation.Trigger.template(adv_data_t_const_ref)
)
BLEManufacturerDataAdvertiseTrigger = esp32_ble_tracker_ns.class_(
'BLEManufacturerDataAdvertiseTrigger', automation.Trigger.template(adv_data_t_const_ref))
"BLEManufacturerDataAdvertiseTrigger",
automation.Trigger.template(adv_data_t_const_ref),
)
def validate_scan_parameters(config):
@ -37,19 +50,23 @@ def validate_scan_parameters(config):
window = config[CONF_WINDOW]
if window > interval:
raise cv.Invalid("Scan window ({}) needs to be smaller than scan interval ({})"
"".format(window, interval))
raise cv.Invalid(
"Scan window ({}) needs to be smaller than scan interval ({})"
"".format(window, interval)
)
if interval.total_milliseconds * 3 > duration.total_milliseconds:
raise cv.Invalid("Scan duration needs to be at least three times the scan interval to"
"cover all BLE channels.")
raise cv.Invalid(
"Scan duration needs to be at least three times the scan interval to"
"cover all BLE channels."
)
return config
bt_uuid16_format = 'XXXX'
bt_uuid32_format = 'XXXXXXXX'
bt_uuid128_format = 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
bt_uuid16_format = "XXXX"
bt_uuid32_format = "XXXXXXXX"
bt_uuid128_format = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
def bt_uuid(value):
@ -60,67 +77,103 @@ def bt_uuid(value):
pattern = re.compile("^[A-F|0-9]{4,}$")
if not pattern.match(value):
raise cv.Invalid(
f"Invalid hexadecimal value for 16 bit UUID format: '{in_value}'")
f"Invalid hexadecimal value for 16 bit UUID format: '{in_value}'"
)
return value
if len(value) == len(bt_uuid32_format):
pattern = re.compile("^[A-F|0-9]{8,}$")
if not pattern.match(value):
raise cv.Invalid(
f"Invalid hexadecimal value for 32 bit UUID format: '{in_value}'")
f"Invalid hexadecimal value for 32 bit UUID format: '{in_value}'"
)
return value
if len(value) == len(bt_uuid128_format):
pattern = re.compile(
"^[A-F|0-9]{8,}-[A-F|0-9]{4,}-[A-F|0-9]{4,}-[A-F|0-9]{4,}-[A-F|0-9]{12,}$")
"^[A-F|0-9]{8,}-[A-F|0-9]{4,}-[A-F|0-9]{4,}-[A-F|0-9]{4,}-[A-F|0-9]{12,}$"
)
if not pattern.match(value):
raise cv.Invalid(
f"Invalid hexadecimal value for 128 UUID format: '{in_value}'")
f"Invalid hexadecimal value for 128 UUID format: '{in_value}'"
)
return value
raise cv.Invalid(
"Service UUID must be in 16 bit '{}', 32 bit '{}', or 128 bit '{}' format".format(
bt_uuid16_format, bt_uuid32_format, bt_uuid128_format))
bt_uuid16_format, bt_uuid32_format, bt_uuid128_format
)
)
def as_hex(value):
return cg.RawExpression(f'0x{value}ULL')
return cg.RawExpression(f"0x{value}ULL")
def as_hex_array(value):
value = value.replace("-", "")
cpp_array = [f'0x{part}' for part in [value[i:i+2] for i in range(0, len(value), 2)]]
cpp_array = [
f"0x{part}" for part in [value[i : i + 2] for i in range(0, len(value), 2)]
]
return cg.RawExpression(
'(uint8_t*)(const uint8_t[16]){{{}}}'.format(','.join(reversed(cpp_array))))
"(uint8_t*)(const uint8_t[16]){{{}}}".format(",".join(reversed(cpp_array)))
)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(ESP32BLETracker),
cv.Optional(CONF_SCAN_PARAMETERS, default={}): cv.All(cv.Schema({
cv.Optional(CONF_DURATION, default='5min'): cv.positive_time_period_seconds,
cv.Optional(CONF_INTERVAL, default='320ms'): cv.positive_time_period_milliseconds,
cv.Optional(CONF_WINDOW, default='30ms'): cv.positive_time_period_milliseconds,
cv.Optional(CONF_ACTIVE, default=True): cv.boolean,
}), validate_scan_parameters),
cv.Optional(CONF_ON_BLE_ADVERTISE): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPBTAdvertiseTrigger),
cv.Optional(CONF_MAC_ADDRESS): cv.mac_address,
}),
cv.Optional(CONF_ON_BLE_SERVICE_DATA_ADVERTISE): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(BLEServiceDataAdvertiseTrigger),
cv.Optional(CONF_MAC_ADDRESS): cv.mac_address,
cv.Required(CONF_SERVICE_UUID): bt_uuid,
}),
cv.Optional(CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(BLEManufacturerDataAdvertiseTrigger),
cv.Optional(CONF_MAC_ADDRESS): cv.mac_address,
cv.Required(CONF_MANUFACTURER_ID): bt_uuid,
}),
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(ESP32BLETracker),
cv.Optional(CONF_SCAN_PARAMETERS, default={}): cv.All(
cv.Schema(
{
cv.Optional(
CONF_DURATION, default="5min"
): cv.positive_time_period_seconds,
cv.Optional(
CONF_INTERVAL, default="320ms"
): cv.positive_time_period_milliseconds,
cv.Optional(
CONF_WINDOW, default="30ms"
): cv.positive_time_period_milliseconds,
cv.Optional(CONF_ACTIVE, default=True): cv.boolean,
}
),
validate_scan_parameters,
),
cv.Optional(CONF_ON_BLE_ADVERTISE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPBTAdvertiseTrigger),
cv.Optional(CONF_MAC_ADDRESS): cv.mac_address,
}
),
cv.Optional(CONF_ON_BLE_SERVICE_DATA_ADVERTISE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
BLEServiceDataAdvertiseTrigger
),
cv.Optional(CONF_MAC_ADDRESS): cv.mac_address,
cv.Required(CONF_SERVICE_UUID): bt_uuid,
}
),
cv.Optional(
CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE
): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
BLEManufacturerDataAdvertiseTrigger
),
cv.Optional(CONF_MAC_ADDRESS): cv.mac_address,
cv.Required(CONF_MANUFACTURER_ID): bt_uuid,
}
),
cv.Optional("scan_interval"): cv.invalid(
"This option has been removed in 1.14 (Reason: " "it never had an effect)"
),
}
).extend(cv.COMPONENT_SCHEMA)
cv.Optional('scan_interval'): cv.invalid("This option has been removed in 1.14 (Reason: "
"it never had an effect)"),
}).extend(cv.COMPONENT_SCHEMA)
ESP_BLE_DEVICE_SCHEMA = cv.Schema({
cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_id(ESP32BLETracker),
})
ESP_BLE_DEVICE_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_ESP32_BLE_ID): cv.use_id(ESP32BLETracker),
}
)
def to_code(config):
@ -135,7 +188,7 @@ def to_code(config):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
if CONF_MAC_ADDRESS in conf:
cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex))
yield automation.build_automation(trigger, [(ESPBTDeviceConstRef, 'x')], conf)
yield automation.build_automation(trigger, [(ESPBTDeviceConstRef, "x")], conf)
for conf in config.get(CONF_ON_BLE_SERVICE_DATA_ADVERTISE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
if len(conf[CONF_SERVICE_UUID]) == len(bt_uuid16_format):
@ -147,7 +200,7 @@ def to_code(config):
cg.add(trigger.set_service_uuid128(uuid128))
if CONF_MAC_ADDRESS in conf:
cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex))
yield automation.build_automation(trigger, [(adv_data_t_const_ref, 'x')], conf)
yield automation.build_automation(trigger, [(adv_data_t_const_ref, "x")], conf)
for conf in config.get(CONF_ON_BLE_MANUFACTURER_DATA_ADVERTISE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
if len(conf[CONF_MANUFACTURER_ID]) == len(bt_uuid16_format):
@ -159,7 +212,7 @@ def to_code(config):
cg.add(trigger.set_manufacturer_uuid128(uuid128))
if CONF_MAC_ADDRESS in conf:
cg.add(trigger.set_address(conf[CONF_MAC_ADDRESS].as_hex))
yield automation.build_automation(trigger, [(adv_data_t_const_ref, 'x')], conf)
yield automation.build_automation(trigger, [(adv_data_t_const_ref, "x")], conf)
@coroutine

View File

@ -1,105 +1,126 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.const import CONF_FREQUENCY, CONF_ID, CONF_NAME, CONF_PIN, CONF_SCL, CONF_SDA, \
ESP_PLATFORM_ESP32, CONF_DATA_PINS, CONF_RESET_PIN, CONF_RESOLUTION, CONF_BRIGHTNESS, \
CONF_CONTRAST
from esphome.const import (
CONF_FREQUENCY,
CONF_ID,
CONF_NAME,
CONF_PIN,
CONF_SCL,
CONF_SDA,
ESP_PLATFORM_ESP32,
CONF_DATA_PINS,
CONF_RESET_PIN,
CONF_RESOLUTION,
CONF_BRIGHTNESS,
CONF_CONTRAST,
)
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
DEPENDENCIES = ['api']
DEPENDENCIES = ["api"]
esp32_camera_ns = cg.esphome_ns.namespace('esp32_camera')
ESP32Camera = esp32_camera_ns.class_('ESP32Camera', cg.PollingComponent, cg.Nameable)
ESP32CameraFrameSize = esp32_camera_ns.enum('ESP32CameraFrameSize')
esp32_camera_ns = cg.esphome_ns.namespace("esp32_camera")
ESP32Camera = esp32_camera_ns.class_("ESP32Camera", cg.PollingComponent, cg.Nameable)
ESP32CameraFrameSize = esp32_camera_ns.enum("ESP32CameraFrameSize")
FRAME_SIZES = {
'160X120': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_160X120,
'QQVGA': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_160X120,
'128X160': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_128X160,
'QQVGA2': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_128X160,
'176X144': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_176X144,
'QCIF': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_176X144,
'240X176': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_240X176,
'HQVGA': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_240X176,
'320X240': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_320X240,
'QVGA': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_320X240,
'400X296': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_400X296,
'CIF': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_400X296,
'640X480': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_640X480,
'VGA': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_640X480,
'800X600': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_800X600,
'SVGA': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_800X600,
'1024X768': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1024X768,
'XGA': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1024X768,
'1280X1024': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1280X1024,
'SXGA': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1280X1024,
'1600X1200': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1600X1200,
'UXGA': ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1600X1200,
"160X120": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_160X120,
"QQVGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_160X120,
"128X160": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_128X160,
"QQVGA2": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_128X160,
"176X144": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_176X144,
"QCIF": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_176X144,
"240X176": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_240X176,
"HQVGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_240X176,
"320X240": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_320X240,
"QVGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_320X240,
"400X296": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_400X296,
"CIF": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_400X296,
"640X480": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_640X480,
"VGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_640X480,
"800X600": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_800X600,
"SVGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_800X600,
"1024X768": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1024X768,
"XGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1024X768,
"1280X1024": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1280X1024,
"SXGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1280X1024,
"1600X1200": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1600X1200,
"UXGA": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_1600X1200,
}
CONF_VSYNC_PIN = 'vsync_pin'
CONF_HREF_PIN = 'href_pin'
CONF_PIXEL_CLOCK_PIN = 'pixel_clock_pin'
CONF_EXTERNAL_CLOCK = 'external_clock'
CONF_I2C_PINS = 'i2c_pins'
CONF_POWER_DOWN_PIN = 'power_down_pin'
CONF_VSYNC_PIN = "vsync_pin"
CONF_HREF_PIN = "href_pin"
CONF_PIXEL_CLOCK_PIN = "pixel_clock_pin"
CONF_EXTERNAL_CLOCK = "external_clock"
CONF_I2C_PINS = "i2c_pins"
CONF_POWER_DOWN_PIN = "power_down_pin"
CONF_MAX_FRAMERATE = 'max_framerate'
CONF_IDLE_FRAMERATE = 'idle_framerate'
CONF_JPEG_QUALITY = 'jpeg_quality'
CONF_VERTICAL_FLIP = 'vertical_flip'
CONF_HORIZONTAL_MIRROR = 'horizontal_mirror'
CONF_SATURATION = 'saturation'
CONF_TEST_PATTERN = 'test_pattern'
CONF_MAX_FRAMERATE = "max_framerate"
CONF_IDLE_FRAMERATE = "idle_framerate"
CONF_JPEG_QUALITY = "jpeg_quality"
CONF_VERTICAL_FLIP = "vertical_flip"
CONF_HORIZONTAL_MIRROR = "horizontal_mirror"
CONF_SATURATION = "saturation"
CONF_TEST_PATTERN = "test_pattern"
camera_range_param = cv.int_range(min=-2, max=2)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(ESP32Camera),
cv.Required(CONF_NAME): cv.string,
cv.Required(CONF_DATA_PINS): cv.All([pins.input_pin], cv.Length(min=8, max=8)),
cv.Required(CONF_VSYNC_PIN): pins.input_pin,
cv.Required(CONF_HREF_PIN): pins.input_pin,
cv.Required(CONF_PIXEL_CLOCK_PIN): pins.input_pin,
cv.Required(CONF_EXTERNAL_CLOCK): cv.Schema({
cv.Required(CONF_PIN): pins.output_pin,
cv.Optional(CONF_FREQUENCY, default='20MHz'): cv.All(cv.frequency, cv.one_of(20e6, 10e6)),
}),
cv.Required(CONF_I2C_PINS): cv.Schema({
cv.Required(CONF_SDA): pins.output_pin,
cv.Required(CONF_SCL): pins.output_pin,
}),
cv.Optional(CONF_RESET_PIN): pins.output_pin,
cv.Optional(CONF_POWER_DOWN_PIN): pins.output_pin,
cv.Optional(CONF_MAX_FRAMERATE, default='10 fps'): cv.All(cv.framerate,
cv.Range(min=0, min_included=False,
max=60)),
cv.Optional(CONF_IDLE_FRAMERATE, default='0.1 fps'): cv.All(cv.framerate,
cv.Range(min=0, max=1)),
cv.Optional(CONF_RESOLUTION, default='640X480'): cv.enum(FRAME_SIZES, upper=True),
cv.Optional(CONF_JPEG_QUALITY, default=10): cv.int_range(min=10, max=63),
cv.Optional(CONF_CONTRAST, default=0): camera_range_param,
cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param,
cv.Optional(CONF_SATURATION, default=0): camera_range_param,
cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean,
cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean,
cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean,
}).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(ESP32Camera),
cv.Required(CONF_NAME): cv.string,
cv.Required(CONF_DATA_PINS): cv.All([pins.input_pin], cv.Length(min=8, max=8)),
cv.Required(CONF_VSYNC_PIN): pins.input_pin,
cv.Required(CONF_HREF_PIN): pins.input_pin,
cv.Required(CONF_PIXEL_CLOCK_PIN): pins.input_pin,
cv.Required(CONF_EXTERNAL_CLOCK): cv.Schema(
{
cv.Required(CONF_PIN): pins.output_pin,
cv.Optional(CONF_FREQUENCY, default="20MHz"): cv.All(
cv.frequency, cv.one_of(20e6, 10e6)
),
}
),
cv.Required(CONF_I2C_PINS): cv.Schema(
{
cv.Required(CONF_SDA): pins.output_pin,
cv.Required(CONF_SCL): pins.output_pin,
}
),
cv.Optional(CONF_RESET_PIN): pins.output_pin,
cv.Optional(CONF_POWER_DOWN_PIN): pins.output_pin,
cv.Optional(CONF_MAX_FRAMERATE, default="10 fps"): cv.All(
cv.framerate, cv.Range(min=0, min_included=False, max=60)
),
cv.Optional(CONF_IDLE_FRAMERATE, default="0.1 fps"): cv.All(
cv.framerate, cv.Range(min=0, max=1)
),
cv.Optional(CONF_RESOLUTION, default="640X480"): cv.enum(
FRAME_SIZES, upper=True
),
cv.Optional(CONF_JPEG_QUALITY, default=10): cv.int_range(min=10, max=63),
cv.Optional(CONF_CONTRAST, default=0): camera_range_param,
cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param,
cv.Optional(CONF_SATURATION, default=0): camera_range_param,
cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean,
cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean,
cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean,
}
).extend(cv.COMPONENT_SCHEMA)
SETTERS = {
CONF_DATA_PINS: 'set_data_pins',
CONF_VSYNC_PIN: 'set_vsync_pin',
CONF_HREF_PIN: 'set_href_pin',
CONF_PIXEL_CLOCK_PIN: 'set_pixel_clock_pin',
CONF_RESET_PIN: 'set_reset_pin',
CONF_POWER_DOWN_PIN: 'set_power_down_pin',
CONF_JPEG_QUALITY: 'set_jpeg_quality',
CONF_VERTICAL_FLIP: 'set_vertical_flip',
CONF_HORIZONTAL_MIRROR: 'set_horizontal_mirror',
CONF_CONTRAST: 'set_contrast',
CONF_BRIGHTNESS: 'set_brightness',
CONF_SATURATION: 'set_saturation',
CONF_TEST_PATTERN: 'set_test_pattern',
CONF_DATA_PINS: "set_data_pins",
CONF_VSYNC_PIN: "set_vsync_pin",
CONF_HREF_PIN: "set_href_pin",
CONF_PIXEL_CLOCK_PIN: "set_pixel_clock_pin",
CONF_RESET_PIN: "set_reset_pin",
CONF_POWER_DOWN_PIN: "set_power_down_pin",
CONF_JPEG_QUALITY: "set_jpeg_quality",
CONF_VERTICAL_FLIP: "set_vertical_flip",
CONF_HORIZONTAL_MIRROR: "set_horizontal_mirror",
CONF_CONTRAST: "set_contrast",
CONF_BRIGHTNESS: "set_brightness",
CONF_SATURATION: "set_saturation",
CONF_TEST_PATTERN: "set_test_pattern",
}
@ -122,5 +143,5 @@ def to_code(config):
cg.add(var.set_idle_update_interval(1000 / config[CONF_IDLE_FRAMERATE]))
cg.add(var.set_frame_size(config[CONF_RESOLUTION]))
cg.add_define('USE_ESP32_CAMERA')
cg.add_build_flag('-DBOARD_HAS_PSRAM')
cg.add_define("USE_ESP32_CAMERA")
cg.add_build_flag("-DBOARD_HAS_PSRAM")

View File

@ -13,13 +13,17 @@ def valid_dac_pin(value):
return value
esp32_dac_ns = cg.esphome_ns.namespace('esp32_dac')
ESP32DAC = esp32_dac_ns.class_('ESP32DAC', output.FloatOutput, cg.Component)
esp32_dac_ns = cg.esphome_ns.namespace("esp32_dac")
ESP32DAC = esp32_dac_ns.class_("ESP32DAC", output.FloatOutput, cg.Component)
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({
cv.Required(CONF_ID): cv.declare_id(ESP32DAC),
cv.Required(CONF_PIN): cv.All(pins.internal_gpio_output_pin_schema, valid_dac_pin),
}).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
{
cv.Required(CONF_ID): cv.declare_id(ESP32DAC),
cv.Required(CONF_PIN): cv.All(
pins.internal_gpio_output_pin_schema, valid_dac_pin
),
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config):

View File

@ -1,16 +1,30 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import CONF_ID, ESP_PLATFORM_ESP32, UNIT_MICROTESLA, ICON_MAGNET
from esphome.const import (
CONF_ID,
DEVICE_CLASS_EMPTY,
ESP_PLATFORM_ESP32,
UNIT_MICROTESLA,
ICON_MAGNET,
)
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
esp32_hall_ns = cg.esphome_ns.namespace('esp32_hall')
ESP32HallSensor = esp32_hall_ns.class_('ESP32HallSensor', sensor.Sensor, cg.PollingComponent)
esp32_hall_ns = cg.esphome_ns.namespace("esp32_hall")
ESP32HallSensor = esp32_hall_ns.class_(
"ESP32HallSensor", sensor.Sensor, cg.PollingComponent
)
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_MICROTESLA, ICON_MAGNET, 1).extend({
cv.GenerateID(): cv.declare_id(ESP32HallSensor),
}).extend(cv.polling_component_schema('60s'))
CONFIG_SCHEMA = (
sensor.sensor_schema(UNIT_MICROTESLA, ICON_MAGNET, 1, DEVICE_CLASS_EMPTY)
.extend(
{
cv.GenerateID(): cv.declare_id(ESP32HallSensor),
}
)
.extend(cv.polling_component_schema("60s"))
)
def to_code(config):

View File

@ -1,15 +1,23 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.const import CONF_HIGH_VOLTAGE_REFERENCE, CONF_ID, CONF_IIR_FILTER, \
CONF_LOW_VOLTAGE_REFERENCE, CONF_MEASUREMENT_DURATION, CONF_SETUP_MODE, CONF_SLEEP_DURATION, \
CONF_VOLTAGE_ATTENUATION, ESP_PLATFORM_ESP32
from esphome.const import (
CONF_HIGH_VOLTAGE_REFERENCE,
CONF_ID,
CONF_IIR_FILTER,
CONF_LOW_VOLTAGE_REFERENCE,
CONF_MEASUREMENT_DURATION,
CONF_SETUP_MODE,
CONF_SLEEP_DURATION,
CONF_VOLTAGE_ATTENUATION,
ESP_PLATFORM_ESP32,
)
from esphome.core import TimePeriod
AUTO_LOAD = ['binary_sensor']
AUTO_LOAD = ["binary_sensor"]
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
esp32_touch_ns = cg.esphome_ns.namespace('esp32_touch')
ESP32TouchComponent = esp32_touch_ns.class_('ESP32TouchComponent', cg.Component)
esp32_touch_ns = cg.esphome_ns.namespace("esp32_touch")
ESP32TouchComponent = esp32_touch_ns.class_("ESP32TouchComponent", cg.Component)
def validate_voltage(values):
@ -17,46 +25,56 @@ def validate_voltage(values):
if isinstance(value, float) and value.is_integer():
value = int(value)
value = cv.string(value)
if not value.endswith('V'):
value += 'V'
if not value.endswith("V"):
value += "V"
return cv.one_of(*values)(value)
return validator
LOW_VOLTAGE_REFERENCE = {
'0.5V': cg.global_ns.TOUCH_LVOLT_0V5,
'0.6V': cg.global_ns.TOUCH_LVOLT_0V6,
'0.7V': cg.global_ns.TOUCH_LVOLT_0V7,
'0.8V': cg.global_ns.TOUCH_LVOLT_0V8,
"0.5V": cg.global_ns.TOUCH_LVOLT_0V5,
"0.6V": cg.global_ns.TOUCH_LVOLT_0V6,
"0.7V": cg.global_ns.TOUCH_LVOLT_0V7,
"0.8V": cg.global_ns.TOUCH_LVOLT_0V8,
}
HIGH_VOLTAGE_REFERENCE = {
'2.4V': cg.global_ns.TOUCH_HVOLT_2V4,
'2.5V': cg.global_ns.TOUCH_HVOLT_2V5,
'2.6V': cg.global_ns.TOUCH_HVOLT_2V6,
'2.7V': cg.global_ns.TOUCH_HVOLT_2V7,
"2.4V": cg.global_ns.TOUCH_HVOLT_2V4,
"2.5V": cg.global_ns.TOUCH_HVOLT_2V5,
"2.6V": cg.global_ns.TOUCH_HVOLT_2V6,
"2.7V": cg.global_ns.TOUCH_HVOLT_2V7,
}
VOLTAGE_ATTENUATION = {
'1.5V': cg.global_ns.TOUCH_HVOLT_ATTEN_1V5,
'1V': cg.global_ns.TOUCH_HVOLT_ATTEN_1V,
'0.5V': cg.global_ns.TOUCH_HVOLT_ATTEN_0V5,
'0V': cg.global_ns.TOUCH_HVOLT_ATTEN_0V,
"1.5V": cg.global_ns.TOUCH_HVOLT_ATTEN_1V5,
"1V": cg.global_ns.TOUCH_HVOLT_ATTEN_1V,
"0.5V": cg.global_ns.TOUCH_HVOLT_ATTEN_0V5,
"0V": cg.global_ns.TOUCH_HVOLT_ATTEN_0V,
}
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(ESP32TouchComponent),
cv.Optional(CONF_SETUP_MODE, default=False): cv.boolean,
cv.Optional(CONF_IIR_FILTER, default='0ms'): cv.positive_time_period_milliseconds,
cv.Optional(CONF_SLEEP_DURATION, default='27306us'):
cv.All(cv.positive_time_period, cv.Range(max=TimePeriod(microseconds=436906))),
cv.Optional(CONF_MEASUREMENT_DURATION, default='8192us'):
cv.All(cv.positive_time_period, cv.Range(max=TimePeriod(microseconds=8192))),
cv.Optional(CONF_LOW_VOLTAGE_REFERENCE, default='0.5V'):
validate_voltage(LOW_VOLTAGE_REFERENCE),
cv.Optional(CONF_HIGH_VOLTAGE_REFERENCE, default='2.7V'):
validate_voltage(HIGH_VOLTAGE_REFERENCE),
cv.Optional(CONF_VOLTAGE_ATTENUATION, default='0V'): validate_voltage(VOLTAGE_ATTENUATION),
}).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(ESP32TouchComponent),
cv.Optional(CONF_SETUP_MODE, default=False): cv.boolean,
cv.Optional(
CONF_IIR_FILTER, default="0ms"
): cv.positive_time_period_milliseconds,
cv.Optional(CONF_SLEEP_DURATION, default="27306us"): cv.All(
cv.positive_time_period, cv.Range(max=TimePeriod(microseconds=436906))
),
cv.Optional(CONF_MEASUREMENT_DURATION, default="8192us"): cv.All(
cv.positive_time_period, cv.Range(max=TimePeriod(microseconds=8192))
),
cv.Optional(CONF_LOW_VOLTAGE_REFERENCE, default="0.5V"): validate_voltage(
LOW_VOLTAGE_REFERENCE
),
cv.Optional(CONF_HIGH_VOLTAGE_REFERENCE, default="2.7V"): validate_voltage(
HIGH_VOLTAGE_REFERENCE
),
cv.Optional(CONF_VOLTAGE_ATTENUATION, default="0V"): validate_voltage(
VOLTAGE_ATTENUATION
),
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config):
@ -69,12 +87,23 @@ def to_code(config):
sleep_duration = int(round(config[CONF_SLEEP_DURATION].total_microseconds * 0.15))
cg.add(touch.set_sleep_duration(sleep_duration))
measurement_duration = int(round(config[CONF_MEASUREMENT_DURATION].total_microseconds *
7.99987793))
measurement_duration = int(
round(config[CONF_MEASUREMENT_DURATION].total_microseconds * 7.99987793)
)
cg.add(touch.set_measurement_duration(measurement_duration))
cg.add(touch.set_low_voltage_reference(
LOW_VOLTAGE_REFERENCE[config[CONF_LOW_VOLTAGE_REFERENCE]]))
cg.add(touch.set_high_voltage_reference(
HIGH_VOLTAGE_REFERENCE[config[CONF_HIGH_VOLTAGE_REFERENCE]]))
cg.add(touch.set_voltage_attenuation(VOLTAGE_ATTENUATION[config[CONF_VOLTAGE_ATTENUATION]]))
cg.add(
touch.set_low_voltage_reference(
LOW_VOLTAGE_REFERENCE[config[CONF_LOW_VOLTAGE_REFERENCE]]
)
)
cg.add(
touch.set_high_voltage_reference(
HIGH_VOLTAGE_REFERENCE[config[CONF_HIGH_VOLTAGE_REFERENCE]]
)
)
cg.add(
touch.set_voltage_attenuation(
VOLTAGE_ATTENUATION[config[CONF_VOLTAGE_ATTENUATION]]
)
)

View File

@ -1,14 +1,20 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor
from esphome.const import CONF_NAME, CONF_PIN, CONF_THRESHOLD, ESP_PLATFORM_ESP32, CONF_ID
from esphome.const import (
CONF_NAME,
CONF_PIN,
CONF_THRESHOLD,
ESP_PLATFORM_ESP32,
CONF_ID,
)
from esphome.pins import validate_gpio_pin
from . import esp32_touch_ns, ESP32TouchComponent
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
DEPENDENCIES = ['esp32_touch']
DEPENDENCIES = ["esp32_touch"]
CONF_ESP32_TOUCH_ID = 'esp32_touch_id'
CONF_ESP32_TOUCH_ID = "esp32_touch_id"
TOUCH_PADS = {
4: cg.global_ns.TOUCH_PAD_NUM0,
@ -31,19 +37,27 @@ def validate_touch_pad(value):
return value
ESP32TouchBinarySensor = esp32_touch_ns.class_('ESP32TouchBinarySensor', binary_sensor.BinarySensor)
ESP32TouchBinarySensor = esp32_touch_ns.class_(
"ESP32TouchBinarySensor", binary_sensor.BinarySensor
)
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(ESP32TouchBinarySensor),
cv.GenerateID(CONF_ESP32_TOUCH_ID): cv.use_id(ESP32TouchComponent),
cv.Required(CONF_PIN): validate_touch_pad,
cv.Required(CONF_THRESHOLD): cv.uint16_t,
})
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(ESP32TouchBinarySensor),
cv.GenerateID(CONF_ESP32_TOUCH_ID): cv.use_id(ESP32TouchComponent),
cv.Required(CONF_PIN): validate_touch_pad,
cv.Required(CONF_THRESHOLD): cv.uint16_t,
}
)
def to_code(config):
hub = yield cg.get_variable(config[CONF_ESP32_TOUCH_ID])
var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], TOUCH_PADS[config[CONF_PIN]],
config[CONF_THRESHOLD])
var = cg.new_Pvariable(
config[CONF_ID],
config[CONF_NAME],
TOUCH_PADS[config[CONF_PIN]],
config[CONF_THRESHOLD],
)
yield binary_sensor.register_binary_sensor(var, config)
cg.add(hub.register_touch_pad(var))

View File

@ -2,7 +2,13 @@ from esphome import pins, automation
from esphome.components import output
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.const import CONF_FREQUENCY, CONF_ID, CONF_NUMBER, CONF_PIN, ESP_PLATFORM_ESP8266
from esphome.const import (
CONF_FREQUENCY,
CONF_ID,
CONF_NUMBER,
CONF_PIN,
ESP_PLATFORM_ESP8266,
)
ESP_PLATFORMS = [ESP_PLATFORM_ESP8266]
@ -13,16 +19,20 @@ def valid_pwm_pin(value):
return value
esp8266_pwm_ns = cg.esphome_ns.namespace('esp8266_pwm')
ESP8266PWM = esp8266_pwm_ns.class_('ESP8266PWM', output.FloatOutput, cg.Component)
SetFrequencyAction = esp8266_pwm_ns.class_('SetFrequencyAction', automation.Action)
esp8266_pwm_ns = cg.esphome_ns.namespace("esp8266_pwm")
ESP8266PWM = esp8266_pwm_ns.class_("ESP8266PWM", output.FloatOutput, cg.Component)
SetFrequencyAction = esp8266_pwm_ns.class_("SetFrequencyAction", automation.Action)
validate_frequency = cv.All(cv.frequency, cv.Range(min=1.0e-6))
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({
cv.Required(CONF_ID): cv.declare_id(ESP8266PWM),
cv.Required(CONF_PIN): cv.All(pins.internal_gpio_output_pin_schema, valid_pwm_pin),
cv.Optional(CONF_FREQUENCY, default='1kHz'): validate_frequency,
}).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
{
cv.Required(CONF_ID): cv.declare_id(ESP8266PWM),
cv.Required(CONF_PIN): cv.All(
pins.internal_gpio_output_pin_schema, valid_pwm_pin
),
cv.Optional(CONF_FREQUENCY, default="1kHz"): validate_frequency,
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config):
@ -36,10 +46,16 @@ def to_code(config):
cg.add(var.set_frequency(config[CONF_FREQUENCY]))
@automation.register_action('output.esp8266_pwm.set_frequency', SetFrequencyAction, cv.Schema({
cv.Required(CONF_ID): cv.use_id(ESP8266PWM),
cv.Required(CONF_FREQUENCY): cv.templatable(validate_frequency),
}))
@automation.register_action(
"output.esp8266_pwm.set_frequency",
SetFrequencyAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(ESP8266PWM),
cv.Required(CONF_FREQUENCY): cv.templatable(validate_frequency),
}
),
)
def esp8266_set_frequency_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)

View File

@ -1,47 +1,60 @@
from esphome import pins
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.const import CONF_DOMAIN, CONF_ID, CONF_MANUAL_IP, CONF_STATIC_IP, CONF_TYPE, \
CONF_USE_ADDRESS, ESP_PLATFORM_ESP32, CONF_GATEWAY, CONF_SUBNET, CONF_DNS1, CONF_DNS2
from esphome.const import (
CONF_DOMAIN,
CONF_ID,
CONF_MANUAL_IP,
CONF_STATIC_IP,
CONF_TYPE,
CONF_USE_ADDRESS,
ESP_PLATFORM_ESP32,
CONF_GATEWAY,
CONF_SUBNET,
CONF_DNS1,
CONF_DNS2,
)
from esphome.core import CORE, coroutine_with_priority
CONFLICTS_WITH = ['wifi']
CONFLICTS_WITH = ["wifi"]
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
AUTO_LOAD = ['network']
AUTO_LOAD = ["network"]
ethernet_ns = cg.esphome_ns.namespace('ethernet')
CONF_PHY_ADDR = 'phy_addr'
CONF_MDC_PIN = 'mdc_pin'
CONF_MDIO_PIN = 'mdio_pin'
CONF_CLK_MODE = 'clk_mode'
CONF_POWER_PIN = 'power_pin'
ethernet_ns = cg.esphome_ns.namespace("ethernet")
CONF_PHY_ADDR = "phy_addr"
CONF_MDC_PIN = "mdc_pin"
CONF_MDIO_PIN = "mdio_pin"
CONF_CLK_MODE = "clk_mode"
CONF_POWER_PIN = "power_pin"
EthernetType = ethernet_ns.enum('EthernetType')
EthernetType = ethernet_ns.enum("EthernetType")
ETHERNET_TYPES = {
'LAN8720': EthernetType.ETHERNET_TYPE_LAN8720,
'TLK110': EthernetType.ETHERNET_TYPE_TLK110,
"LAN8720": EthernetType.ETHERNET_TYPE_LAN8720,
"TLK110": EthernetType.ETHERNET_TYPE_TLK110,
}
eth_clock_mode_t = cg.global_ns.enum('eth_clock_mode_t')
eth_clock_mode_t = cg.global_ns.enum("eth_clock_mode_t")
CLK_MODES = {
'GPIO0_IN': eth_clock_mode_t.ETH_CLOCK_GPIO0_IN,
'GPIO0_OUT': eth_clock_mode_t.ETH_CLOCK_GPIO0_OUT,
'GPIO16_OUT': eth_clock_mode_t.ETH_CLOCK_GPIO16_OUT,
'GPIO17_OUT': eth_clock_mode_t.ETH_CLOCK_GPIO17_OUT,
"GPIO0_IN": eth_clock_mode_t.ETH_CLOCK_GPIO0_IN,
"GPIO0_OUT": eth_clock_mode_t.ETH_CLOCK_GPIO0_OUT,
"GPIO16_OUT": eth_clock_mode_t.ETH_CLOCK_GPIO16_OUT,
"GPIO17_OUT": eth_clock_mode_t.ETH_CLOCK_GPIO17_OUT,
}
MANUAL_IP_SCHEMA = cv.Schema({
cv.Required(CONF_STATIC_IP): cv.ipv4,
cv.Required(CONF_GATEWAY): cv.ipv4,
cv.Required(CONF_SUBNET): cv.ipv4,
cv.Optional(CONF_DNS1, default="0.0.0.0"): cv.ipv4,
cv.Optional(CONF_DNS2, default="0.0.0.0"): cv.ipv4,
})
MANUAL_IP_SCHEMA = cv.Schema(
{
cv.Required(CONF_STATIC_IP): cv.ipv4,
cv.Required(CONF_GATEWAY): cv.ipv4,
cv.Required(CONF_SUBNET): cv.ipv4,
cv.Optional(CONF_DNS1, default="0.0.0.0"): cv.ipv4,
cv.Optional(CONF_DNS2, default="0.0.0.0"): cv.ipv4,
}
)
EthernetComponent = ethernet_ns.class_('EthernetComponent', cg.Component)
IPAddress = cg.global_ns.class_('IPAddress')
ManualIP = ethernet_ns.struct('ManualIP')
EthernetComponent = ethernet_ns.class_("EthernetComponent", cg.Component)
IPAddress = cg.global_ns.class_("IPAddress")
ManualIP = ethernet_ns.struct("ManualIP")
def validate(config):
@ -54,30 +67,38 @@ def validate(config):
return config
CONFIG_SCHEMA = cv.All(cv.Schema({
cv.GenerateID(): cv.declare_id(EthernetComponent),
cv.Required(CONF_TYPE): cv.enum(ETHERNET_TYPES, upper=True),
cv.Required(CONF_MDC_PIN): pins.output_pin,
cv.Required(CONF_MDIO_PIN): pins.input_output_pin,
cv.Optional(CONF_CLK_MODE, default='GPIO0_IN'): cv.enum(CLK_MODES, upper=True, space='_'),
cv.Optional(CONF_PHY_ADDR, default=0): cv.int_range(min=0, max=31),
cv.Optional(CONF_POWER_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_MANUAL_IP): MANUAL_IP_SCHEMA,
cv.Optional(CONF_DOMAIN, default='.local'): cv.domain_name,
cv.Optional(CONF_USE_ADDRESS): cv.string_strict,
cv.Optional('hostname'): cv.invalid("The hostname option has been removed in 1.11.0"),
}).extend(cv.COMPONENT_SCHEMA), validate)
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(EthernetComponent),
cv.Required(CONF_TYPE): cv.enum(ETHERNET_TYPES, upper=True),
cv.Required(CONF_MDC_PIN): pins.output_pin,
cv.Required(CONF_MDIO_PIN): pins.input_output_pin,
cv.Optional(CONF_CLK_MODE, default="GPIO0_IN"): cv.enum(
CLK_MODES, upper=True, space="_"
),
cv.Optional(CONF_PHY_ADDR, default=0): cv.int_range(min=0, max=31),
cv.Optional(CONF_POWER_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_MANUAL_IP): MANUAL_IP_SCHEMA,
cv.Optional(CONF_DOMAIN, default=".local"): cv.domain_name,
cv.Optional(CONF_USE_ADDRESS): cv.string_strict,
cv.Optional("hostname"): cv.invalid(
"The hostname option has been removed in 1.11.0"
),
}
).extend(cv.COMPONENT_SCHEMA),
validate,
)
def manual_ip(config):
return cg.StructInitializer(
ManualIP,
('static_ip', IPAddress(*config[CONF_STATIC_IP].args)),
('gateway', IPAddress(*config[CONF_GATEWAY].args)),
('subnet', IPAddress(*config[CONF_SUBNET].args)),
('dns1', IPAddress(*config[CONF_DNS1].args)),
('dns2', IPAddress(*config[CONF_DNS2].args)),
("static_ip", IPAddress(*config[CONF_STATIC_IP].args)),
("gateway", IPAddress(*config[CONF_GATEWAY].args)),
("subnet", IPAddress(*config[CONF_SUBNET].args)),
("dns1", IPAddress(*config[CONF_DNS1].args)),
("dns2", IPAddress(*config[CONF_DNS2].args)),
)
@ -100,4 +121,4 @@ def to_code(config):
if CONF_MANUAL_IP in config:
cg.add(var.set_manual_ip(manual_ip(config[CONF_MANUAL_IP])))
cg.add_define('USE_ETHERNET')
cg.add_define("USE_ETHERNET")

View File

@ -4,26 +4,36 @@ import esphome.config_validation as cv
from esphome.components import esp32_ble_tracker
from esphome.const import CONF_TRIGGER_ID
CODEOWNERS = ['@OttoWinter']
DEPENDENCIES = ['esp32_ble_tracker']
CODEOWNERS = ["@OttoWinter"]
DEPENDENCIES = ["esp32_ble_tracker"]
exposure_notifications_ns = cg.esphome_ns.namespace('exposure_notifications')
ExposureNotification = exposure_notifications_ns.struct('ExposureNotification')
exposure_notifications_ns = cg.esphome_ns.namespace("exposure_notifications")
ExposureNotification = exposure_notifications_ns.struct("ExposureNotification")
ExposureNotificationTrigger = exposure_notifications_ns.class_(
'ExposureNotificationTrigger', esp32_ble_tracker.ESPBTDeviceListener,
automation.Trigger.template(ExposureNotification))
"ExposureNotificationTrigger",
esp32_ble_tracker.ESPBTDeviceListener,
automation.Trigger.template(ExposureNotification),
)
CONF_ON_EXPOSURE_NOTIFICATION = 'on_exposure_notification'
CONF_ON_EXPOSURE_NOTIFICATION = "on_exposure_notification"
CONFIG_SCHEMA = cv.Schema({
cv.Required(CONF_ON_EXPOSURE_NOTIFICATION): automation.validate_automation(cv.Schema({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ExposureNotificationTrigger),
}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)),
})
CONFIG_SCHEMA = cv.Schema(
{
cv.Required(CONF_ON_EXPOSURE_NOTIFICATION): automation.validate_automation(
cv.Schema(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
ExposureNotificationTrigger
),
}
).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
),
}
)
def to_code(config):
for conf in config.get(CONF_ON_EXPOSURE_NOTIFICATION, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
yield automation.build_automation(trigger, [(ExposureNotification, 'x')], conf)
yield automation.build_automation(trigger, [(ExposureNotification, "x")], conf)
yield esp32_ble_tracker.register_ble_device(trigger, conf)

View File

@ -3,17 +3,25 @@ import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import CONF_ID
CODEOWNERS = ['@ssieb']
CODEOWNERS = ["@ssieb"]
DEPENDENCIES = ['i2c']
DEPENDENCIES = ["i2c"]
ezo_ns = cg.esphome_ns.namespace('ezo')
ezo_ns = cg.esphome_ns.namespace("ezo")
EZOSensor = ezo_ns.class_('EZOSensor', sensor.Sensor, cg.PollingComponent, i2c.I2CDevice)
EZOSensor = ezo_ns.class_(
"EZOSensor", sensor.Sensor, cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(EZOSensor),
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(None))
CONFIG_SCHEMA = (
sensor.SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(EZOSensor),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(None))
)
def to_code(config):

View File

@ -3,42 +3,49 @@ import esphome.config_validation as cv
from esphome import automation
from esphome.automation import maybe_simple_id
from esphome.components import mqtt
from esphome.const import CONF_ID, CONF_INTERNAL, CONF_MQTT_ID, CONF_OSCILLATING, \
CONF_OSCILLATION_COMMAND_TOPIC, CONF_OSCILLATION_STATE_TOPIC, CONF_SPEED, \
CONF_SPEED_COMMAND_TOPIC, CONF_SPEED_STATE_TOPIC, CONF_NAME
from esphome.const import (
CONF_ID,
CONF_INTERNAL,
CONF_MQTT_ID,
CONF_OSCILLATING,
CONF_OSCILLATION_COMMAND_TOPIC,
CONF_OSCILLATION_STATE_TOPIC,
CONF_SPEED,
CONF_SPEED_COMMAND_TOPIC,
CONF_SPEED_STATE_TOPIC,
CONF_NAME,
)
from esphome.core import CORE, coroutine, coroutine_with_priority
IS_PLATFORM_COMPONENT = True
fan_ns = cg.esphome_ns.namespace('fan')
FanState = fan_ns.class_('FanState', cg.Nameable, cg.Component)
MakeFan = cg.Application.struct('MakeFan')
fan_ns = cg.esphome_ns.namespace("fan")
FanState = fan_ns.class_("FanState", cg.Nameable, cg.Component)
MakeFan = cg.Application.struct("MakeFan")
# Actions
TurnOnAction = fan_ns.class_('TurnOnAction', automation.Action)
TurnOffAction = fan_ns.class_('TurnOffAction', automation.Action)
ToggleAction = fan_ns.class_('ToggleAction', automation.Action)
TurnOnAction = fan_ns.class_("TurnOnAction", automation.Action)
TurnOffAction = fan_ns.class_("TurnOffAction", automation.Action)
ToggleAction = fan_ns.class_("ToggleAction", automation.Action)
FanSpeed = fan_ns.enum('FanSpeed')
FAN_SPEEDS = {
'OFF': FanSpeed.FAN_SPEED_OFF,
'LOW': FanSpeed.FAN_SPEED_LOW,
'MEDIUM': FanSpeed.FAN_SPEED_MEDIUM,
'HIGH': FanSpeed.FAN_SPEED_HIGH,
}
FAN_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(FanState),
cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTFanComponent),
cv.Optional(CONF_OSCILLATION_STATE_TOPIC): cv.All(cv.requires_component('mqtt'),
cv.publish_topic),
cv.Optional(CONF_OSCILLATION_COMMAND_TOPIC): cv.All(cv.requires_component('mqtt'),
cv.subscribe_topic),
cv.Optional(CONF_SPEED_STATE_TOPIC): cv.All(cv.requires_component('mqtt'),
cv.publish_topic),
cv.Optional(CONF_SPEED_COMMAND_TOPIC): cv.All(cv.requires_component('mqtt'),
cv.subscribe_topic),
})
FAN_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(FanState),
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTFanComponent),
cv.Optional(CONF_OSCILLATION_STATE_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.publish_topic
),
cv.Optional(CONF_OSCILLATION_COMMAND_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.subscribe_topic
),
cv.Optional(CONF_SPEED_STATE_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.publish_topic
),
cv.Optional(CONF_SPEED_COMMAND_TOPIC): cv.All(
cv.requires_component("mqtt"), cv.subscribe_topic
),
}
)
@coroutine
@ -52,14 +59,23 @@ def setup_fan_core_(var, config):
yield mqtt.register_mqtt_component(mqtt_, config)
if CONF_OSCILLATION_STATE_TOPIC in config:
cg.add(mqtt_.set_custom_oscillation_state_topic(config[CONF_OSCILLATION_STATE_TOPIC]))
cg.add(
mqtt_.set_custom_oscillation_state_topic(
config[CONF_OSCILLATION_STATE_TOPIC]
)
)
if CONF_OSCILLATION_COMMAND_TOPIC in config:
cg.add(mqtt_.set_custom_oscillation_command_topic(
config[CONF_OSCILLATION_COMMAND_TOPIC]))
cg.add(
mqtt_.set_custom_oscillation_command_topic(
config[CONF_OSCILLATION_COMMAND_TOPIC]
)
)
if CONF_SPEED_STATE_TOPIC in config:
cg.add(mqtt_.set_custom_speed_state_topic(config[CONF_SPEED_STATE_TOPIC]))
if CONF_SPEED_COMMAND_TOPIC in config:
cg.add(mqtt_.set_custom_speed_command_topic(config[CONF_SPEED_COMMAND_TOPIC]))
cg.add(
mqtt_.set_custom_speed_command_topic(config[CONF_SPEED_COMMAND_TOPIC])
)
@coroutine
@ -78,28 +94,36 @@ def create_fan_state(config):
yield var
FAN_ACTION_SCHEMA = maybe_simple_id({
cv.Required(CONF_ID): cv.use_id(FanState),
})
FAN_ACTION_SCHEMA = maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(FanState),
}
)
@automation.register_action('fan.toggle', ToggleAction, FAN_ACTION_SCHEMA)
@automation.register_action("fan.toggle", ToggleAction, FAN_ACTION_SCHEMA)
def fan_toggle_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_action('fan.turn_off', TurnOffAction, FAN_ACTION_SCHEMA)
@automation.register_action("fan.turn_off", TurnOffAction, FAN_ACTION_SCHEMA)
def fan_turn_off_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(action_id, template_arg, paren)
@automation.register_action('fan.turn_on', TurnOnAction, maybe_simple_id({
cv.Required(CONF_ID): cv.use_id(FanState),
cv.Optional(CONF_OSCILLATING): cv.templatable(cv.boolean),
cv.Optional(CONF_SPEED): cv.templatable(cv.enum(FAN_SPEEDS, upper=True)),
}))
@automation.register_action(
"fan.turn_on",
TurnOnAction,
maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(FanState),
cv.Optional(CONF_OSCILLATING): cv.templatable(cv.boolean),
cv.Optional(CONF_SPEED): cv.templatable(cv.int_range(1)),
}
),
)
def fan_turn_on_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
@ -107,12 +131,12 @@ def fan_turn_on_to_code(config, action_id, template_arg, args):
template_ = yield cg.templatable(config[CONF_OSCILLATING], args, bool)
cg.add(var.set_oscillating(template_))
if CONF_SPEED in config:
template_ = yield cg.templatable(config[CONF_SPEED], args, FanSpeed)
template_ = yield cg.templatable(config[CONF_SPEED], args, int)
cg.add(var.set_speed(template_))
yield var
@coroutine_with_priority(100.0)
def to_code(config):
cg.add_define('USE_FAN')
cg.add_define("USE_FAN")
cg.add_global(fan_ns.using)

View File

@ -12,7 +12,7 @@ template<typename... Ts> class TurnOnAction : public Action<Ts...> {
explicit TurnOnAction(FanState *state) : state_(state) {}
TEMPLATABLE_VALUE(bool, oscillating)
TEMPLATABLE_VALUE(FanSpeed, speed)
TEMPLATABLE_VALUE(int, speed)
void play(Ts... x) override {
auto call = this->state_->turn_on();

View File

@ -0,0 +1,20 @@
#include <cassert>
#include "fan_helpers.h"
namespace esphome {
namespace fan {
FanSpeed speed_level_to_enum(int speed_level, int supported_speed_levels) {
const auto speed_ratio = static_cast<float>(speed_level) / (supported_speed_levels + 1);
const auto legacy_level = static_cast<int>(clamp(ceilf(speed_ratio * 3), 1, 3));
return static_cast<FanSpeed>(legacy_level - 1);
}
int speed_enum_to_level(FanSpeed speed, int supported_speed_levels) {
const auto enum_level = static_cast<int>(speed) + 1;
const auto speed_level = roundf(enum_level / 3.0f * supported_speed_levels);
return static_cast<int>(speed_level);
}
} // namespace fan
} // namespace esphome

View File

@ -0,0 +1,11 @@
#pragma once
#include "fan_state.h"
namespace esphome {
namespace fan {
FanSpeed speed_level_to_enum(int speed_level, int supported_speed_levels);
int speed_enum_to_level(FanSpeed speed, int supported_speed_levels);
} // namespace fan
} // namespace esphome

View File

@ -1,4 +1,5 @@
#include "fan_state.h"
#include "fan_helpers.h"
#include "esphome/core/log.h"
namespace esphome {
@ -20,7 +21,7 @@ FanStateCall FanState::make_call() { return FanStateCall(this); }
struct FanStateRTCState {
bool state;
FanSpeed speed;
int speed;
bool oscillating;
FanDirection direction;
};
@ -52,16 +53,8 @@ void FanStateCall::perform() const {
this->state_->direction = *this->direction_;
}
if (this->speed_.has_value()) {
switch (*this->speed_) {
case FAN_SPEED_LOW:
case FAN_SPEED_MEDIUM:
case FAN_SPEED_HIGH:
this->state_->speed = *this->speed_;
break;
default:
// protect from invalid input
break;
}
const int speed_count = this->state_->get_traits().supported_speed_count();
this->state_->speed = static_cast<int>(clamp(*this->speed_, 1, speed_count));
}
FanStateRTCState saved{};
@ -73,13 +66,15 @@ void FanStateCall::perform() const {
this->state_->state_callback_.call();
}
FanStateCall &FanStateCall::set_speed(const char *speed) {
if (strcasecmp(speed, "low") == 0) {
this->set_speed(FAN_SPEED_LOW);
} else if (strcasecmp(speed, "medium") == 0) {
this->set_speed(FAN_SPEED_MEDIUM);
} else if (strcasecmp(speed, "high") == 0) {
this->set_speed(FAN_SPEED_HIGH);
FanStateCall &FanStateCall::set_speed(const char *legacy_speed) {
const auto supported_speed_count = this->state_->get_traits().supported_speed_count();
if (strcasecmp(legacy_speed, "low") == 0) {
this->set_speed(fan::speed_enum_to_level(FAN_SPEED_LOW, supported_speed_count));
} else if (strcasecmp(legacy_speed, "medium") == 0) {
this->set_speed(fan::speed_enum_to_level(FAN_SPEED_MEDIUM, supported_speed_count));
} else if (strcasecmp(legacy_speed, "high") == 0) {
this->set_speed(fan::speed_enum_to_level(FAN_SPEED_HIGH, supported_speed_count));
}
return *this;
}

View File

@ -3,12 +3,13 @@
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/core/preferences.h"
#include "esphome/core/log.h"
#include "fan_traits.h"
namespace esphome {
namespace fan {
/// Simple enum to represent the speed of a fan.
/// Simple enum to represent the speed of a fan. - DEPRECATED - Will be deleted soon
enum FanSpeed {
FAN_SPEED_LOW = 0, ///< The fan is running on low speed.
FAN_SPEED_MEDIUM = 1, ///< The fan is running on medium speed.
@ -40,15 +41,11 @@ class FanStateCall {
this->oscillating_ = oscillating;
return *this;
}
FanStateCall &set_speed(FanSpeed speed) {
FanStateCall &set_speed(int speed) {
this->speed_ = speed;
return *this;
}
FanStateCall &set_speed(optional<FanSpeed> speed) {
this->speed_ = speed;
return *this;
}
FanStateCall &set_speed(const char *speed);
FanStateCall &set_speed(const char *legacy_speed);
FanStateCall &set_direction(FanDirection direction) {
this->direction_ = direction;
return *this;
@ -63,8 +60,8 @@ class FanStateCall {
protected:
FanState *const state_;
optional<bool> binary_state_;
optional<bool> oscillating_{};
optional<FanSpeed> speed_{};
optional<bool> oscillating_;
optional<int> speed_;
optional<FanDirection> direction_{};
};
@ -86,8 +83,8 @@ class FanState : public Nameable, public Component {
bool state{false};
/// The current oscillation state of the fan.
bool oscillating{false};
/// The current fan speed.
FanSpeed speed{FAN_SPEED_HIGH};
/// The current fan speed level
int speed{};
/// The current direction of the fan
FanDirection direction{FAN_DIRECTION_FORWARD};

View File

@ -6,8 +6,8 @@ namespace fan {
class FanTraits {
public:
FanTraits() = default;
FanTraits(bool oscillation, bool speed, bool direction)
: oscillation_(oscillation), speed_(speed), direction_(direction) {}
FanTraits(bool oscillation, bool speed, bool direction, int speed_count)
: oscillation_(oscillation), speed_(speed), direction_(direction), speed_count_(speed_count) {}
/// Return if this fan supports oscillation.
bool supports_oscillation() const { return this->oscillation_; }
@ -15,8 +15,12 @@ class FanTraits {
void set_oscillation(bool oscillation) { this->oscillation_ = oscillation; }
/// Return if this fan supports speed modes.
bool supports_speed() const { return this->speed_; }
/// Set whether this fan supports speed modes.
/// Set whether this fan supports speed levels.
void set_speed(bool speed) { this->speed_ = speed; }
/// Return how many speed levels the fan has
int supported_speed_count() const { return this->speed_count_; }
/// Set how many speed levels this fan has.
void set_supported_speed_count(int speed_count) { this->speed_count_ = speed_count; }
/// Return if this fan supports changing direction
bool supports_direction() const { return this->direction_; }
/// Set whether this fan supports changing direction
@ -26,6 +30,7 @@ class FanTraits {
bool oscillation_{false};
bool speed_{false};
bool direction_{false};
int speed_count_{};
};
} // namespace fan

View File

@ -1,29 +1,37 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import light
from esphome.const import CONF_OUTPUT_ID, CONF_NUM_LEDS, CONF_RGB_ORDER, CONF_MAX_REFRESH_RATE
from esphome.const import (
CONF_OUTPUT_ID,
CONF_NUM_LEDS,
CONF_RGB_ORDER,
CONF_MAX_REFRESH_RATE,
)
from esphome.core import coroutine
CODEOWNERS = ['@OttoWinter']
fastled_base_ns = cg.esphome_ns.namespace('fastled_base')
FastLEDLightOutput = fastled_base_ns.class_('FastLEDLightOutput', light.AddressableLight)
CODEOWNERS = ["@OttoWinter"]
fastled_base_ns = cg.esphome_ns.namespace("fastled_base")
FastLEDLightOutput = fastled_base_ns.class_(
"FastLEDLightOutput", light.AddressableLight
)
RGB_ORDERS = [
'RGB',
'RBG',
'GRB',
'GBR',
'BRG',
'BGR',
"RGB",
"RBG",
"GRB",
"GBR",
"BRG",
"BGR",
]
BASE_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend({
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(FastLEDLightOutput),
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
cv.Optional(CONF_RGB_ORDER): cv.one_of(*RGB_ORDERS, upper=True),
cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
}).extend(cv.COMPONENT_SCHEMA)
BASE_SCHEMA = light.ADDRESSABLE_LIGHT_SCHEMA.extend(
{
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(FastLEDLightOutput),
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
cv.Optional(CONF_RGB_ORDER): cv.one_of(*RGB_ORDERS, upper=True),
cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
}
).extend(cv.COMPONENT_SCHEMA)
@coroutine
@ -38,5 +46,5 @@ def new_fastled_light(config):
# https://github.com/FastLED/FastLED/blob/master/library.json
# 3.3.3 has an issue on ESP32 with RMT and fastled_clockless:
# https://github.com/esphome/issues/issues/1375
cg.add_library('FastLED', '3.3.2')
cg.add_library("FastLED", "3.3.2")
yield var

View File

@ -4,46 +4,51 @@ from esphome import pins
from esphome.components import fastled_base
from esphome.const import CONF_CHIPSET, CONF_NUM_LEDS, CONF_PIN, CONF_RGB_ORDER
AUTO_LOAD = ['fastled_base']
AUTO_LOAD = ["fastled_base"]
CHIPSETS = [
'NEOPIXEL',
'TM1829',
'TM1809',
'TM1804',
'TM1803',
'UCS1903',
'UCS1903B',
'UCS1904',
'UCS2903',
'WS2812',
'WS2852',
'WS2812B',
'SK6812',
'SK6822',
'APA106',
'PL9823',
'WS2811',
'WS2813',
'APA104',
'WS2811_400',
'GW6205',
'GW6205_400',
'LPD1886',
'LPD1886_8BIT',
"NEOPIXEL",
"TM1829",
"TM1809",
"TM1804",
"TM1803",
"UCS1903",
"UCS1903B",
"UCS1904",
"UCS2903",
"WS2812",
"WS2852",
"WS2812B",
"SK6812",
"SK6822",
"APA106",
"PL9823",
"WS2811",
"WS2813",
"APA104",
"WS2811_400",
"GW6205",
"GW6205_400",
"LPD1886",
"LPD1886_8BIT",
]
def validate(value):
if value[CONF_CHIPSET] == 'NEOPIXEL' and CONF_RGB_ORDER in value:
if value[CONF_CHIPSET] == "NEOPIXEL" and CONF_RGB_ORDER in value:
raise cv.Invalid("NEOPIXEL doesn't support RGB order")
return value
CONFIG_SCHEMA = cv.All(fastled_base.BASE_SCHEMA.extend({
cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True),
cv.Required(CONF_PIN): pins.output_pin,
}), validate)
CONFIG_SCHEMA = cv.All(
fastled_base.BASE_SCHEMA.extend(
{
cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True),
cv.Required(CONF_PIN): pins.output_pin,
}
),
validate,
)
def to_code(config):
@ -52,6 +57,7 @@ def to_code(config):
rgb_order = None
if CONF_RGB_ORDER in config:
rgb_order = cg.RawExpression(config[CONF_RGB_ORDER])
template_args = cg.TemplateArguments(cg.RawExpression(config[CONF_CHIPSET]),
config[CONF_PIN], rgb_order)
template_args = cg.TemplateArguments(
cg.RawExpression(config[CONF_CHIPSET]), config[CONF_PIN], rgb_order
)
cg.add(var.add_leds(template_args, config[CONF_NUM_LEDS]))

View File

@ -2,34 +2,44 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import fastled_base
from esphome.const import CONF_CHIPSET, CONF_CLOCK_PIN, CONF_DATA_PIN, CONF_DATA_RATE, \
CONF_NUM_LEDS, CONF_RGB_ORDER
from esphome.const import (
CONF_CHIPSET,
CONF_CLOCK_PIN,
CONF_DATA_PIN,
CONF_DATA_RATE,
CONF_NUM_LEDS,
CONF_RGB_ORDER,
)
AUTO_LOAD = ['fastled_base']
AUTO_LOAD = ["fastled_base"]
CHIPSETS = [
'LPD8806',
'WS2801',
'WS2803',
'SM16716',
'P9813',
'APA102',
'SK9822',
'DOTSTAR',
"LPD8806",
"WS2801",
"WS2803",
"SM16716",
"P9813",
"APA102",
"SK9822",
"DOTSTAR",
]
CONFIG_SCHEMA = fastled_base.BASE_SCHEMA.extend({
cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True),
cv.Required(CONF_DATA_PIN): pins.output_pin,
cv.Required(CONF_CLOCK_PIN): pins.output_pin,
cv.Optional(CONF_DATA_RATE): cv.frequency,
})
CONFIG_SCHEMA = fastled_base.BASE_SCHEMA.extend(
{
cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True),
cv.Required(CONF_DATA_PIN): pins.output_pin,
cv.Required(CONF_CLOCK_PIN): pins.output_pin,
cv.Optional(CONF_DATA_RATE): cv.frequency,
}
)
def to_code(config):
var = yield fastled_base.new_fastled_light(config)
rgb_order = cg.RawExpression(config[CONF_RGB_ORDER] if CONF_RGB_ORDER in config else "RGB")
rgb_order = cg.RawExpression(
config[CONF_RGB_ORDER] if CONF_RGB_ORDER in config else "RGB"
)
data_rate = None
if CONF_DATA_RATE in config:
@ -39,7 +49,11 @@ def to_code(config):
else:
data_rate_mhz = int(data_rate_khz / 1000)
data_rate = cg.RawExpression(f"DATA_RATE_MHZ({data_rate_mhz})")
template_args = cg.TemplateArguments(cg.RawExpression(config[CONF_CHIPSET]),
config[CONF_DATA_PIN], config[CONF_CLOCK_PIN], rgb_order,
data_rate)
template_args = cg.TemplateArguments(
cg.RawExpression(config[CONF_CHIPSET]),
config[CONF_DATA_PIN],
config[CONF_CLOCK_PIN],
rgb_order,
data_rate,
)
cg.add(var.add_leds(template_args, config[CONF_NUM_LEDS]))

View File

@ -7,11 +7,11 @@ import esphome.codegen as cg
from esphome.const import CONF_FILE, CONF_GLYPHS, CONF_ID, CONF_SIZE
from esphome.core import CORE, HexInt
DEPENDENCIES = ['display']
DEPENDENCIES = ["display"]
MULTI_CONF = True
Font = display.display_ns.class_('Font')
Glyph = display.display_ns.class_('Glyph')
Font = display.display_ns.class_("Font")
Glyph = display.display_ns.class_("Glyph")
def validate_glyphs(value):
@ -20,8 +20,8 @@ def validate_glyphs(value):
value = cv.Schema([cv.string])(list(value))
def comparator(x, y):
x_ = x.encode('utf-8')
y_ = y.encode('utf-8')
x_ = x.encode("utf-8")
y_ = y.encode("utf-8")
for c in range(min(len(x_), len(y_))):
if x_[c] < y_[c]:
@ -43,36 +43,48 @@ def validate_pillow_installed(value):
try:
import PIL
except ImportError as err:
raise cv.Invalid("Please install the pillow python package to use this feature. "
"(pip install pillow)") from err
raise cv.Invalid(
"Please install the pillow python package to use this feature. "
"(pip install pillow)"
) from err
if PIL.__version__[0] < '4':
raise cv.Invalid("Please update your pillow installation to at least 4.0.x. "
"(pip install -U pillow)")
if PIL.__version__[0] < "4":
raise cv.Invalid(
"Please update your pillow installation to at least 4.0.x. "
"(pip install -U pillow)"
)
return value
def validate_truetype_file(value):
if value.endswith('.zip'): # for Google Fonts downloads
raise cv.Invalid("Please unzip the font archive '{}' first and then use the .ttf files "
"inside.".format(value))
if not value.endswith('.ttf'):
raise cv.Invalid("Only truetype (.ttf) files are supported. Please make sure you're "
"using the correct format or rename the extension to .ttf")
if value.endswith(".zip"): # for Google Fonts downloads
raise cv.Invalid(
"Please unzip the font archive '{}' first and then use the .ttf files "
"inside.".format(value)
)
if not value.endswith(".ttf"):
raise cv.Invalid(
"Only truetype (.ttf) files are supported. Please make sure you're "
"using the correct format or rename the extension to .ttf"
)
return cv.file_(value)
DEFAULT_GLYPHS = ' !"%()+,-.:0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz°'
CONF_RAW_DATA_ID = 'raw_data_id'
DEFAULT_GLYPHS = (
' !"%()+,-.:0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz°'
)
CONF_RAW_DATA_ID = "raw_data_id"
FONT_SCHEMA = cv.Schema({
cv.Required(CONF_ID): cv.declare_id(Font),
cv.Required(CONF_FILE): validate_truetype_file,
cv.Optional(CONF_GLYPHS, default=DEFAULT_GLYPHS): validate_glyphs,
cv.Optional(CONF_SIZE, default=20): cv.int_range(min=1),
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
})
FONT_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.declare_id(Font),
cv.Required(CONF_FILE): validate_truetype_file,
cv.Optional(CONF_GLYPHS, default=DEFAULT_GLYPHS): validate_glyphs,
cv.Optional(CONF_SIZE, default=20): cv.int_range(min=1),
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
}
)
CONFIG_SCHEMA = cv.All(validate_pillow_installed, FONT_SCHEMA)
@ -91,7 +103,7 @@ def to_code(config):
glyph_args = {}
data = []
for glyph in config[CONF_GLYPHS]:
mask = font.getmask(glyph, mode='1')
mask = font.getmask(glyph, mode="1")
_, (offset_x, offset_y) = font.font.getsize(glyph)
width, height = mask.size
width8 = ((width + 7) // 8) * 8

View File

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

View File

@ -3,224 +3,207 @@
namespace esphome {
namespace fujitsu_general {
static const char *TAG = "fujitsu_general.climate";
// bytes' bits are reversed for fujitsu, so nibbles are ordered 1, 0, 3, 2, 5, 4, etc...
// Control packet
const uint16_t FUJITSU_GENERAL_STATE_LENGTH = 16;
#define SET_NIBBLE(message, nibble, value) (message[nibble / 2] |= (value & 0b00001111) << ((nibble % 2) ? 0 : 4))
#define GET_NIBBLE(message, nibble) ((message[nibble / 2] >> ((nibble % 2) ? 0 : 4)) & 0b00001111)
const uint8_t FUJITSU_GENERAL_BASE_BYTE0 = 0x14;
const uint8_t FUJITSU_GENERAL_BASE_BYTE1 = 0x63;
const uint8_t FUJITSU_GENERAL_BASE_BYTE2 = 0x00;
const uint8_t FUJITSU_GENERAL_BASE_BYTE3 = 0x10;
const uint8_t FUJITSU_GENERAL_BASE_BYTE4 = 0x10;
const uint8_t FUJITSU_GENERAL_BASE_BYTE5 = 0xFE;
const uint8_t FUJITSU_GENERAL_BASE_BYTE6 = 0x09;
const uint8_t FUJITSU_GENERAL_BASE_BYTE7 = 0x30;
static const char* TAG = "fujitsu_general.climate";
// Temperature and POWER ON
const uint8_t FUJITSU_GENERAL_POWER_ON_MASK_BYTE8 = 0b00000001;
const uint8_t FUJITSU_GENERAL_BASE_BYTE8 = 0x40;
// Common header
const uint8_t FUJITSU_GENERAL_COMMON_LENGTH = 6;
const uint8_t FUJITSU_GENERAL_COMMON_BYTE0 = 0x14;
const uint8_t FUJITSU_GENERAL_COMMON_BYTE1 = 0x63;
const uint8_t FUJITSU_GENERAL_COMMON_BYTE2 = 0x00;
const uint8_t FUJITSU_GENERAL_COMMON_BYTE3 = 0x10;
const uint8_t FUJITSU_GENERAL_COMMON_BYTE4 = 0x10;
const uint8_t FUJITSU_GENERAL_MESSAGE_TYPE_BYTE = 5;
// State message - temp & fan etc.
const uint8_t FUJITSU_GENERAL_STATE_MESSAGE_LENGTH = 16;
const uint8_t FUJITSU_GENERAL_MESSAGE_TYPE_STATE = 0xFE;
// Util messages - off & eco etc.
const uint8_t FUJITSU_GENERAL_UTIL_MESSAGE_LENGTH = 7;
const uint8_t FUJITSU_GENERAL_MESSAGE_TYPE_OFF = 0x02;
const uint8_t FUJITSU_GENERAL_MESSAGE_TYPE_ECONOMY = 0x09;
const uint8_t FUJITSU_GENERAL_MESSAGE_TYPE_NUDGE = 0x6C;
// State header
const uint8_t FUJITSU_GENERAL_STATE_HEADER_BYTE0 = 0x09;
const uint8_t FUJITSU_GENERAL_STATE_HEADER_BYTE1 = 0x30;
// State footer
const uint8_t FUJITSU_GENERAL_STATE_FOOTER_BYTE0 = 0x20;
// Temperature
const uint8_t FUJITSU_GENERAL_TEMPERATURE_NIBBLE = 16;
// Power on
const uint8_t FUJITSU_GENERAL_POWER_ON_NIBBLE = 17;
const uint8_t FUJITSU_GENERAL_POWER_ON = 0x01;
const uint8_t FUJITSU_GENERAL_POWER_OFF = 0x00;
// Mode
const uint8_t FUJITSU_GENERAL_MODE_AUTO_BYTE9 = 0x00;
const uint8_t FUJITSU_GENERAL_MODE_HEAT_BYTE9 = 0x04;
const uint8_t FUJITSU_GENERAL_MODE_COOL_BYTE9 = 0x01;
const uint8_t FUJITSU_GENERAL_MODE_DRY_BYTE9 = 0x02;
const uint8_t FUJITSU_GENERAL_MODE_FAN_BYTE9 = 0x03;
const uint8_t FUJITSU_GENERAL_MODE_10C_BYTE9 = 0x0B;
const uint8_t FUJITSU_GENERAL_BASE_BYTE9 = 0x01;
const uint8_t FUJITSU_GENERAL_MODE_NIBBLE = 19;
const uint8_t FUJITSU_GENERAL_MODE_AUTO = 0x00;
const uint8_t FUJITSU_GENERAL_MODE_HEAT = 0x04;
const uint8_t FUJITSU_GENERAL_MODE_COOL = 0x01;
const uint8_t FUJITSU_GENERAL_MODE_DRY = 0x02;
const uint8_t FUJITSU_GENERAL_MODE_FAN = 0x03;
// const uint8_t FUJITSU_GENERAL_MODE_10C = 0x0B;
// Fan speed and swing
const uint8_t FUJITSU_GENERAL_FAN_AUTO_BYTE10 = 0x00;
const uint8_t FUJITSU_GENERAL_FAN_HIGH_BYTE10 = 0x01;
const uint8_t FUJITSU_GENERAL_FAN_MEDIUM_BYTE10 = 0x02;
const uint8_t FUJITSU_GENERAL_FAN_LOW_BYTE10 = 0x03;
const uint8_t FUJITSU_GENERAL_FAN_SILENT_BYTE10 = 0x04;
const uint8_t FUJITSU_GENERAL_SWING_NONE_BYTE10 = 0x00;
const uint8_t FUJITSU_GENERAL_SWING_VERTICAL_BYTE10 = 0x01;
const uint8_t FUJITSU_GENERAL_SWING_HORIZONTAL_BYTE10 = 0x02;
const uint8_t FUJITSU_GENERAL_SWING_BOTH_BYTE10 = 0x03;
const uint8_t FUJITSU_GENERAL_BASE_BYTE10 = 0x00;
// Swing
const uint8_t FUJITSU_GENERAL_FAN_NIBBLE = 20;
const uint8_t FUJITSU_GENERAL_FAN_AUTO = 0x00;
const uint8_t FUJITSU_GENERAL_FAN_HIGH = 0x01;
const uint8_t FUJITSU_GENERAL_FAN_MEDIUM = 0x02;
const uint8_t FUJITSU_GENERAL_FAN_LOW = 0x03;
const uint8_t FUJITSU_GENERAL_FAN_SILENT = 0x04;
const uint8_t FUJITSU_GENERAL_BASE_BYTE11 = 0x00;
const uint8_t FUJITSU_GENERAL_BASE_BYTE12 = 0x00;
const uint8_t FUJITSU_GENERAL_BASE_BYTE13 = 0x00;
// Fan speed
const uint8_t FUJITSU_GENERAL_SWING_NIBBLE = 21;
const uint8_t FUJITSU_GENERAL_SWING_NONE = 0x00;
const uint8_t FUJITSU_GENERAL_SWING_VERTICAL = 0x01;
const uint8_t FUJITSU_GENERAL_SWING_HORIZONTAL = 0x02;
const uint8_t FUJITSU_GENERAL_SWING_BOTH = 0x03;
// Outdoor Unit Low Noise
const uint8_t FUJITSU_GENERAL_OUTDOOR_UNIT_LOW_NOISE_BYTE14 = 0xA0;
const uint8_t FUJITSU_GENERAL_BASE_BYTE14 = 0x20;
// CRC
const uint8_t FUJITSU_GENERAL_BASE_BYTE15 = 0x6F;
// Power off packet is specific
const uint16_t FUJITSU_GENERAL_OFF_LENGTH = 7;
const uint8_t FUJITSU_GENERAL_OFF_BYTE0 = FUJITSU_GENERAL_BASE_BYTE0;
const uint8_t FUJITSU_GENERAL_OFF_BYTE1 = FUJITSU_GENERAL_BASE_BYTE1;
const uint8_t FUJITSU_GENERAL_OFF_BYTE2 = FUJITSU_GENERAL_BASE_BYTE2;
const uint8_t FUJITSU_GENERAL_OFF_BYTE3 = FUJITSU_GENERAL_BASE_BYTE3;
const uint8_t FUJITSU_GENERAL_OFF_BYTE4 = FUJITSU_GENERAL_BASE_BYTE4;
const uint8_t FUJITSU_GENERAL_OFF_BYTE5 = 0x02;
const uint8_t FUJITSU_GENERAL_OFF_BYTE6 = 0xFD;
const uint8_t FUJITSU_GENERAL_TEMP_MAX = 30; // Celsius
const uint8_t FUJITSU_GENERAL_TEMP_MIN = 16; // Celsius
// TODO Outdoor Unit Low Noise
// const uint8_t FUJITSU_GENERAL_OUTDOOR_UNIT_LOW_NOISE_BYTE14 = 0xA0;
// const uint8_t FUJITSU_GENERAL_STATE_BYTE14 = 0x20;
const uint16_t FUJITSU_GENERAL_HEADER_MARK = 3300;
const uint16_t FUJITSU_GENERAL_HEADER_SPACE = 1600;
const uint16_t FUJITSU_GENERAL_BIT_MARK = 420;
const uint16_t FUJITSU_GENERAL_ONE_SPACE = 1200;
const uint16_t FUJITSU_GENERAL_ZERO_SPACE = 420;
const uint16_t FUJITSU_GENERAL_TRL_MARK = 420;
const uint16_t FUJITSU_GENERAL_TRL_SPACE = 8000;
const uint32_t FUJITSU_GENERAL_CARRIER_FREQUENCY = 38000;
FujitsuGeneralClimate::FujitsuGeneralClimate()
: ClimateIR(
FUJITSU_GENERAL_TEMP_MIN, FUJITSU_GENERAL_TEMP_MAX, 1.0f, true, true,
{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH},
{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL, climate::CLIMATE_SWING_HORIZONTAL,
climate::CLIMATE_SWING_BOTH}) {}
void FujitsuGeneralClimate::transmit_state() {
if (this->mode == climate::CLIMATE_MODE_OFF) {
this->transmit_off_();
return;
}
uint8_t remote_state[FUJITSU_GENERAL_STATE_LENGTH] = {0};
remote_state[0] = FUJITSU_GENERAL_BASE_BYTE0;
remote_state[1] = FUJITSU_GENERAL_BASE_BYTE1;
remote_state[2] = FUJITSU_GENERAL_BASE_BYTE2;
remote_state[3] = FUJITSU_GENERAL_BASE_BYTE3;
remote_state[4] = FUJITSU_GENERAL_BASE_BYTE4;
remote_state[5] = FUJITSU_GENERAL_BASE_BYTE5;
remote_state[6] = FUJITSU_GENERAL_BASE_BYTE6;
remote_state[7] = FUJITSU_GENERAL_BASE_BYTE7;
remote_state[8] = FUJITSU_GENERAL_BASE_BYTE8;
remote_state[9] = FUJITSU_GENERAL_BASE_BYTE9;
remote_state[10] = FUJITSU_GENERAL_BASE_BYTE10;
remote_state[11] = FUJITSU_GENERAL_BASE_BYTE11;
remote_state[12] = FUJITSU_GENERAL_BASE_BYTE12;
remote_state[13] = FUJITSU_GENERAL_BASE_BYTE13;
remote_state[14] = FUJITSU_GENERAL_BASE_BYTE14;
remote_state[15] = FUJITSU_GENERAL_BASE_BYTE15;
ESP_LOGV(TAG, "Transmit state");
uint8_t remote_state[FUJITSU_GENERAL_STATE_MESSAGE_LENGTH] = {0};
// Common message header
remote_state[0] = FUJITSU_GENERAL_COMMON_BYTE0;
remote_state[1] = FUJITSU_GENERAL_COMMON_BYTE1;
remote_state[2] = FUJITSU_GENERAL_COMMON_BYTE2;
remote_state[3] = FUJITSU_GENERAL_COMMON_BYTE3;
remote_state[4] = FUJITSU_GENERAL_COMMON_BYTE4;
remote_state[5] = FUJITSU_GENERAL_MESSAGE_TYPE_STATE;
remote_state[6] = FUJITSU_GENERAL_STATE_HEADER_BYTE0;
remote_state[7] = FUJITSU_GENERAL_STATE_HEADER_BYTE1;
// unknown, does not appear to change with any remote settings
remote_state[14] = FUJITSU_GENERAL_STATE_FOOTER_BYTE0;
// Set temperature
auto safecelsius =
uint8_t temperature_clamped =
(uint8_t) roundf(clamp(this->target_temperature, FUJITSU_GENERAL_TEMP_MIN, FUJITSU_GENERAL_TEMP_MAX));
remote_state[8] = (byte) safecelsius - 16;
remote_state[8] = remote_state[8] << 4;
uint8_t temperature_offset = temperature_clamped - FUJITSU_GENERAL_TEMP_MIN;
SET_NIBBLE(remote_state, FUJITSU_GENERAL_TEMPERATURE_NIBBLE, temperature_offset);
// If not powered - set power on flag
// Set power on
if (!this->power_) {
remote_state[8] = (byte) remote_state[8] | FUJITSU_GENERAL_POWER_ON_MASK_BYTE8;
SET_NIBBLE(remote_state, FUJITSU_GENERAL_POWER_ON_NIBBLE, FUJITSU_GENERAL_POWER_ON);
}
// Set mode
switch (this->mode) {
case climate::CLIMATE_MODE_COOL:
remote_state[9] = FUJITSU_GENERAL_MODE_COOL_BYTE9;
SET_NIBBLE(remote_state, FUJITSU_GENERAL_MODE_NIBBLE, FUJITSU_GENERAL_MODE_COOL);
break;
case climate::CLIMATE_MODE_HEAT:
remote_state[9] = FUJITSU_GENERAL_MODE_HEAT_BYTE9;
SET_NIBBLE(remote_state, FUJITSU_GENERAL_MODE_NIBBLE, FUJITSU_GENERAL_MODE_HEAT);
break;
case climate::CLIMATE_MODE_DRY:
remote_state[9] = FUJITSU_GENERAL_MODE_DRY_BYTE9;
SET_NIBBLE(remote_state, FUJITSU_GENERAL_MODE_NIBBLE, FUJITSU_GENERAL_MODE_DRY);
break;
case climate::CLIMATE_MODE_FAN_ONLY:
remote_state[9] = FUJITSU_GENERAL_MODE_FAN_BYTE9;
SET_NIBBLE(remote_state, FUJITSU_GENERAL_MODE_NIBBLE, FUJITSU_GENERAL_MODE_FAN);
break;
case climate::CLIMATE_MODE_AUTO:
default:
remote_state[9] = FUJITSU_GENERAL_MODE_AUTO_BYTE9;
SET_NIBBLE(remote_state, FUJITSU_GENERAL_MODE_NIBBLE, FUJITSU_GENERAL_MODE_AUTO);
break;
// TODO: CLIMATE_MODE_10C are missing in esphome
// TODO: CLIMATE_MODE_10C is missing from esphome
}
// Set fan
switch (this->fan_mode) {
case climate::CLIMATE_FAN_HIGH:
remote_state[10] = FUJITSU_GENERAL_FAN_HIGH_BYTE10;
SET_NIBBLE(remote_state, FUJITSU_GENERAL_FAN_NIBBLE, FUJITSU_GENERAL_FAN_HIGH);
break;
case climate::CLIMATE_FAN_MEDIUM:
remote_state[10] = FUJITSU_GENERAL_FAN_MEDIUM_BYTE10;
SET_NIBBLE(remote_state, FUJITSU_GENERAL_FAN_NIBBLE, FUJITSU_GENERAL_FAN_MEDIUM);
break;
case climate::CLIMATE_FAN_LOW:
remote_state[10] = FUJITSU_GENERAL_FAN_LOW_BYTE10;
SET_NIBBLE(remote_state, FUJITSU_GENERAL_FAN_NIBBLE, FUJITSU_GENERAL_FAN_LOW);
break;
case climate::CLIMATE_FAN_AUTO:
default:
remote_state[10] = FUJITSU_GENERAL_FAN_AUTO_BYTE10;
SET_NIBBLE(remote_state, FUJITSU_GENERAL_FAN_NIBBLE, FUJITSU_GENERAL_FAN_AUTO);
break;
// TODO Quiet / Silent
}
// Set swing
switch (this->swing_mode) {
case climate::CLIMATE_SWING_VERTICAL:
remote_state[10] = (byte) remote_state[10] | (FUJITSU_GENERAL_SWING_VERTICAL_BYTE10 << 4);
SET_NIBBLE(remote_state, FUJITSU_GENERAL_SWING_NIBBLE, FUJITSU_GENERAL_SWING_VERTICAL);
break;
case climate::CLIMATE_SWING_HORIZONTAL:
remote_state[10] = (byte) remote_state[10] | (FUJITSU_GENERAL_SWING_HORIZONTAL_BYTE10 << 4);
SET_NIBBLE(remote_state, FUJITSU_GENERAL_SWING_NIBBLE, FUJITSU_GENERAL_SWING_HORIZONTAL);
break;
case climate::CLIMATE_SWING_BOTH:
remote_state[10] = (byte) remote_state[10] | (FUJITSU_GENERAL_SWING_BOTH_BYTE10 << 4);
SET_NIBBLE(remote_state, FUJITSU_GENERAL_SWING_NIBBLE, FUJITSU_GENERAL_SWING_BOTH);
break;
case climate::CLIMATE_SWING_OFF:
default:
remote_state[10] = (byte) remote_state[10] | (FUJITSU_GENERAL_SWING_NONE_BYTE10 << 4);
SET_NIBBLE(remote_state, FUJITSU_GENERAL_SWING_NIBBLE, FUJITSU_GENERAL_SWING_NONE);
break;
}
// TODO: missing support for outdoor unit low noise
// remote_state[14] = (byte) remote_state[14] | FUJITSU_GENERAL_OUTDOOR_UNIT_LOW_NOISE_BYTE14;
// CRC
remote_state[15] = 0;
for (int i = 7; i < 15; i++) {
remote_state[15] += (byte) remote_state[i]; // Addiction
}
remote_state[15] = 0x100 - remote_state[15]; // mod 256
remote_state[FUJITSU_GENERAL_STATE_MESSAGE_LENGTH - 1] = this->checksum_state_(remote_state);
auto transmit = this->transmitter_->transmit();
auto data = transmit.get_data();
data->set_carrier_frequency(FUJITSU_GENERAL_CARRIER_FREQUENCY);
// Header
data->mark(FUJITSU_GENERAL_HEADER_MARK);
data->space(FUJITSU_GENERAL_HEADER_SPACE);
// Data
for (uint8_t i : remote_state) {
// Send all Bits from Byte Data in Reverse Order
for (uint8_t mask = 00000001; mask > 0; mask <<= 1) { // iterate through bit mask
data->mark(FUJITSU_GENERAL_BIT_MARK);
bool bit = i & mask;
data->space(bit ? FUJITSU_GENERAL_ONE_SPACE : FUJITSU_GENERAL_ZERO_SPACE);
// Next bits
}
}
// Footer
data->mark(FUJITSU_GENERAL_TRL_MARK);
data->space(FUJITSU_GENERAL_TRL_SPACE);
transmit.perform();
this->transmit_(remote_state, FUJITSU_GENERAL_STATE_MESSAGE_LENGTH);
this->power_ = true;
}
void FujitsuGeneralClimate::transmit_off_() {
uint8_t remote_state[FUJITSU_GENERAL_OFF_LENGTH] = {0};
ESP_LOGV(TAG, "Transmit off");
remote_state[0] = FUJITSU_GENERAL_OFF_BYTE0;
remote_state[1] = FUJITSU_GENERAL_OFF_BYTE1;
remote_state[2] = FUJITSU_GENERAL_OFF_BYTE2;
remote_state[3] = FUJITSU_GENERAL_OFF_BYTE3;
remote_state[4] = FUJITSU_GENERAL_OFF_BYTE4;
remote_state[5] = FUJITSU_GENERAL_OFF_BYTE5;
remote_state[6] = FUJITSU_GENERAL_OFF_BYTE6;
uint8_t remote_state[FUJITSU_GENERAL_UTIL_MESSAGE_LENGTH] = {0};
remote_state[0] = FUJITSU_GENERAL_COMMON_BYTE0;
remote_state[1] = FUJITSU_GENERAL_COMMON_BYTE1;
remote_state[2] = FUJITSU_GENERAL_COMMON_BYTE2;
remote_state[3] = FUJITSU_GENERAL_COMMON_BYTE3;
remote_state[4] = FUJITSU_GENERAL_COMMON_BYTE4;
remote_state[5] = FUJITSU_GENERAL_MESSAGE_TYPE_OFF;
remote_state[6] = this->checksum_util_(remote_state);
this->transmit_(remote_state, FUJITSU_GENERAL_UTIL_MESSAGE_LENGTH);
this->power_ = false;
}
void FujitsuGeneralClimate::transmit_(uint8_t const* message, uint8_t length) {
ESP_LOGV(TAG, "Transmit message length %d", length);
auto transmit = this->transmitter_->transmit();
auto data = transmit.get_data();
@ -232,22 +215,191 @@ void FujitsuGeneralClimate::transmit_off_() {
data->space(FUJITSU_GENERAL_HEADER_SPACE);
// Data
for (uint8_t i : remote_state) {
// Send all Bits from Byte Data in Reverse Order
for (uint8_t mask = 00000001; mask > 0; mask <<= 1) { // iterate through bit mask
for (uint8_t i = 0; i < length; ++i) {
const uint8_t byte = message[i];
for (uint8_t mask = 0b00000001; mask > 0; mask <<= 1) { // write from right to left
data->mark(FUJITSU_GENERAL_BIT_MARK);
bool bit = i & mask;
bool bit = byte & mask;
data->space(bit ? FUJITSU_GENERAL_ONE_SPACE : FUJITSU_GENERAL_ZERO_SPACE);
// Next bits
}
}
// Footer
data->mark(FUJITSU_GENERAL_TRL_MARK);
data->space(FUJITSU_GENERAL_TRL_SPACE);
transmit.perform();
}
this->power_ = false;
uint8_t FujitsuGeneralClimate::checksum_state_(uint8_t const* message) {
uint8_t checksum = 0;
for (uint8_t i = 7; i < FUJITSU_GENERAL_STATE_MESSAGE_LENGTH - 1; ++i) {
checksum += message[i];
}
return 256 - checksum;
}
uint8_t FujitsuGeneralClimate::checksum_util_(uint8_t const* message) { return 255 - message[5]; }
bool FujitsuGeneralClimate::on_receive(remote_base::RemoteReceiveData data) {
ESP_LOGV(TAG, "Received IR message");
// Validate header
if (!data.expect_item(FUJITSU_GENERAL_HEADER_MARK, FUJITSU_GENERAL_HEADER_SPACE)) {
ESP_LOGV(TAG, "Header fail");
return false;
}
uint8_t recv_message[FUJITSU_GENERAL_STATE_MESSAGE_LENGTH] = {0};
// Read header
for (uint8_t byte = 0; byte < FUJITSU_GENERAL_COMMON_LENGTH; ++byte) {
// Read bit
for (uint8_t bit = 0; bit < 8; ++bit) {
if (data.expect_item(FUJITSU_GENERAL_BIT_MARK, FUJITSU_GENERAL_ONE_SPACE)) {
recv_message[byte] |= 1 << bit; // read from right to left
} else if (!data.expect_item(FUJITSU_GENERAL_BIT_MARK, FUJITSU_GENERAL_ZERO_SPACE)) {
ESP_LOGV(TAG, "Byte %d bit %d fail", byte, bit);
return false;
}
}
}
const uint8_t recv_message_type = recv_message[FUJITSU_GENERAL_MESSAGE_TYPE_BYTE];
uint8_t recv_message_length;
switch (recv_message_type) {
case FUJITSU_GENERAL_MESSAGE_TYPE_STATE:
ESP_LOGV(TAG, "Received state message");
recv_message_length = FUJITSU_GENERAL_STATE_MESSAGE_LENGTH;
break;
case FUJITSU_GENERAL_MESSAGE_TYPE_OFF:
case FUJITSU_GENERAL_MESSAGE_TYPE_ECONOMY:
case FUJITSU_GENERAL_MESSAGE_TYPE_NUDGE:
ESP_LOGV(TAG, "Received util message");
recv_message_length = FUJITSU_GENERAL_UTIL_MESSAGE_LENGTH;
break;
default:
ESP_LOGV(TAG, "Unknown message type %X", recv_message_type);
return false;
}
// Read message body
for (uint8_t byte = FUJITSU_GENERAL_COMMON_LENGTH; byte < recv_message_length; ++byte) {
for (uint8_t bit = 0; bit < 8; ++bit) {
if (data.expect_item(FUJITSU_GENERAL_BIT_MARK, FUJITSU_GENERAL_ONE_SPACE)) {
recv_message[byte] |= 1 << bit; // read from right to left
} else if (!data.expect_item(FUJITSU_GENERAL_BIT_MARK, FUJITSU_GENERAL_ZERO_SPACE)) {
ESP_LOGV(TAG, "Byte %d bit %d fail", byte, bit);
return false;
}
}
}
// Validate footer
if (!data.expect_mark(FUJITSU_GENERAL_BIT_MARK)) {
ESP_LOGV(TAG, "Footer fail");
return false;
}
for (uint8_t byte = 0; byte < recv_message_length; ++byte) {
ESP_LOGVV(TAG, "%02X", recv_message[byte]);
}
const uint8_t recv_checksum = recv_message[recv_message_length - 1];
uint8_t calculated_checksum;
if (recv_message_type == FUJITSU_GENERAL_MESSAGE_TYPE_STATE) {
calculated_checksum = this->checksum_state_(recv_message);
} else {
calculated_checksum = this->checksum_util_(recv_message);
}
if (recv_checksum != calculated_checksum) {
ESP_LOGV(TAG, "Checksum fail - expected %X - got %X", calculated_checksum, recv_checksum);
return false;
}
if (recv_message_type == FUJITSU_GENERAL_MESSAGE_TYPE_STATE) {
const uint8_t recv_tempertature = GET_NIBBLE(recv_message, FUJITSU_GENERAL_TEMPERATURE_NIBBLE);
const uint8_t offset_temperature = recv_tempertature + FUJITSU_GENERAL_TEMP_MIN;
this->target_temperature = offset_temperature;
ESP_LOGV(TAG, "Received temperature %d", offset_temperature);
const uint8_t recv_mode = GET_NIBBLE(recv_message, FUJITSU_GENERAL_MODE_NIBBLE);
ESP_LOGV(TAG, "Received mode %X", recv_mode);
switch (recv_mode) {
case FUJITSU_GENERAL_MODE_COOL:
this->mode = climate::CLIMATE_MODE_COOL;
break;
case FUJITSU_GENERAL_MODE_HEAT:
this->mode = climate::CLIMATE_MODE_HEAT;
break;
case FUJITSU_GENERAL_MODE_DRY:
this->mode = climate::CLIMATE_MODE_DRY;
break;
case FUJITSU_GENERAL_MODE_FAN:
this->mode = climate::CLIMATE_MODE_FAN_ONLY;
break;
case FUJITSU_GENERAL_MODE_AUTO:
default:
// TODO: CLIMATE_MODE_10C is missing from esphome
this->mode = climate::CLIMATE_MODE_AUTO;
break;
}
const uint8_t recv_fan_mode = GET_NIBBLE(recv_message, FUJITSU_GENERAL_FAN_NIBBLE);
ESP_LOGV(TAG, "Received fan mode %X", recv_fan_mode);
switch (recv_fan_mode) {
// TODO No Quiet / Silent in ESPH
case FUJITSU_GENERAL_FAN_SILENT:
case FUJITSU_GENERAL_FAN_LOW:
this->fan_mode = climate::CLIMATE_FAN_LOW;
break;
case FUJITSU_GENERAL_FAN_MEDIUM:
this->fan_mode = climate::CLIMATE_FAN_MEDIUM;
break;
case FUJITSU_GENERAL_FAN_HIGH:
this->fan_mode = climate::CLIMATE_FAN_HIGH;
break;
case FUJITSU_GENERAL_FAN_AUTO:
default:
this->fan_mode = climate::CLIMATE_FAN_AUTO;
break;
}
const uint8_t recv_swing_mode = GET_NIBBLE(recv_message, FUJITSU_GENERAL_SWING_NIBBLE);
ESP_LOGV(TAG, "Received swing mode %X", recv_swing_mode);
switch (recv_swing_mode) {
case FUJITSU_GENERAL_SWING_VERTICAL:
this->swing_mode = climate::CLIMATE_SWING_VERTICAL;
break;
case FUJITSU_GENERAL_SWING_HORIZONTAL:
this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
break;
case FUJITSU_GENERAL_SWING_BOTH:
this->swing_mode = climate::CLIMATE_SWING_BOTH;
break;
case FUJITSU_GENERAL_SWING_NONE:
default:
this->swing_mode = climate::CLIMATE_SWING_OFF;
}
this->power_ = true;
}
else if (recv_message_type == FUJITSU_GENERAL_MESSAGE_TYPE_OFF) {
ESP_LOGV(TAG, "Received off message");
this->mode = climate::CLIMATE_MODE_OFF;
this->power_ = false;
}
else {
ESP_LOGV(TAG, "Received unsupprted message type %X", recv_message_type);
return false;
}
this->publish_state();
return true;
}
} // namespace fujitsu_general

View File

@ -1,5 +1,6 @@
#pragma once
#include "esphome/core/log.h"
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "esphome/components/climate_ir/climate_ir.h"
@ -7,9 +8,17 @@
namespace esphome {
namespace fujitsu_general {
const uint8_t FUJITSU_GENERAL_TEMP_MIN = 16; // Celsius // TODO 16 for heating, 18 for cooling, unsupported in ESPH
const uint8_t FUJITSU_GENERAL_TEMP_MAX = 30; // Celsius
class FujitsuGeneralClimate : public climate_ir::ClimateIR {
public:
FujitsuGeneralClimate();
FujitsuGeneralClimate()
: ClimateIR(FUJITSU_GENERAL_TEMP_MIN, FUJITSU_GENERAL_TEMP_MAX, 1.0f, true, true,
{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM,
climate::CLIMATE_FAN_HIGH},
{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL, climate::CLIMATE_SWING_HORIZONTAL,
climate::CLIMATE_SWING_BOTH}) {}
protected:
/// Transmit via IR the state of this climate controller.
@ -17,6 +26,19 @@ class FujitsuGeneralClimate : public climate_ir::ClimateIR {
/// Transmit via IR power off command.
void transmit_off_();
/// Parse incomming message
bool on_receive(remote_base::RemoteReceiveData data) override;
/// Transmit message as IR pulses
void transmit_(uint8_t const* message, uint8_t length);
/// Calculate checksum for a state message
uint8_t checksum_state_(uint8_t const* message);
/// Calculate cecksum for a util message
uint8_t checksum_util_(uint8_t const* message);
// true if currently on - fujitsus transmit an on flag on when the remote moves from off to on
bool power_{false};
};

View File

@ -2,21 +2,29 @@ import hashlib
from esphome import config_validation as cv, automation
from esphome import codegen as cg
from esphome.const import CONF_ID, CONF_INITIAL_VALUE, CONF_RESTORE_VALUE, CONF_TYPE, CONF_VALUE
from esphome.const import (
CONF_ID,
CONF_INITIAL_VALUE,
CONF_RESTORE_VALUE,
CONF_TYPE,
CONF_VALUE,
)
from esphome.core import coroutine_with_priority
CODEOWNERS = ['@esphome/core']
globals_ns = cg.esphome_ns.namespace('globals')
GlobalsComponent = globals_ns.class_('GlobalsComponent', cg.Component)
GlobalVarSetAction = globals_ns.class_('GlobalVarSetAction', automation.Action)
CODEOWNERS = ["@esphome/core"]
globals_ns = cg.esphome_ns.namespace("globals")
GlobalsComponent = globals_ns.class_("GlobalsComponent", cg.Component)
GlobalVarSetAction = globals_ns.class_("GlobalVarSetAction", automation.Action)
MULTI_CONF = True
CONFIG_SCHEMA = cv.Schema({
cv.Required(CONF_ID): cv.declare_id(GlobalsComponent),
cv.Required(CONF_TYPE): cv.string_strict,
cv.Optional(CONF_INITIAL_VALUE): cv.string_strict,
cv.Optional(CONF_RESTORE_VALUE, default=False): cv.boolean,
}).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.declare_id(GlobalsComponent),
cv.Required(CONF_TYPE): cv.string_strict,
cv.Optional(CONF_INITIAL_VALUE): cv.string_strict,
cv.Optional(CONF_RESTORE_VALUE, default=False): cv.boolean,
}
).extend(cv.COMPONENT_SCHEMA)
# Run with low priority so that namespaces are registered first
@ -42,15 +50,22 @@ def to_code(config):
cg.add(glob.set_restore_value(hash_))
@automation.register_action('globals.set', GlobalVarSetAction, cv.Schema({
cv.Required(CONF_ID): cv.use_id(GlobalsComponent),
cv.Required(CONF_VALUE): cv.templatable(cv.string_strict),
}))
@automation.register_action(
"globals.set",
GlobalVarSetAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(GlobalsComponent),
cv.Required(CONF_VALUE): cv.templatable(cv.string_strict),
}
),
)
def globals_set_to_code(config, action_id, template_arg, args):
full_id, paren = yield cg.get_variable_with_full_id(config[CONF_ID])
template_arg = cg.TemplateArguments(full_id.type, *template_arg)
var = cg.new_Pvariable(action_id, template_arg, paren)
templ = yield cg.templatable(config[CONF_VALUE], args, None,
to_exp=cg.RawExpression)
templ = yield cg.templatable(
config[CONF_VALUE], args, None, to_exp=cg.RawExpression
)
cg.add(var.set_value(templ))
yield var

View File

@ -1,4 +1,4 @@
import esphome.codegen as cg
CODEOWNERS = ['@esphome/core']
gpio_ns = cg.esphome_ns.namespace('gpio')
CODEOWNERS = ["@esphome/core"]
gpio_ns = cg.esphome_ns.namespace("gpio")

View File

@ -5,12 +5,16 @@ from esphome.components import binary_sensor
from esphome.const import CONF_ID, CONF_PIN
from .. import gpio_ns
GPIOBinarySensor = gpio_ns.class_('GPIOBinarySensor', binary_sensor.BinarySensor, cg.Component)
GPIOBinarySensor = gpio_ns.class_(
"GPIOBinarySensor", binary_sensor.BinarySensor, cg.Component
)
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(GPIOBinarySensor),
cv.Required(CONF_PIN): pins.gpio_input_pin_schema
}).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(GPIOBinarySensor),
cv.Required(CONF_PIN): pins.gpio_input_pin_schema,
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config):

View File

@ -5,13 +5,14 @@ from esphome.components import output
from esphome.const import CONF_ID, CONF_PIN
from .. import gpio_ns
GPIOBinaryOutput = gpio_ns.class_('GPIOBinaryOutput', output.BinaryOutput,
cg.Component)
GPIOBinaryOutput = gpio_ns.class_("GPIOBinaryOutput", output.BinaryOutput, cg.Component)
CONFIG_SCHEMA = output.BINARY_OUTPUT_SCHEMA.extend({
cv.Required(CONF_ID): cv.declare_id(GPIOBinaryOutput),
cv.Required(CONF_PIN): pins.gpio_output_pin_schema,
}).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = output.BINARY_OUTPUT_SCHEMA.extend(
{
cv.Required(CONF_ID): cv.declare_id(GPIOBinaryOutput),
cv.Required(CONF_PIN): pins.gpio_output_pin_schema,
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config):

View File

@ -5,25 +5,30 @@ from esphome.components import switch
from esphome.const import CONF_ID, CONF_INTERLOCK, CONF_PIN, CONF_RESTORE_MODE
from .. import gpio_ns
GPIOSwitch = gpio_ns.class_('GPIOSwitch', switch.Switch, cg.Component)
GPIOSwitchRestoreMode = gpio_ns.enum('GPIOSwitchRestoreMode')
GPIOSwitch = gpio_ns.class_("GPIOSwitch", switch.Switch, cg.Component)
GPIOSwitchRestoreMode = gpio_ns.enum("GPIOSwitchRestoreMode")
RESTORE_MODES = {
'RESTORE_DEFAULT_OFF': GPIOSwitchRestoreMode.GPIO_SWITCH_RESTORE_DEFAULT_OFF,
'RESTORE_DEFAULT_ON': GPIOSwitchRestoreMode.GPIO_SWITCH_RESTORE_DEFAULT_ON,
'ALWAYS_OFF': GPIOSwitchRestoreMode.GPIO_SWITCH_ALWAYS_OFF,
'ALWAYS_ON': GPIOSwitchRestoreMode.GPIO_SWITCH_ALWAYS_ON,
"RESTORE_DEFAULT_OFF": GPIOSwitchRestoreMode.GPIO_SWITCH_RESTORE_DEFAULT_OFF,
"RESTORE_DEFAULT_ON": GPIOSwitchRestoreMode.GPIO_SWITCH_RESTORE_DEFAULT_ON,
"ALWAYS_OFF": GPIOSwitchRestoreMode.GPIO_SWITCH_ALWAYS_OFF,
"ALWAYS_ON": GPIOSwitchRestoreMode.GPIO_SWITCH_ALWAYS_ON,
}
CONF_INTERLOCK_WAIT_TIME = 'interlock_wait_time'
CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(GPIOSwitch),
cv.Required(CONF_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_RESTORE_MODE, default='RESTORE_DEFAULT_OFF'):
cv.enum(RESTORE_MODES, upper=True, space='_'),
cv.Optional(CONF_INTERLOCK): cv.ensure_list(cv.use_id(switch.Switch)),
cv.Optional(CONF_INTERLOCK_WAIT_TIME, default='0ms'): cv.positive_time_period_milliseconds,
}).extend(cv.COMPONENT_SCHEMA)
CONF_INTERLOCK_WAIT_TIME = "interlock_wait_time"
CONFIG_SCHEMA = switch.SWITCH_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(GPIOSwitch),
cv.Required(CONF_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_RESTORE_MODE, default="RESTORE_DEFAULT_OFF"): cv.enum(
RESTORE_MODES, upper=True, space="_"
),
cv.Optional(CONF_INTERLOCK): cv.ensure_list(cv.use_id(switch.Switch)),
cv.Optional(
CONF_INTERLOCK_WAIT_TIME, default="0ms"
): cv.positive_time_period_milliseconds,
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config):

View File

@ -3,17 +3,23 @@ import esphome.config_validation as cv
from esphome.components import uart
from esphome.const import CONF_ID
DEPENDENCIES = ['uart']
DEPENDENCIES = ["uart"]
gps_ns = cg.esphome_ns.namespace('gps')
GPS = gps_ns.class_('GPS', cg.Component, uart.UARTDevice)
GPSListener = gps_ns.class_('GPSListener')
gps_ns = cg.esphome_ns.namespace("gps")
GPS = gps_ns.class_("GPS", cg.Component, uart.UARTDevice)
GPSListener = gps_ns.class_("GPSListener")
CONF_GPS_ID = 'gps_id'
CONF_GPS_ID = "gps_id"
MULTI_CONF = True
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(GPS),
}).extend(cv.COMPONENT_SCHEMA).extend(uart.UART_DEVICE_SCHEMA)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(GPS),
}
)
.extend(cv.COMPONENT_SCHEMA)
.extend(uart.UART_DEVICE_SCHEMA)
)
def to_code(config):
@ -22,4 +28,4 @@ def to_code(config):
yield uart.register_uart_device(var, config)
# https://platformio.org/lib/show/1655/TinyGPSPlus
cg.add_library('1655', '1.0.2') # TinyGPSPlus, has name conflict
cg.add_library("1655", "1.0.2") # TinyGPSPlus, has name conflict

View File

@ -4,14 +4,18 @@ import esphome.codegen as cg
from esphome.const import CONF_ID
from .. import gps_ns, GPSListener, CONF_GPS_ID, GPS
DEPENDENCIES = ['gps']
DEPENDENCIES = ["gps"]
GPSTime = gps_ns.class_('GPSTime', cg.PollingComponent, time_.RealTimeClock, GPSListener)
GPSTime = gps_ns.class_(
"GPSTime", cg.PollingComponent, time_.RealTimeClock, GPSListener
)
CONFIG_SCHEMA = time_.TIME_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(GPSTime),
cv.GenerateID(CONF_GPS_ID): cv.use_id(GPS),
}).extend(cv.polling_component_schema('5min'))
CONFIG_SCHEMA = time_.TIME_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(GPSTime),
cv.GenerateID(CONF_GPS_ID): cv.use_id(GPS),
}
).extend(cv.polling_component_schema("5min"))
def to_code(config):

View File

@ -3,14 +3,18 @@ import esphome.config_validation as cv
from esphome.components import light, output
from esphome.const import CONF_OUTPUT_ID, CONF_PIN_A, CONF_PIN_B
hbridge_ns = cg.esphome_ns.namespace('hbridge')
HBridgeLightOutput = hbridge_ns.class_('HBridgeLightOutput', cg.PollingComponent, light.LightOutput)
hbridge_ns = cg.esphome_ns.namespace("hbridge")
HBridgeLightOutput = hbridge_ns.class_(
"HBridgeLightOutput", cg.PollingComponent, light.LightOutput
)
CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend({
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(HBridgeLightOutput),
cv.Required(CONF_PIN_A): cv.use_id(output.FloatOutput),
cv.Required(CONF_PIN_B): cv.use_id(output.FloatOutput),
})
CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend(
{
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(HBridgeLightOutput),
cv.Required(CONF_PIN_A): cv.use_id(output.FloatOutput),
cv.Required(CONF_PIN_B): cv.use_id(output.FloatOutput),
}
)
def to_code(config):

View File

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

View File

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

View File

@ -2,40 +2,72 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import sensor
from esphome.const import CONF_CHANGE_MODE_EVERY, CONF_INITIAL_MODE, CONF_CURRENT, \
CONF_CURRENT_RESISTOR, CONF_ID, CONF_POWER, CONF_ENERGY, CONF_SEL_PIN, CONF_VOLTAGE, \
CONF_VOLTAGE_DIVIDER, ICON_FLASH, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT, UNIT_WATT_HOURS
from esphome.const import (
CONF_CHANGE_MODE_EVERY,
CONF_INITIAL_MODE,
CONF_CURRENT,
CONF_CURRENT_RESISTOR,
CONF_ID,
CONF_POWER,
CONF_ENERGY,
CONF_SEL_PIN,
CONF_VOLTAGE,
CONF_VOLTAGE_DIVIDER,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
UNIT_VOLT,
UNIT_AMPERE,
UNIT_WATT,
UNIT_WATT_HOURS,
)
AUTO_LOAD = ['pulse_counter']
AUTO_LOAD = ["pulse_counter"]
hlw8012_ns = cg.esphome_ns.namespace('hlw8012')
HLW8012Component = hlw8012_ns.class_('HLW8012Component', cg.PollingComponent)
HLW8012InitialMode = hlw8012_ns.enum('HLW8012InitialMode')
hlw8012_ns = cg.esphome_ns.namespace("hlw8012")
HLW8012Component = hlw8012_ns.class_("HLW8012Component", cg.PollingComponent)
HLW8012InitialMode = hlw8012_ns.enum("HLW8012InitialMode")
INITIAL_MODES = {
CONF_CURRENT: HLW8012InitialMode.HLW8012_INITIAL_MODE_CURRENT,
CONF_VOLTAGE: HLW8012InitialMode.HLW8012_INITIAL_MODE_VOLTAGE,
}
CONF_CF1_PIN = 'cf1_pin'
CONF_CF_PIN = 'cf_pin'
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(HLW8012Component),
cv.Required(CONF_SEL_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_CF_PIN): cv.All(pins.internal_gpio_input_pullup_pin_schema,
pins.validate_has_interrupt),
cv.Required(CONF_CF1_PIN): cv.All(pins.internal_gpio_input_pullup_pin_schema,
pins.validate_has_interrupt),
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1),
cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2),
cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 1),
cv.Optional(CONF_ENERGY): sensor.sensor_schema(UNIT_WATT_HOURS, ICON_FLASH, 1),
cv.Optional(CONF_CURRENT_RESISTOR, default=0.001): cv.resistance,
cv.Optional(CONF_VOLTAGE_DIVIDER, default=2351): cv.positive_float,
cv.Optional(CONF_CHANGE_MODE_EVERY, default=8): cv.All(cv.uint32_t, cv.Range(min=1)),
cv.Optional(CONF_INITIAL_MODE, default=CONF_VOLTAGE): cv.one_of(*INITIAL_MODES, lower=True),
}).extend(cv.polling_component_schema('60s'))
CONF_CF1_PIN = "cf1_pin"
CONF_CF_PIN = "cf_pin"
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(HLW8012Component),
cv.Required(CONF_SEL_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_CF_PIN): cv.All(
pins.internal_gpio_input_pullup_pin_schema, pins.validate_has_interrupt
),
cv.Required(CONF_CF1_PIN): cv.All(
pins.internal_gpio_input_pullup_pin_schema, pins.validate_has_interrupt
),
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE
),
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
),
cv.Optional(CONF_ENERGY): sensor.sensor_schema(
UNIT_WATT_HOURS, ICON_EMPTY, 1, DEVICE_CLASS_ENERGY
),
cv.Optional(CONF_CURRENT_RESISTOR, default=0.001): cv.resistance,
cv.Optional(CONF_VOLTAGE_DIVIDER, default=2351): cv.positive_float,
cv.Optional(CONF_CHANGE_MODE_EVERY, default=8): cv.All(
cv.uint32_t, cv.Range(min=1)
),
cv.Optional(CONF_INITIAL_MODE, default=CONF_VOLTAGE): cv.one_of(
*INITIAL_MODES, lower=True
),
}
).extend(cv.polling_component_schema("60s"))
def to_code(config):

View File

@ -1,22 +1,31 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import CONF_ID, CONF_PM_2_5, CONF_PM_10_0, CONF_PM_1_0, \
UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON
from esphome.const import (
CONF_ID,
CONF_PM_2_5,
CONF_PM_10_0,
CONF_PM_1_0,
DEVICE_CLASS_EMPTY,
UNIT_MICROGRAMS_PER_CUBIC_METER,
ICON_CHEMICAL_WEAPON,
)
DEPENDENCIES = ['i2c']
DEPENDENCIES = ["i2c"]
hm3301_ns = cg.esphome_ns.namespace('hm3301')
HM3301Component = hm3301_ns.class_('HM3301Component', cg.PollingComponent, i2c.I2CDevice)
AQICalculatorType = hm3301_ns.enum('AQICalculatorType')
hm3301_ns = cg.esphome_ns.namespace("hm3301")
HM3301Component = hm3301_ns.class_(
"HM3301Component", cg.PollingComponent, i2c.I2CDevice
)
AQICalculatorType = hm3301_ns.enum("AQICalculatorType")
CONF_AQI = 'aqi'
CONF_CALCULATION_TYPE = 'calculation_type'
UNIT_INDEX = 'index'
CONF_AQI = "aqi"
CONF_CALCULATION_TYPE = "calculation_type"
UNIT_INDEX = "index"
AQI_CALCULATION_TYPE = {
'CAQI': AQICalculatorType.CAQI_TYPE,
'AQI': AQICalculatorType.AQI_TYPE
"CAQI": AQICalculatorType.CAQI_TYPE,
"AQI": AQICalculatorType.AQI_TYPE,
}
@ -28,20 +37,43 @@ def validate(config):
return config
CONFIG_SCHEMA = cv.All(cv.Schema({
cv.GenerateID(): cv.declare_id(HM3301Component),
cv.Optional(CONF_PM_1_0):
sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0),
cv.Optional(CONF_PM_2_5):
sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0),
cv.Optional(CONF_PM_10_0):
sensor.sensor_schema(UNIT_MICROGRAMS_PER_CUBIC_METER, ICON_CHEMICAL_WEAPON, 0),
cv.Optional(CONF_AQI):
sensor.sensor_schema(UNIT_INDEX, ICON_CHEMICAL_WEAPON, 0).extend({
cv.Required(CONF_CALCULATION_TYPE): cv.enum(AQI_CALCULATION_TYPE, upper=True),
})
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x40)), validate)
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(HM3301Component),
cv.Optional(CONF_PM_1_0): sensor.sensor_schema(
UNIT_MICROGRAMS_PER_CUBIC_METER,
ICON_CHEMICAL_WEAPON,
0,
DEVICE_CLASS_EMPTY,
),
cv.Optional(CONF_PM_2_5): sensor.sensor_schema(
UNIT_MICROGRAMS_PER_CUBIC_METER,
ICON_CHEMICAL_WEAPON,
0,
DEVICE_CLASS_EMPTY,
),
cv.Optional(CONF_PM_10_0): sensor.sensor_schema(
UNIT_MICROGRAMS_PER_CUBIC_METER,
ICON_CHEMICAL_WEAPON,
0,
DEVICE_CLASS_EMPTY,
),
cv.Optional(CONF_AQI): sensor.sensor_schema(
UNIT_INDEX, ICON_CHEMICAL_WEAPON, 0, DEVICE_CLASS_EMPTY
).extend(
{
cv.Required(CONF_CALCULATION_TYPE): cv.enum(
AQI_CALCULATION_TYPE, upper=True
),
}
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x40)),
validate,
)
def to_code(config):
@ -67,4 +99,4 @@ def to_code(config):
cg.add(var.set_aqi_calculation_type(config[CONF_AQI][CONF_CALCULATION_TYPE]))
# https://platformio.org/lib/show/6306/Grove%20-%20Laser%20PM2.5%20Sensor%20HM3301
cg.add_library('6306', '1.0.3')
cg.add_library("6306", "1.0.3")

View File

@ -1,22 +1,33 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import (CONF_ADDRESS, CONF_ID, CONF_OVERSAMPLING, CONF_RANGE, ICON_MAGNET,
UNIT_MICROTESLA, UNIT_DEGREES, ICON_SCREEN_ROTATION,
CONF_UPDATE_INTERVAL)
from esphome.const import (
CONF_ADDRESS,
CONF_ID,
CONF_OVERSAMPLING,
CONF_RANGE,
DEVICE_CLASS_EMPTY,
ICON_MAGNET,
UNIT_MICROTESLA,
UNIT_DEGREES,
ICON_SCREEN_ROTATION,
CONF_UPDATE_INTERVAL,
)
DEPENDENCIES = ['i2c']
DEPENDENCIES = ["i2c"]
hmc5883l_ns = cg.esphome_ns.namespace('hmc5883l')
hmc5883l_ns = cg.esphome_ns.namespace("hmc5883l")
CONF_FIELD_STRENGTH_X = 'field_strength_x'
CONF_FIELD_STRENGTH_Y = 'field_strength_y'
CONF_FIELD_STRENGTH_Z = 'field_strength_z'
CONF_HEADING = 'heading'
CONF_FIELD_STRENGTH_X = "field_strength_x"
CONF_FIELD_STRENGTH_Y = "field_strength_y"
CONF_FIELD_STRENGTH_Z = "field_strength_z"
CONF_HEADING = "heading"
HMC5883LComponent = hmc5883l_ns.class_('HMC5883LComponent', cg.PollingComponent, i2c.I2CDevice)
HMC5883LComponent = hmc5883l_ns.class_(
"HMC5883LComponent", cg.PollingComponent, i2c.I2CDevice
)
HMC5883LOversampling = hmc5883l_ns.enum('HMC5883LOversampling')
HMC5883LOversampling = hmc5883l_ns.enum("HMC5883LOversampling")
HMC5883LOversamplings = {
1: HMC5883LOversampling.HMC5883L_OVERSAMPLING_1,
2: HMC5883LOversampling.HMC5883L_OVERSAMPLING_2,
@ -24,7 +35,7 @@ HMC5883LOversamplings = {
8: HMC5883LOversampling.HMC5883L_OVERSAMPLING_8,
}
HMC5883LDatarate = hmc5883l_ns.enum('HMC5883LDatarate')
HMC5883LDatarate = hmc5883l_ns.enum("HMC5883LDatarate")
HMC5883LDatarates = {
0.75: HMC5883LDatarate.HMC5883L_DATARATE_0_75_HZ,
1.5: HMC5883LDatarate.HMC5883L_DATARATE_1_5_HZ,
@ -35,7 +46,7 @@ HMC5883LDatarates = {
75: HMC5883LDatarate.HMC5883L_DATARATE_75_0_HZ,
}
HMC5883LRange = hmc5883l_ns.enum('HMC5883LRange')
HMC5883LRange = hmc5883l_ns.enum("HMC5883LRange")
HMC5883L_RANGES = {
88: HMC5883LRange.HMC5883L_RANGE_88_UT,
130: HMC5883LRange.HMC5883L_RANGE_130_UT,
@ -59,30 +70,45 @@ def validate_enum(enum_values, units=None, int=True):
value = cv.string(value)
for unit in _units:
if value.endswith(unit):
value = value[:-len(unit)]
value = value[: -len(unit)]
break
return enum_bound(value)
return validate_enum_bound
field_strength_schema = sensor.sensor_schema(UNIT_MICROTESLA, ICON_MAGNET, 1)
heading_schema = sensor.sensor_schema(UNIT_DEGREES, ICON_SCREEN_ROTATION, 1)
field_strength_schema = sensor.sensor_schema(
UNIT_MICROTESLA, ICON_MAGNET, 1, DEVICE_CLASS_EMPTY
)
heading_schema = sensor.sensor_schema(
UNIT_DEGREES, ICON_SCREEN_ROTATION, 1, DEVICE_CLASS_EMPTY
)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(HMC5883LComponent),
cv.Optional(CONF_ADDRESS): cv.i2c_address,
cv.Optional(CONF_OVERSAMPLING, default='1x'): validate_enum(HMC5883LOversamplings, units="x"),
cv.Optional(CONF_RANGE, default='130µT'): validate_enum(HMC5883L_RANGES, units=["uT", "µT"]),
cv.Optional(CONF_FIELD_STRENGTH_X): field_strength_schema,
cv.Optional(CONF_FIELD_STRENGTH_Y): field_strength_schema,
cv.Optional(CONF_FIELD_STRENGTH_Z): field_strength_schema,
cv.Optional(CONF_HEADING): heading_schema,
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x1E))
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(HMC5883LComponent),
cv.Optional(CONF_ADDRESS): cv.i2c_address,
cv.Optional(CONF_OVERSAMPLING, default="1x"): validate_enum(
HMC5883LOversamplings, units="x"
),
cv.Optional(CONF_RANGE, default="130µT"): validate_enum(
HMC5883L_RANGES, units=["uT", "µT"]
),
cv.Optional(CONF_FIELD_STRENGTH_X): field_strength_schema,
cv.Optional(CONF_FIELD_STRENGTH_Y): field_strength_schema,
cv.Optional(CONF_FIELD_STRENGTH_Z): field_strength_schema,
cv.Optional(CONF_HEADING): heading_schema,
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x1E))
)
def auto_data_rate(config):
interval_sec = config[CONF_UPDATE_INTERVAL].seconds
interval_hz = 1.0/interval_sec
interval_hz = 1.0 / interval_sec
for datarate in sorted(HMC5883LDatarates.keys()):
if float(datarate) >= interval_hz:
return HMC5883LDatarates[datarate]

View File

@ -1,4 +1,4 @@
import esphome.codegen as cg
CODEOWNERS = ['@OttoWinter']
homeassistant_ns = cg.esphome_ns.namespace('homeassistant')
CODEOWNERS = ["@OttoWinter"]
homeassistant_ns = cg.esphome_ns.namespace("homeassistant")

View File

@ -4,15 +4,17 @@ from esphome.components import binary_sensor
from esphome.const import CONF_ENTITY_ID, CONF_ID
from .. import homeassistant_ns
DEPENDENCIES = ['api']
HomeassistantBinarySensor = homeassistant_ns.class_('HomeassistantBinarySensor',
binary_sensor.BinarySensor,
cg.Component)
DEPENDENCIES = ["api"]
HomeassistantBinarySensor = homeassistant_ns.class_(
"HomeassistantBinarySensor", binary_sensor.BinarySensor, cg.Component
)
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(HomeassistantBinarySensor),
cv.Required(CONF_ENTITY_ID): cv.entity_id,
}).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(HomeassistantBinarySensor),
cv.Required(CONF_ENTITY_ID): cv.entity_id,
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config):

View File

@ -1,18 +1,29 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import CONF_ENTITY_ID, CONF_ID, ICON_EMPTY, UNIT_EMPTY
from esphome.const import (
CONF_ENTITY_ID,
CONF_ID,
ICON_EMPTY,
UNIT_EMPTY,
DEVICE_CLASS_EMPTY,
)
from .. import homeassistant_ns
DEPENDENCIES = ['api']
DEPENDENCIES = ["api"]
HomeassistantSensor = homeassistant_ns.class_('HomeassistantSensor', sensor.Sensor,
cg.Component)
HomeassistantSensor = homeassistant_ns.class_(
"HomeassistantSensor", sensor.Sensor, cg.Component
)
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 1).extend({
cv.GenerateID(): cv.declare_id(HomeassistantSensor),
cv.Required(CONF_ENTITY_ID): cv.entity_id,
})
CONFIG_SCHEMA = sensor.sensor_schema(
UNIT_EMPTY, ICON_EMPTY, 1, DEVICE_CLASS_EMPTY
).extend(
{
cv.GenerateID(): cv.declare_id(HomeassistantSensor),
cv.Required(CONF_ENTITY_ID): cv.entity_id,
}
)
def to_code(config):

View File

@ -4,15 +4,18 @@ from esphome.components import text_sensor
from esphome.const import CONF_ENTITY_ID, CONF_ID
from .. import homeassistant_ns
DEPENDENCIES = ['api']
DEPENDENCIES = ["api"]
HomeassistantTextSensor = homeassistant_ns.class_('HomeassistantTextSensor',
text_sensor.TextSensor, cg.Component)
HomeassistantTextSensor = homeassistant_ns.class_(
"HomeassistantTextSensor", text_sensor.TextSensor, cg.Component
)
CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(HomeassistantTextSensor),
cv.Required(CONF_ENTITY_ID): cv.entity_id,
})
CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(HomeassistantTextSensor),
cv.Required(CONF_ENTITY_ID): cv.entity_id,
}
)
def to_code(config):

View File

@ -4,17 +4,19 @@ import esphome.codegen as cg
from esphome.const import CONF_ID
from .. import homeassistant_ns
DEPENDENCIES = ['api']
DEPENDENCIES = ["api"]
HomeassistantTime = homeassistant_ns.class_('HomeassistantTime', time_.RealTimeClock)
HomeassistantTime = homeassistant_ns.class_("HomeassistantTime", time_.RealTimeClock)
CONFIG_SCHEMA = time_.TIME_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(HomeassistantTime),
}).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = time_.TIME_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(HomeassistantTime),
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield time_.register_time(var, config)
yield cg.register_component(var, config)
cg.add_define('USE_HOMEASSISTANT_TIME')
cg.add_define("USE_HOMEASSISTANT_TIME")

View File

@ -3,39 +3,59 @@ import urllib.parse as urlparse
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.const import CONF_ID, CONF_TIMEOUT, CONF_ESPHOME, CONF_METHOD, \
CONF_ARDUINO_VERSION, ARDUINO_VERSION_ESP8266, CONF_URL
from esphome.const import (
CONF_ID,
CONF_TIMEOUT,
CONF_ESPHOME,
CONF_METHOD,
CONF_ARDUINO_VERSION,
ARDUINO_VERSION_ESP8266,
CONF_TRIGGER_ID,
CONF_URL,
)
from esphome.core import CORE, Lambda
from esphome.core_config import PLATFORMIO_ESP8266_LUT
DEPENDENCIES = ['network']
AUTO_LOAD = ['json']
DEPENDENCIES = ["network"]
AUTO_LOAD = ["json"]
http_request_ns = cg.esphome_ns.namespace('http_request')
HttpRequestComponent = http_request_ns.class_('HttpRequestComponent', cg.Component)
HttpRequestSendAction = http_request_ns.class_('HttpRequestSendAction', automation.Action)
http_request_ns = cg.esphome_ns.namespace("http_request")
HttpRequestComponent = http_request_ns.class_("HttpRequestComponent", cg.Component)
HttpRequestSendAction = http_request_ns.class_(
"HttpRequestSendAction", automation.Action
)
HttpRequestResponseTrigger = http_request_ns.class_(
"HttpRequestResponseTrigger", automation.Trigger
)
CONF_HEADERS = 'headers'
CONF_USERAGENT = 'useragent'
CONF_BODY = 'body'
CONF_JSON = 'json'
CONF_VERIFY_SSL = 'verify_ssl'
CONF_HEADERS = "headers"
CONF_USERAGENT = "useragent"
CONF_BODY = "body"
CONF_JSON = "json"
CONF_VERIFY_SSL = "verify_ssl"
CONF_ON_RESPONSE = "on_response"
def validate_framework(config):
if CORE.is_esp32:
return config
version = 'RECOMMENDED'
version = "RECOMMENDED"
if CONF_ARDUINO_VERSION in CORE.raw_config[CONF_ESPHOME]:
version = CORE.raw_config[CONF_ESPHOME][CONF_ARDUINO_VERSION]
if version in ['LATEST', 'DEV']:
if version in ["LATEST", "DEV"]:
return config
framework = PLATFORMIO_ESP8266_LUT[version] if version in PLATFORMIO_ESP8266_LUT else version
if framework < ARDUINO_VERSION_ESP8266['2.5.1']:
raise cv.Invalid('This component is not supported on arduino framework version below 2.5.1')
framework = (
PLATFORMIO_ESP8266_LUT[version]
if version in PLATFORMIO_ESP8266_LUT
else version
)
if framework < ARDUINO_VERSION_ESP8266["2.5.1"]:
raise cv.Invalid(
"This component is not supported on arduino framework version below 2.5.1"
)
return config
@ -44,34 +64,47 @@ def validate_url(value):
try:
parsed = list(urlparse.urlparse(value))
except Exception as err:
raise cv.Invalid('Invalid URL') from err
raise cv.Invalid("Invalid URL") from err
if not parsed[0] or not parsed[1]:
raise cv.Invalid('URL must have a URL scheme and host')
raise cv.Invalid("URL must have a URL scheme and host")
if parsed[0] not in ['http', 'https']:
raise cv.Invalid('Scheme must be http or https')
if parsed[0] not in ["http", "https"]:
raise cv.Invalid("Scheme must be http or https")
if not parsed[2]:
parsed[2] = '/'
parsed[2] = "/"
return urlparse.urlunparse(parsed)
def validate_secure_url(config):
url_ = config[CONF_URL]
if config.get(CONF_VERIFY_SSL) and not isinstance(url_, Lambda) \
and url_.lower().startswith('https:'):
raise cv.Invalid('Currently ESPHome doesn\'t support SSL verification. '
'Set \'verify_ssl: false\' to make insecure HTTPS requests.')
if (
config.get(CONF_VERIFY_SSL)
and not isinstance(url_, Lambda)
and url_.lower().startswith("https:")
):
raise cv.Invalid(
"Currently ESPHome doesn't support SSL verification. "
"Set 'verify_ssl: false' to make insecure HTTPS requests."
)
return config
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(HttpRequestComponent),
cv.Optional(CONF_USERAGENT, 'ESPHome'): cv.string,
cv.Optional(CONF_TIMEOUT, default='5s'): cv.positive_time_period_milliseconds,
}).add_extra(validate_framework).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(HttpRequestComponent),
cv.Optional(CONF_USERAGENT, "ESPHome"): cv.string,
cv.Optional(
CONF_TIMEOUT, default="5s"
): cv.positive_time_period_milliseconds,
}
)
.add_extra(validate_framework)
.extend(cv.COMPONENT_SCHEMA)
)
def to_code(config):
@ -81,41 +114,63 @@ def to_code(config):
yield cg.register_component(var, config)
HTTP_REQUEST_ACTION_SCHEMA = cv.Schema({
cv.GenerateID(): cv.use_id(HttpRequestComponent),
cv.Required(CONF_URL): cv.templatable(validate_url),
cv.Optional(CONF_HEADERS): cv.All(cv.Schema({cv.string: cv.templatable(cv.string)})),
cv.Optional(CONF_VERIFY_SSL, default=True): cv.boolean,
}).add_extra(validate_secure_url)
HTTP_REQUEST_ACTION_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.use_id(HttpRequestComponent),
cv.Required(CONF_URL): cv.templatable(validate_url),
cv.Optional(CONF_HEADERS): cv.All(
cv.Schema({cv.string: cv.templatable(cv.string)})
),
cv.Optional(CONF_VERIFY_SSL, default=True): cv.boolean,
cv.Optional(CONF_ON_RESPONSE): automation.validate_automation(
{cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(HttpRequestResponseTrigger)}
),
}
).add_extra(validate_secure_url)
HTTP_REQUEST_GET_ACTION_SCHEMA = automation.maybe_conf(
CONF_URL, HTTP_REQUEST_ACTION_SCHEMA.extend({
cv.Optional(CONF_METHOD, default='GET'): cv.one_of('GET', upper=True),
})
CONF_URL,
HTTP_REQUEST_ACTION_SCHEMA.extend(
{
cv.Optional(CONF_METHOD, default="GET"): cv.one_of("GET", upper=True),
}
),
)
HTTP_REQUEST_POST_ACTION_SCHEMA = automation.maybe_conf(
CONF_URL, HTTP_REQUEST_ACTION_SCHEMA.extend({
cv.Optional(CONF_METHOD, default='POST'): cv.one_of('POST', upper=True),
cv.Exclusive(CONF_BODY, 'body'): cv.templatable(cv.string),
cv.Exclusive(CONF_JSON, 'body'): cv.Any(
cv.lambda_, cv.All(cv.Schema({cv.string: cv.templatable(cv.string_strict)})),
),
})
)
HTTP_REQUEST_SEND_ACTION_SCHEMA = HTTP_REQUEST_ACTION_SCHEMA.extend({
cv.Required(CONF_METHOD): cv.one_of('GET', 'POST', 'PUT', 'DELETE', 'PATCH', upper=True),
cv.Exclusive(CONF_BODY, 'body'): cv.templatable(cv.string),
cv.Exclusive(CONF_JSON, 'body'): cv.Any(
cv.lambda_, cv.All(cv.Schema({cv.string: cv.templatable(cv.string_strict)})),
CONF_URL,
HTTP_REQUEST_ACTION_SCHEMA.extend(
{
cv.Optional(CONF_METHOD, default="POST"): cv.one_of("POST", upper=True),
cv.Exclusive(CONF_BODY, "body"): cv.templatable(cv.string),
cv.Exclusive(CONF_JSON, "body"): cv.Any(
cv.lambda_,
cv.All(cv.Schema({cv.string: cv.templatable(cv.string_strict)})),
),
}
),
})
)
HTTP_REQUEST_SEND_ACTION_SCHEMA = HTTP_REQUEST_ACTION_SCHEMA.extend(
{
cv.Required(CONF_METHOD): cv.one_of(
"GET", "POST", "PUT", "DELETE", "PATCH", upper=True
),
cv.Exclusive(CONF_BODY, "body"): cv.templatable(cv.string),
cv.Exclusive(CONF_JSON, "body"): cv.Any(
cv.lambda_,
cv.All(cv.Schema({cv.string: cv.templatable(cv.string_strict)})),
),
}
)
@automation.register_action('http_request.get', HttpRequestSendAction,
HTTP_REQUEST_GET_ACTION_SCHEMA)
@automation.register_action('http_request.post', HttpRequestSendAction,
HTTP_REQUEST_POST_ACTION_SCHEMA)
@automation.register_action('http_request.send', HttpRequestSendAction,
HTTP_REQUEST_SEND_ACTION_SCHEMA)
@automation.register_action(
"http_request.get", HttpRequestSendAction, HTTP_REQUEST_GET_ACTION_SCHEMA
)
@automation.register_action(
"http_request.post", HttpRequestSendAction, HTTP_REQUEST_POST_ACTION_SCHEMA
)
@automation.register_action(
"http_request.send", HttpRequestSendAction, HTTP_REQUEST_SEND_ACTION_SCHEMA
)
def http_request_action_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
@ -129,7 +184,7 @@ def http_request_action_to_code(config, action_id, template_arg, args):
if CONF_JSON in config:
json_ = config[CONF_JSON]
if isinstance(json_, Lambda):
args_ = args + [(cg.JsonObjectRef, 'root')]
args_ = args + [(cg.JsonObjectRef, "root")]
lambda_ = yield cg.process_lambda(json_, args_, return_type=cg.void)
cg.add(var.set_json(lambda_))
else:
@ -137,7 +192,14 @@ def http_request_action_to_code(config, action_id, template_arg, args):
template_ = yield cg.templatable(json_[key], args, cg.std_string)
cg.add(var.add_json(key, template_))
for key in config.get(CONF_HEADERS, []):
template_ = yield cg.templatable(config[CONF_HEADERS][key], args, cg.const_char_ptr)
template_ = yield cg.templatable(
config[CONF_HEADERS][key], args, cg.const_char_ptr
)
cg.add(var.add_header(key, template_))
for conf in config.get(CONF_ON_RESPONSE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID])
cg.add(var.register_response_trigger(trigger))
yield automation.build_automation(trigger, [(int, "status_code")], conf)
yield var

View File

@ -24,7 +24,7 @@ void HttpRequestComponent::set_url(std::string url) {
this->client_.setReuse(true);
}
void HttpRequestComponent::send() {
void HttpRequestComponent::send(const std::vector<HttpRequestResponseTrigger *> &response_triggers) {
bool begin_status = false;
const String url = this->url_.c_str();
#ifdef ARDUINO_ARCH_ESP32
@ -54,6 +54,9 @@ void HttpRequestComponent::send() {
}
int http_code = this->client_.sendRequest(this->method_, this->body_.c_str());
for (auto *trigger : response_triggers)
trigger->process(http_code);
if (http_code < 0) {
ESP_LOGW(TAG, "HTTP Request failed; URL: %s; Error: %s", this->url_.c_str(),
HTTPClient::errorToString(http_code).c_str());

View File

@ -22,6 +22,8 @@ struct Header {
const char *value;
};
class HttpRequestResponseTrigger;
class HttpRequestComponent : public Component {
public:
void dump_config() override;
@ -33,7 +35,7 @@ class HttpRequestComponent : public Component {
void set_timeout(uint16_t timeout) { this->timeout_ = timeout; }
void set_body(std::string body) { this->body_ = body; }
void set_headers(std::list<Header> headers) { this->headers_ = headers; }
void send();
void send(const std::vector<HttpRequestResponseTrigger *> &response_triggers);
void close();
const char *get_string();
@ -69,6 +71,8 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> {
void set_json(std::function<void(Ts..., JsonObject &)> json_func) { this->json_func_ = json_func; }
void register_response_trigger(HttpRequestResponseTrigger *trigger) { this->response_triggers_.push_back(trigger); }
void play(Ts... x) override {
this->parent_->set_url(this->url_.value(x...));
this->parent_->set_method(this->method_.value(x...));
@ -100,7 +104,7 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> {
}
this->parent_->set_headers(headers);
}
this->parent_->send();
this->parent_->send(this->response_triggers_);
this->parent_->close();
}
@ -116,6 +120,12 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> {
std::map<const char *, TemplatableValue<const char *, Ts...>> headers_{};
std::map<const char *, TemplatableValue<std::string, Ts...>> json_{};
std::function<void(Ts..., JsonObject &)> json_func_{nullptr};
std::vector<HttpRequestResponseTrigger *> response_triggers_;
};
class HttpRequestResponseTrigger : public Trigger<int> {
public:
void process(int status_code) { this->trigger(status_code); }
};
} // namespace http_request

View File

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

View File

@ -2,26 +2,39 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import sensor
from esphome.const import CONF_CLK_PIN, CONF_GAIN, CONF_ID, ICON_SCALE
from esphome.const import (
CONF_CLK_PIN,
CONF_GAIN,
CONF_ID,
DEVICE_CLASS_EMPTY,
ICON_SCALE,
UNIT_EMPTY,
)
hx711_ns = cg.esphome_ns.namespace('hx711')
HX711Sensor = hx711_ns.class_('HX711Sensor', sensor.Sensor, cg.PollingComponent)
hx711_ns = cg.esphome_ns.namespace("hx711")
HX711Sensor = hx711_ns.class_("HX711Sensor", sensor.Sensor, cg.PollingComponent)
CONF_DOUT_PIN = 'dout_pin'
CONF_DOUT_PIN = "dout_pin"
HX711Gain = hx711_ns.enum('HX711Gain')
HX711Gain = hx711_ns.enum("HX711Gain")
GAINS = {
128: HX711Gain.HX711_GAIN_128,
32: HX711Gain.HX711_GAIN_32,
64: HX711Gain.HX711_GAIN_64,
}
CONFIG_SCHEMA = sensor.sensor_schema('', ICON_SCALE, 0).extend({
cv.GenerateID(): cv.declare_id(HX711Sensor),
cv.Required(CONF_DOUT_PIN): pins.gpio_input_pin_schema,
cv.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_GAIN, default=128): cv.enum(GAINS, int=True),
}).extend(cv.polling_component_schema('60s'))
CONFIG_SCHEMA = (
sensor.sensor_schema(UNIT_EMPTY, ICON_SCALE, 0, DEVICE_CLASS_EMPTY)
.extend(
{
cv.GenerateID(): cv.declare_id(HX711Sensor),
cv.Required(CONF_DOUT_PIN): pins.gpio_input_pin_schema,
cv.Required(CONF_CLK_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_GAIN, default=128): cv.enum(GAINS, int=True),
}
)
.extend(cv.polling_component_schema("60s"))
)
def to_code(config):

View File

@ -1,24 +1,34 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.const import CONF_FREQUENCY, CONF_ID, CONF_SCAN, CONF_SCL, CONF_SDA, CONF_ADDRESS, \
CONF_I2C_ID
from esphome.const import (
CONF_FREQUENCY,
CONF_ID,
CONF_SCAN,
CONF_SCL,
CONF_SDA,
CONF_ADDRESS,
CONF_I2C_ID,
)
from esphome.core import coroutine, coroutine_with_priority
CODEOWNERS = ['@esphome/core']
i2c_ns = cg.esphome_ns.namespace('i2c')
I2CComponent = i2c_ns.class_('I2CComponent', cg.Component)
I2CDevice = i2c_ns.class_('I2CDevice')
CODEOWNERS = ["@esphome/core"]
i2c_ns = cg.esphome_ns.namespace("i2c")
I2CComponent = i2c_ns.class_("I2CComponent", cg.Component)
I2CDevice = i2c_ns.class_("I2CDevice")
MULTI_CONF = True
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(I2CComponent),
cv.Optional(CONF_SDA, default='SDA'): pins.input_pin,
cv.Optional(CONF_SCL, default='SCL'): pins.input_pin,
cv.Optional(CONF_FREQUENCY, default='50kHz'):
cv.All(cv.frequency, cv.Range(min=0, min_included=False)),
cv.Optional(CONF_SCAN, default=True): cv.boolean,
}).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(I2CComponent),
cv.Optional(CONF_SDA, default="SDA"): pins.input_pin,
cv.Optional(CONF_SCL, default="SCL"): pins.input_pin,
cv.Optional(CONF_FREQUENCY, default="50kHz"): cv.All(
cv.frequency, cv.Range(min=0, min_included=False)
),
cv.Optional(CONF_SCAN, default=True): cv.boolean,
}
).extend(cv.COMPONENT_SCHEMA)
@coroutine_with_priority(1.0)
@ -31,7 +41,7 @@ def to_code(config):
cg.add(var.set_scl_pin(config[CONF_SCL]))
cg.add(var.set_frequency(int(config[CONF_FREQUENCY])))
cg.add(var.set_scan(config[CONF_SCAN]))
cg.add_library('Wire', None)
cg.add_library("Wire", None)
def i2c_device_schema(default_address):

View File

@ -2,42 +2,55 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import display, spi
from esphome.const import CONF_DC_PIN, \
CONF_ID, CONF_LAMBDA, CONF_MODEL, CONF_PAGES, CONF_RESET_PIN
from esphome.const import (
CONF_DC_PIN,
CONF_ID,
CONF_LAMBDA,
CONF_MODEL,
CONF_PAGES,
CONF_RESET_PIN,
)
DEPENDENCIES = ['spi']
DEPENDENCIES = ["spi"]
CONF_LED_PIN = 'led_pin'
CONF_LED_PIN = "led_pin"
ili9341_ns = cg.esphome_ns.namespace('ili9341')
ili9341 = ili9341_ns.class_('ILI9341Display', cg.PollingComponent, spi.SPIDevice,
display.DisplayBuffer)
ILI9341M5Stack = ili9341_ns.class_('ILI9341M5Stack', ili9341)
ILI9341TFT24 = ili9341_ns.class_('ILI9341TFT24', ili9341)
ili9341_ns = cg.esphome_ns.namespace("ili9341")
ili9341 = ili9341_ns.class_(
"ILI9341Display", cg.PollingComponent, spi.SPIDevice, display.DisplayBuffer
)
ILI9341M5Stack = ili9341_ns.class_("ILI9341M5Stack", ili9341)
ILI9341TFT24 = ili9341_ns.class_("ILI9341TFT24", ili9341)
ILI9341Model = ili9341_ns.enum('ILI9341Model')
ILI9341Model = ili9341_ns.enum("ILI9341Model")
MODELS = {
'M5STACK': ILI9341Model.M5STACK,
'TFT_2.4': ILI9341Model.TFT_24,
"M5STACK": ILI9341Model.M5STACK,
"TFT_2.4": ILI9341Model.TFT_24,
}
ILI9341_MODEL = cv.enum(MODELS, upper=True, space="_")
CONFIG_SCHEMA = cv.All(display.FULL_DISPLAY_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(ili9341),
cv.Required(CONF_MODEL): ILI9341_MODEL,
cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_LED_PIN): pins.gpio_output_pin_schema,
}).extend(cv.polling_component_schema('1s')).extend(spi.spi_device_schema()),
cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA))
CONFIG_SCHEMA = cv.All(
display.FULL_DISPLAY_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(ili9341),
cv.Required(CONF_MODEL): ILI9341_MODEL,
cv.Required(CONF_DC_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_RESET_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_LED_PIN): pins.gpio_output_pin_schema,
}
)
.extend(cv.polling_component_schema("1s"))
.extend(spi.spi_device_schema()),
cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA),
)
def to_code(config):
if config[CONF_MODEL] == 'M5STACK':
if config[CONF_MODEL] == "M5STACK":
lcd_type = ILI9341M5Stack
if config[CONF_MODEL] == 'TFT_2.4':
if config[CONF_MODEL] == "TFT_2.4":
lcd_type = ILI9341TFT24
rhs = lcd_type.new()
var = cg.Pvariable(config[CONF_ID], rhs)
@ -50,8 +63,9 @@ def to_code(config):
cg.add(var.set_dc_pin(dc))
if CONF_LAMBDA in config:
lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')],
return_type=cg.void)
lambda_ = yield cg.process_lambda(
config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void
)
cg.add(var.set_writer(lambda_))
if CONF_RESET_PIN in config:
reset = yield cg.gpio_pin_expression(config[CONF_RESET_PIN])

View File

@ -130,7 +130,7 @@ uint8_t ILI9341Display::convert_to_8bit_color_(uint16_t color_16bit) {
}
void ILI9341Display::fill(Color color) {
auto color565 = color.to_rgb_565();
auto color565 = display::ColorUtil::color_to_565(color);
memset(this->buffer_, convert_to_8bit_color_(color565), this->get_buffer_length_());
this->x_low_ = 0;
this->y_low_ = 0;
@ -142,7 +142,7 @@ void ILI9341Display::fill_internal_(Color color) {
this->set_addr_window_(0, 0, this->get_width_internal(), this->get_height_internal());
this->start_data_();
auto color565 = color.to_rgb_565();
auto color565 = display::ColorUtil::color_to_565(color);
for (uint32_t i = 0; i < (this->get_width_internal()) * (this->get_height_internal()); i++) {
this->write_byte(color565 >> 8);
this->write_byte(color565);
@ -162,7 +162,7 @@ void HOT ILI9341Display::draw_absolute_pixel_internal(int x, int y, Color color)
this->y_high_ = (y > this->y_high_) ? y : this->y_high_;
uint32_t pos = (y * width_) + x;
auto color565 = color.to_rgb_565();
auto color565 = display::ColorUtil::color_to_565(color);
buffer_[pos] = convert_to_8bit_color_(color565);
}

View File

@ -9,28 +9,32 @@ from esphome.core import CORE, HexInt
_LOGGER = logging.getLogger(__name__)
DEPENDENCIES = ['display']
DEPENDENCIES = ["display"]
MULTI_CONF = True
ImageType = display.display_ns.enum('ImageType')
ImageType = display.display_ns.enum("ImageType")
IMAGE_TYPE = {
'BINARY': ImageType.IMAGE_TYPE_BINARY,
'GRAYSCALE': ImageType.IMAGE_TYPE_GRAYSCALE,
'RGB24': ImageType.IMAGE_TYPE_RGB24,
"BINARY": ImageType.IMAGE_TYPE_BINARY,
"GRAYSCALE": ImageType.IMAGE_TYPE_GRAYSCALE,
"RGB24": ImageType.IMAGE_TYPE_RGB24,
}
Image_ = display.display_ns.class_('Image')
Image_ = display.display_ns.class_("Image")
CONF_RAW_DATA_ID = 'raw_data_id'
CONF_RAW_DATA_ID = "raw_data_id"
IMAGE_SCHEMA = cv.Schema({
cv.Required(CONF_ID): cv.declare_id(Image_),
cv.Required(CONF_FILE): cv.file_,
cv.Optional(CONF_RESIZE): cv.dimensions,
cv.Optional(CONF_TYPE, default='BINARY'): cv.enum(IMAGE_TYPE, upper=True),
cv.Optional(CONF_DITHER, default='NONE'): cv.one_of("NONE", "FLOYDSTEINBERG", upper=True),
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
})
IMAGE_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.declare_id(Image_),
cv.Required(CONF_FILE): cv.file_,
cv.Optional(CONF_RESIZE): cv.dimensions,
cv.Optional(CONF_TYPE, default="BINARY"): cv.enum(IMAGE_TYPE, upper=True),
cv.Optional(CONF_DITHER, default="NONE"): cv.one_of(
"NONE", "FLOYDSTEINBERG", upper=True
),
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
}
)
CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, IMAGE_SCHEMA)
@ -51,12 +55,14 @@ def to_code(config):
width, height = image.size
else:
if width > 500 or height > 500:
_LOGGER.warning("The image you requested is very big. Please consider using"
" the resize parameter.")
_LOGGER.warning(
"The image you requested is very big. Please consider using"
" the resize parameter."
)
dither = Image.NONE if config[CONF_DITHER] == 'NONE' else Image.FLOYDSTEINBERG
if config[CONF_TYPE] == 'GRAYSCALE':
image = image.convert('L', dither=dither)
dither = Image.NONE if config[CONF_DITHER] == "NONE" else Image.FLOYDSTEINBERG
if config[CONF_TYPE] == "GRAYSCALE":
image = image.convert("L", dither=dither)
pixels = list(image.getdata())
data = [0 for _ in range(height * width)]
pos = 0
@ -64,8 +70,8 @@ def to_code(config):
data[pos] = pix
pos += 1
elif config[CONF_TYPE] == 'RGB24':
image = image.convert('RGB')
elif config[CONF_TYPE] == "RGB24":
image = image.convert("RGB")
pixels = list(image.getdata())
data = [0 for _ in range(height * width * 3)]
pos = 0
@ -77,8 +83,8 @@ def to_code(config):
data[pos] = pix[2]
pos += 1
elif config[CONF_TYPE] == 'BINARY':
image = image.convert('1', dither=dither)
elif config[CONF_TYPE] == "BINARY":
image = image.convert("1", dither=dither)
width8 = ((width + 7) // 8) * 8
data = [0 for _ in range(height * width8 // 8)]
for y in range(height):
@ -90,5 +96,6 @@ def to_code(config):
rhs = [HexInt(x) for x in data]
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
cg.new_Pvariable(config[CONF_ID], prog_arr, width, height,
IMAGE_TYPE[config[CONF_TYPE]])
cg.new_Pvariable(
config[CONF_ID], prog_arr, width, height, IMAGE_TYPE[config[CONF_TYPE]]
)

View File

@ -1,26 +1,61 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import CONF_BUS_VOLTAGE, CONF_CURRENT, CONF_ID, \
CONF_MAX_CURRENT, CONF_MAX_VOLTAGE, CONF_POWER, CONF_SHUNT_RESISTANCE, \
CONF_SHUNT_VOLTAGE, ICON_FLASH, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT
from esphome.const import (
CONF_BUS_VOLTAGE,
CONF_CURRENT,
CONF_ID,
CONF_MAX_CURRENT,
CONF_MAX_VOLTAGE,
CONF_POWER,
CONF_SHUNT_RESISTANCE,
CONF_SHUNT_VOLTAGE,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_POWER,
DEVICE_CLASS_VOLTAGE,
ICON_EMPTY,
UNIT_VOLT,
UNIT_AMPERE,
UNIT_WATT,
)
DEPENDENCIES = ['i2c']
DEPENDENCIES = ["i2c"]
ina219_ns = cg.esphome_ns.namespace('ina219')
INA219Component = ina219_ns.class_('INA219Component', cg.PollingComponent, i2c.I2CDevice)
ina219_ns = cg.esphome_ns.namespace("ina219")
INA219Component = ina219_ns.class_(
"INA219Component", cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(INA219Component),
cv.Optional(CONF_BUS_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2),
cv.Optional(CONF_SHUNT_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2),
cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 3),
cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 2),
cv.Optional(CONF_SHUNT_RESISTANCE, default=0.1): cv.All(cv.resistance,
cv.Range(min=0.0, max=32.0)),
cv.Optional(CONF_MAX_VOLTAGE, default=32.0): cv.All(cv.voltage, cv.Range(min=0.0, max=32.0)),
cv.Optional(CONF_MAX_CURRENT, default=3.2): cv.All(cv.current, cv.Range(min=0.0)),
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x40))
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(INA219Component),
cv.Optional(CONF_BUS_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE
),
cv.Optional(CONF_SHUNT_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE
),
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 3, DEVICE_CLASS_CURRENT
),
cv.Optional(CONF_POWER): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER
),
cv.Optional(CONF_SHUNT_RESISTANCE, default=0.1): cv.All(
cv.resistance, cv.Range(min=0.0, max=32.0)
),
cv.Optional(CONF_MAX_VOLTAGE, default=32.0): cv.All(
cv.voltage, cv.Range(min=0.0, max=32.0)
),
cv.Optional(CONF_MAX_CURRENT, default=3.2): cv.All(
cv.current, cv.Range(min=0.0)
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x40))
)
def to_code(config):

View File

@ -1,24 +1,57 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import CONF_BUS_VOLTAGE, CONF_CURRENT, CONF_ID, \
CONF_MAX_CURRENT, CONF_POWER, CONF_SHUNT_RESISTANCE, \
CONF_SHUNT_VOLTAGE, ICON_FLASH, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT
from esphome.const import (
CONF_BUS_VOLTAGE,
CONF_CURRENT,
CONF_ID,
CONF_MAX_CURRENT,
CONF_POWER,
CONF_SHUNT_RESISTANCE,
CONF_SHUNT_VOLTAGE,
DEVICE_CLASS_VOLTAGE,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_POWER,
ICON_EMPTY,
UNIT_VOLT,
UNIT_AMPERE,
UNIT_WATT,
)
DEPENDENCIES = ['i2c']
DEPENDENCIES = ["i2c"]
ina226_ns = cg.esphome_ns.namespace('ina226')
INA226Component = ina226_ns.class_('INA226Component', cg.PollingComponent, i2c.I2CDevice)
ina226_ns = cg.esphome_ns.namespace("ina226")
INA226Component = ina226_ns.class_(
"INA226Component", cg.PollingComponent, i2c.I2CDevice
)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(INA226Component),
cv.Optional(CONF_BUS_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2),
cv.Optional(CONF_SHUNT_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2),
cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 3),
cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 2),
cv.Optional(CONF_SHUNT_RESISTANCE, default=0.1): cv.All(cv.resistance, cv.Range(min=0.0)),
cv.Optional(CONF_MAX_CURRENT, default=3.2): cv.All(cv.current, cv.Range(min=0.0)),
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x40))
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(INA226Component),
cv.Optional(CONF_BUS_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE
),
cv.Optional(CONF_SHUNT_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE
),
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
UNIT_AMPERE, ICON_EMPTY, 3, DEVICE_CLASS_CURRENT
),
cv.Optional(CONF_POWER): sensor.sensor_schema(
UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER
),
cv.Optional(CONF_SHUNT_RESISTANCE, default=0.1): cv.All(
cv.resistance, cv.Range(min=0.0)
),
cv.Optional(CONF_MAX_CURRENT, default=3.2): cv.All(
cv.current, cv.Range(min=0.0)
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x40))
)
def to_code(config):

View File

@ -1,34 +1,65 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import CONF_BUS_VOLTAGE, CONF_CURRENT, CONF_ID, CONF_POWER, \
CONF_SHUNT_RESISTANCE, CONF_SHUNT_VOLTAGE, ICON_FLASH, \
UNIT_VOLT, UNIT_AMPERE, UNIT_WATT
from esphome.const import (
CONF_BUS_VOLTAGE,
CONF_CURRENT,
CONF_ID,
CONF_POWER,
CONF_SHUNT_RESISTANCE,
CONF_SHUNT_VOLTAGE,
DEVICE_CLASS_VOLTAGE,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_POWER,
ICON_EMPTY,
UNIT_VOLT,
UNIT_AMPERE,
UNIT_WATT,
)
DEPENDENCIES = ['i2c']
DEPENDENCIES = ["i2c"]
CONF_CHANNEL_1 = 'channel_1'
CONF_CHANNEL_2 = 'channel_2'
CONF_CHANNEL_3 = 'channel_3'
CONF_CHANNEL_1 = "channel_1"
CONF_CHANNEL_2 = "channel_2"
CONF_CHANNEL_3 = "channel_3"
ina3221_ns = cg.esphome_ns.namespace('ina3221')
INA3221Component = ina3221_ns.class_('INA3221Component', cg.PollingComponent, i2c.I2CDevice)
ina3221_ns = cg.esphome_ns.namespace("ina3221")
INA3221Component = ina3221_ns.class_(
"INA3221Component", cg.PollingComponent, i2c.I2CDevice
)
INA3221_CHANNEL_SCHEMA = cv.Schema({
cv.Optional(CONF_BUS_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2),
cv.Optional(CONF_SHUNT_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2),
cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2),
cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 2),
cv.Optional(CONF_SHUNT_RESISTANCE, default=0.1): cv.All(cv.resistance,
cv.Range(min=0.0, max=32.0)),
})
INA3221_CHANNEL_SCHEMA = cv.Schema(
{
cv.Optional(CONF_BUS_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE
),
cv.Optional(CONF_SHUNT_VOLTAGE): sensor.sensor_schema(
UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE
),
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, 2, DEVICE_CLASS_POWER
),
cv.Optional(CONF_SHUNT_RESISTANCE, default=0.1): cv.All(
cv.resistance, cv.Range(min=0.0, max=32.0)
),
}
)
CONFIG_SCHEMA = cv.Schema({
cv.GenerateID(): cv.declare_id(INA3221Component),
cv.Optional(CONF_CHANNEL_1): INA3221_CHANNEL_SCHEMA,
cv.Optional(CONF_CHANNEL_2): INA3221_CHANNEL_SCHEMA,
cv.Optional(CONF_CHANNEL_3): INA3221_CHANNEL_SCHEMA,
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x40))
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(INA3221Component),
cv.Optional(CONF_CHANNEL_1): INA3221_CHANNEL_SCHEMA,
cv.Optional(CONF_CHANNEL_2): INA3221_CHANNEL_SCHEMA,
cv.Optional(CONF_CHANNEL_3): INA3221_CHANNEL_SCHEMA,
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x40))
)
def to_code(config):

View File

@ -0,0 +1,86 @@
#include "inkbird_ibsth1_mini.h"
#include "esphome/core/log.h"
#ifdef ARDUINO_ARCH_ESP32
namespace esphome {
namespace inkbird_ibsth1_mini {
static const char *TAG = "inkbird_ibsth1_mini";
void InkbirdIBSTH1_MINI::dump_config() {
ESP_LOGCONFIG(TAG, "Inkbird IBS TH1 MINI");
LOG_SENSOR(" ", "Temperature", this->temperature_);
LOG_SENSOR(" ", "Humidity", this->humidity_);
LOG_SENSOR(" ", "Battery Level", this->battery_level_);
}
bool InkbirdIBSTH1_MINI::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
// The below is based on my research and reverse engineering of a single device
// It is entirely possible that some of that may be inaccurate or incomplete
// for Inkbird IBS-TH1 Mini device we expect
// 1) expected mac address
// 2) device address type == PUBLIC
// 3) no service datas
// 4) one manufacturer datas
// 5) the manufacturer datas should contain a 16-bit uuid amd a 7-byte data vector
// 6) the 7-byte data component should have data[2] == 0 and data[6] == 8
// the address should match the address we declared
if (device.address_uint64() != this->address_) {
ESP_LOGVV(TAG, "parse_device(): unknown MAC address.");
return false;
}
if (device.get_address_type() != BLE_ADDR_TYPE_PUBLIC) {
ESP_LOGVV(TAG, "parse_device(): address is not public");
return false;
}
if (device.get_service_datas().size() != 0) {
ESP_LOGVV(TAG, "parse_device(): service_data is expected to be empty");
return false;
}
auto mnfDatas = device.get_manufacturer_datas();
if (mnfDatas.size() != 1) {
ESP_LOGVV(TAG, "parse_device(): manufacturer_datas is expected to have a single element");
return false;
}
auto mnfData = mnfDatas[0];
if (mnfData.uuid.get_uuid().len != ESP_UUID_LEN_16) {
ESP_LOGVV(TAG, "parse_device(): manufacturer data element is expected to have uuid of length 16");
return false;
}
if (mnfData.data.size() != 7) {
ESP_LOGVV(TAG, "parse_device(): manufacturer data element length is expected to be of length 7");
return false;
}
if ((mnfData.data[2] != 0) || (mnfData.data[6] != 8)) {
ESP_LOGVV(TAG, "parse_device(): unexpected data");
return false;
}
// sensor output encoding
// data[5] is a battery level
// data[0] and data[1] is humidity * 100 (in pct)
// uuid is a temperature * 100 (in Celcius)
auto battery_level = mnfData.data[5];
auto temperature = mnfData.uuid.get_uuid().uuid.uuid16 / 100.0f;
auto humidity = ((mnfData.data[1] << 8) + mnfData.data[0]) / 100.0f;
if (this->temperature_ != nullptr) {
this->temperature_->publish_state(temperature);
}
if (this->humidity_ != nullptr) {
this->humidity_->publish_state(humidity);
}
if (this->battery_level_ != nullptr) {
this->battery_level_->publish_state(battery_level);
}
return true;
}
} // namespace inkbird_ibsth1_mini
} // namespace esphome
#endif

View File

@ -0,0 +1,34 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
#ifdef ARDUINO_ARCH_ESP32
namespace esphome {
namespace inkbird_ibsth1_mini {
class InkbirdIBSTH1_MINI : public Component, public esp32_ble_tracker::ESPBTDeviceListener {
public:
void set_address(uint64_t address) { address_ = address; }
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
void dump_config() override;
float get_setup_priority() const override { return setup_priority::DATA; }
void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; }
void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; }
void set_battery_level(sensor::Sensor *battery_level) { battery_level_ = battery_level; }
protected:
uint64_t address_;
sensor::Sensor *temperature_{nullptr};
sensor::Sensor *humidity_{nullptr};
sensor::Sensor *battery_level_{nullptr};
};
} // namespace inkbird_ibsth1_mini
} // namespace esphome
#endif

View File

@ -0,0 +1,62 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, esp32_ble_tracker
from esphome.const import (
CONF_BATTERY_LEVEL,
CONF_HUMIDITY,
CONF_MAC_ADDRESS,
CONF_TEMPERATURE,
DEVICE_CLASS_BATTERY,
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
UNIT_CELSIUS,
UNIT_PERCENT,
CONF_ID,
)
CODEOWNERS = ["@fkirill"]
DEPENDENCIES = ["esp32_ble_tracker"]
inkbird_ibsth1_mini_ns = cg.esphome_ns.namespace("inkbird_ibsth1_mini")
InkbirdUBSTH1_MINI = inkbird_ibsth1_mini_ns.class_(
"InkbirdIBSTH1_MINI", esp32_ble_tracker.ESPBTDeviceListener, cg.Component
)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(InkbirdUBSTH1_MINI),
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
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
),
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_BATTERY
),
}
)
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
.extend(cv.COMPONENT_SCHEMA)
)
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config)
yield esp32_ble_tracker.register_ble_device(var, config)
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
if CONF_TEMPERATURE in config:
sens = yield sensor.new_sensor(config[CONF_TEMPERATURE])
cg.add(var.set_temperature(sens))
if CONF_HUMIDITY in config:
sens = yield sensor.new_sensor(config[CONF_HUMIDITY])
cg.add(var.set_humidity(sens))
if CONF_BATTERY_LEVEL in config:
sens = yield sensor.new_sensor(config[CONF_BATTERY_LEVEL])
cg.add(var.set_battery_level(sens))

View File

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

View File

@ -2,67 +2,96 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import display, i2c
from esphome.const import CONF_FULL_UPDATE_EVERY, CONF_ID, CONF_LAMBDA, CONF_PAGES, \
CONF_WAKEUP_PIN, ESP_PLATFORM_ESP32
from esphome.const import (
CONF_FULL_UPDATE_EVERY,
CONF_ID,
CONF_LAMBDA,
CONF_PAGES,
CONF_WAKEUP_PIN,
ESP_PLATFORM_ESP32,
)
DEPENDENCIES = ['i2c']
DEPENDENCIES = ["i2c"]
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
CONF_DISPLAY_DATA_0_PIN = 'display_data_0_pin'
CONF_DISPLAY_DATA_1_PIN = 'display_data_1_pin'
CONF_DISPLAY_DATA_2_PIN = 'display_data_2_pin'
CONF_DISPLAY_DATA_3_PIN = 'display_data_3_pin'
CONF_DISPLAY_DATA_4_PIN = 'display_data_4_pin'
CONF_DISPLAY_DATA_5_PIN = 'display_data_5_pin'
CONF_DISPLAY_DATA_6_PIN = 'display_data_6_pin'
CONF_DISPLAY_DATA_7_PIN = 'display_data_7_pin'
CONF_DISPLAY_DATA_0_PIN = "display_data_0_pin"
CONF_DISPLAY_DATA_1_PIN = "display_data_1_pin"
CONF_DISPLAY_DATA_2_PIN = "display_data_2_pin"
CONF_DISPLAY_DATA_3_PIN = "display_data_3_pin"
CONF_DISPLAY_DATA_4_PIN = "display_data_4_pin"
CONF_DISPLAY_DATA_5_PIN = "display_data_5_pin"
CONF_DISPLAY_DATA_6_PIN = "display_data_6_pin"
CONF_DISPLAY_DATA_7_PIN = "display_data_7_pin"
CONF_CL_PIN = 'cl_pin'
CONF_CKV_PIN = 'ckv_pin'
CONF_GREYSCALE = 'greyscale'
CONF_GMOD_PIN = 'gmod_pin'
CONF_GPIO0_ENABLE_PIN = 'gpio0_enable_pin'
CONF_LE_PIN = 'le_pin'
CONF_OE_PIN = 'oe_pin'
CONF_PARTIAL_UPDATING = 'partial_updating'
CONF_POWERUP_PIN = 'powerup_pin'
CONF_SPH_PIN = 'sph_pin'
CONF_SPV_PIN = 'spv_pin'
CONF_VCOM_PIN = 'vcom_pin'
CONF_CL_PIN = "cl_pin"
CONF_CKV_PIN = "ckv_pin"
CONF_GREYSCALE = "greyscale"
CONF_GMOD_PIN = "gmod_pin"
CONF_GPIO0_ENABLE_PIN = "gpio0_enable_pin"
CONF_LE_PIN = "le_pin"
CONF_OE_PIN = "oe_pin"
CONF_PARTIAL_UPDATING = "partial_updating"
CONF_POWERUP_PIN = "powerup_pin"
CONF_SPH_PIN = "sph_pin"
CONF_SPV_PIN = "spv_pin"
CONF_VCOM_PIN = "vcom_pin"
inkplate6_ns = cg.esphome_ns.namespace('inkplate6')
Inkplate6 = inkplate6_ns.class_('Inkplate6', cg.PollingComponent, i2c.I2CDevice,
display.DisplayBuffer)
inkplate6_ns = cg.esphome_ns.namespace("inkplate6")
Inkplate6 = inkplate6_ns.class_(
"Inkplate6", cg.PollingComponent, i2c.I2CDevice, display.DisplayBuffer
)
CONFIG_SCHEMA = cv.All(display.FULL_DISPLAY_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(Inkplate6),
cv.Optional(CONF_GREYSCALE, default=False): cv.boolean,
cv.Optional(CONF_PARTIAL_UPDATING, default=True): cv.boolean,
cv.Optional(CONF_FULL_UPDATE_EVERY, default=10): cv.uint32_t,
# Control pins
cv.Required(CONF_CKV_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_GMOD_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_GPIO0_ENABLE_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_OE_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_POWERUP_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_SPH_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_SPV_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_VCOM_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_WAKEUP_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_CL_PIN, default=0): pins.internal_gpio_output_pin_schema,
cv.Optional(CONF_LE_PIN, default=2): pins.internal_gpio_output_pin_schema,
# Data pins
cv.Optional(CONF_DISPLAY_DATA_0_PIN, default=4): pins.internal_gpio_output_pin_schema,
cv.Optional(CONF_DISPLAY_DATA_1_PIN, default=5): pins.internal_gpio_output_pin_schema,
cv.Optional(CONF_DISPLAY_DATA_2_PIN, default=18): pins.internal_gpio_output_pin_schema,
cv.Optional(CONF_DISPLAY_DATA_3_PIN, default=19): pins.internal_gpio_output_pin_schema,
cv.Optional(CONF_DISPLAY_DATA_4_PIN, default=23): pins.internal_gpio_output_pin_schema,
cv.Optional(CONF_DISPLAY_DATA_5_PIN, default=25): pins.internal_gpio_output_pin_schema,
cv.Optional(CONF_DISPLAY_DATA_6_PIN, default=26): pins.internal_gpio_output_pin_schema,
cv.Optional(CONF_DISPLAY_DATA_7_PIN, default=27): pins.internal_gpio_output_pin_schema,
}).extend(cv.polling_component_schema('5s').extend(i2c.i2c_device_schema(0x48))),
cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA))
CONFIG_SCHEMA = cv.All(
display.FULL_DISPLAY_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(Inkplate6),
cv.Optional(CONF_GREYSCALE, default=False): cv.boolean,
cv.Optional(CONF_PARTIAL_UPDATING, default=True): cv.boolean,
cv.Optional(CONF_FULL_UPDATE_EVERY, default=10): cv.uint32_t,
# Control pins
cv.Required(CONF_CKV_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_GMOD_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_GPIO0_ENABLE_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_OE_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_POWERUP_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_SPH_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_SPV_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_VCOM_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_WAKEUP_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_CL_PIN, default=0): pins.internal_gpio_output_pin_schema,
cv.Optional(CONF_LE_PIN, default=2): pins.internal_gpio_output_pin_schema,
# Data pins
cv.Optional(
CONF_DISPLAY_DATA_0_PIN, default=4
): pins.internal_gpio_output_pin_schema,
cv.Optional(
CONF_DISPLAY_DATA_1_PIN, default=5
): pins.internal_gpio_output_pin_schema,
cv.Optional(
CONF_DISPLAY_DATA_2_PIN, default=18
): pins.internal_gpio_output_pin_schema,
cv.Optional(
CONF_DISPLAY_DATA_3_PIN, default=19
): pins.internal_gpio_output_pin_schema,
cv.Optional(
CONF_DISPLAY_DATA_4_PIN, default=23
): pins.internal_gpio_output_pin_schema,
cv.Optional(
CONF_DISPLAY_DATA_5_PIN, default=25
): pins.internal_gpio_output_pin_schema,
cv.Optional(
CONF_DISPLAY_DATA_6_PIN, default=26
): pins.internal_gpio_output_pin_schema,
cv.Optional(
CONF_DISPLAY_DATA_7_PIN, default=27
): pins.internal_gpio_output_pin_schema,
}
)
.extend(cv.polling_component_schema("5s"))
.extend(i2c.i2c_device_schema(0x48)),
cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA),
)
def to_code(config):
@ -73,8 +102,9 @@ def to_code(config):
yield i2c.register_i2c_device(var, config)
if CONF_LAMBDA in config:
lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(display.DisplayBufferRef, 'it')],
return_type=cg.void)
lambda_ = yield cg.process_lambda(
config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void
)
cg.add(var.set_writer(lambda_))
cg.add(var.set_greyscale(config[CONF_GREYSCALE]))
@ -138,4 +168,4 @@ def to_code(config):
display_data_7 = yield cg.gpio_pin_expression(config[CONF_DISPLAY_DATA_7_PIN])
cg.add(var.set_display_data_7_pin(display_data_7))
cg.add_build_flag('-DBOARD_HAS_PSRAM')
cg.add_build_flag("-DBOARD_HAS_PSRAM")

View File

@ -204,12 +204,10 @@ void Inkplate6::fill(Color color) {
if (this->greyscale_) {
uint8_t fill = ((color.red * 2126 / 10000) + (color.green * 7152 / 10000) + (color.blue * 722 / 10000)) >> 5;
for (uint32_t i = 0; i < this->get_buffer_length_(); i++)
this->buffer_[i] = (fill << 4) | fill;
memset(this->buffer_, (fill << 4) | fill, this->get_buffer_length_());
} else {
uint8_t fill = color.is_on() ? 0x00 : 0xFF;
for (uint32_t i = 0; i < this->get_buffer_length_(); i++)
this->partial_buffer_[i] = fill;
memset(this->partial_buffer_, fill, this->get_buffer_length_());
}
ESP_LOGV(TAG, "Fill finished (%lums)", millis() - start_time);
@ -233,15 +231,12 @@ void Inkplate6::display1b_() {
ESP_LOGV(TAG, "Display1b called");
unsigned long start_time = millis();
for (int i = 0; i < this->get_buffer_length_(); i++) {
this->buffer_[i] &= this->partial_buffer_[i];
this->buffer_[i] |= this->partial_buffer_[i];
}
memcpy(this->buffer_, this->partial_buffer_, this->get_buffer_length_());
uint16_t pos;
uint32_t send;
uint8_t data;
uint8_t buffer_value;
const uint8_t *buffer_ptr;
eink_on_();
clean_fast_(0, 1);
clean_fast_(1, 5);
@ -252,75 +247,72 @@ void Inkplate6::display1b_() {
clean_fast_(2, 1);
clean_fast_(0, 11);
uint32_t clock = (1 << this->cl_pin_->get_pin());
ESP_LOGV(TAG, "Display1b start loops (%lums)", millis() - start_time);
for (int k = 0; k < 3; k++) {
pos = this->get_buffer_length_() - 1;
buffer_ptr = &this->buffer_[this->get_buffer_length_() - 1];
vscan_start_();
for (int i = 0; i < this->get_height_internal(); i++) {
buffer_value = this->buffer_[pos];
buffer_value = *(buffer_ptr--);
data = LUTB[(buffer_value >> 4) & 0x0F];
send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
hscan_start_(send);
data = LUTB[buffer_value & 0x0F];
send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
pos--;
(((data & B11100000) >> 5) << 25) | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) {
buffer_value = this->buffer_[pos];
for (int j = 0, jm = (this->get_width_internal() / 8) - 1; j < jm; j++) {
buffer_value = *(buffer_ptr--);
data = LUTB[(buffer_value >> 4) & 0x0F];
send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
(((data & B11100000) >> 5) << 25) | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
data = LUTB[buffer_value & 0x0F];
send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
pos--;
(((data & B11100000) >> 5) << 25) | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
}
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
GPIO.out_w1ts = send;
GPIO.out_w1tc = get_data_pin_mask_() | clock;
vscan_end_();
}
delayMicroseconds(230);
}
ESP_LOGV(TAG, "Display1b first loop x %d (%lums)", 3, millis() - start_time);
pos = this->get_buffer_length_() - 1;
buffer_ptr = &this->buffer_[this->get_buffer_length_() - 1];
vscan_start_();
for (int i = 0; i < this->get_height_internal(); i++) {
buffer_value = this->buffer_[pos];
buffer_value = *(buffer_ptr--);
data = LUT2[(buffer_value >> 4) & 0x0F];
send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
hscan_start_(send);
data = LUT2[buffer_value & 0x0F];
send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
pos--;
for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) {
buffer_value = this->buffer_[pos];
(((data & B11100000) >> 5) << 25) | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
for (int j = 0, jm = (this->get_width_internal() / 8) - 1; j < jm; j++) {
buffer_value = *(buffer_ptr--);
data = LUT2[(buffer_value >> 4) & 0x0F];
send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
(((data & B11100000) >> 5) << 25) | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
data = LUT2[buffer_value & 0x0F];
send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
pos--;
(((data & B11100000) >> 5) << 25) | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
}
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
GPIO.out_w1ts = send;
GPIO.out_w1tc = get_data_pin_mask_() | clock;
vscan_end_();
}
delayMicroseconds(230);
@ -328,21 +320,21 @@ void Inkplate6::display1b_() {
vscan_start_();
for (int i = 0; i < this->get_height_internal(); i++) {
buffer_value = this->buffer_[pos];
data = 0b00000000;
send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
hscan_start_(send);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
send |= clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) {
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
}
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
GPIO.out_w1ts = clock;
GPIO.out_w1tc = get_data_pin_mask_() | clock;
vscan_end_();
}
delayMicroseconds(230);
@ -368,8 +360,9 @@ void Inkplate6::display3b_() {
clean_fast_(2, 1);
clean_fast_(0, 11);
uint32_t clock = (1 << this->cl_pin_->get_pin());
for (int k = 0; k < 8; k++) {
uint32_t pos = this->get_buffer_length_() - 1;
const uint8_t *buffer_ptr = &this->buffer_[this->get_buffer_length_() - 1];
uint32_t send;
uint8_t pix1;
uint8_t pix2;
@ -380,10 +373,10 @@ void Inkplate6::display3b_() {
vscan_start_();
for (int i = 0; i < this->get_height_internal(); i++) {
pix1 = this->buffer_[pos--];
pix2 = this->buffer_[pos--];
pix3 = this->buffer_[pos--];
pix4 = this->buffer_[pos--];
pix1 = (*buffer_ptr--);
pix2 = (*buffer_ptr--);
pix3 = (*buffer_ptr--);
pix4 = (*buffer_ptr--);
pixel = (waveform3Bit[pix1 & 0x07][k] << 6) | (waveform3Bit[(pix1 >> 4) & 0x07][k] << 4) |
(waveform3Bit[pix2 & 0x07][k] << 2) | (waveform3Bit[(pix2 >> 4) & 0x07][k] << 0);
pixel2 = (waveform3Bit[pix3 & 0x07][k] << 6) | (waveform3Bit[(pix3 >> 4) & 0x07][k] << 4) |
@ -393,32 +386,32 @@ void Inkplate6::display3b_() {
(((pixel & B11100000) >> 5) << 25);
hscan_start_(send);
send = ((pixel2 & B00000011) << 4) | (((pixel2 & B00001100) >> 2) << 18) | (((pixel2 & B00010000) >> 4) << 23) |
(((pixel2 & B11100000) >> 5) << 25);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
(((pixel2 & B11100000) >> 5) << 25) | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
for (int j = 0; j < (this->get_width_internal() / 8) - 1; j++) {
pix1 = this->buffer_[pos--];
pix2 = this->buffer_[pos--];
pix3 = this->buffer_[pos--];
pix4 = this->buffer_[pos--];
for (int j = 0, jm = (this->get_width_internal() / 8) - 1; j < jm; j++) {
pix1 = (*buffer_ptr--);
pix2 = (*buffer_ptr--);
pix3 = (*buffer_ptr--);
pix4 = (*buffer_ptr--);
pixel = (waveform3Bit[pix1 & 0x07][k] << 6) | (waveform3Bit[(pix1 >> 4) & 0x07][k] << 4) |
(waveform3Bit[pix2 & 0x07][k] << 2) | (waveform3Bit[(pix2 >> 4) & 0x07][k] << 0);
pixel2 = (waveform3Bit[pix3 & 0x07][k] << 6) | (waveform3Bit[(pix3 >> 4) & 0x07][k] << 4) |
(waveform3Bit[pix4 & 0x07][k] << 2) | (waveform3Bit[(pix4 >> 4) & 0x07][k] << 0);
send = ((pixel & B00000011) << 4) | (((pixel & B00001100) >> 2) << 18) | (((pixel & B00010000) >> 4) << 23) |
(((pixel & B11100000) >> 5) << 25);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
(((pixel & B11100000) >> 5) << 25) | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
send = ((pixel2 & B00000011) << 4) | (((pixel2 & B00001100) >> 2) << 18) | (((pixel2 & B00010000) >> 4) << 23) |
(((pixel2 & B11100000) >> 5) << 25);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
(((pixel2 & B11100000) >> 5) << 25) | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
}
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
GPIO.out_w1ts = send;
GPIO.out_w1tc = get_data_pin_mask_() | clock;
vscan_end_();
}
delayMicroseconds(230);
@ -445,8 +438,8 @@ bool Inkplate6::partial_update_() {
uint8_t diffw, diffb;
uint32_t n = (this->get_buffer_length_() * 2) - 1;
for (int i = 0; i < this->get_height_internal(); i++) {
for (int j = 0; j < (this->get_width_internal() / 8); j++) {
for (int i = 0, im = this->get_height_internal(); i < im; i++) {
for (int j = 0, jm = (this->get_width_internal() / 8); j < jm; j++) {
diffw = (this->buffer_[pos] ^ this->partial_buffer_[pos]) & ~(this->partial_buffer_[pos]);
diffb = (this->buffer_[pos] ^ this->partial_buffer_[pos]) & this->partial_buffer_[pos];
pos--;
@ -457,23 +450,24 @@ bool Inkplate6::partial_update_() {
ESP_LOGV(TAG, "Partial update buffer built after (%lums)", millis() - start_time);
eink_on_();
uint32_t clock = (1 << this->cl_pin_->get_pin());
for (int k = 0; k < 5; k++) {
vscan_start_();
n = (this->get_buffer_length_() * 2) - 1;
const uint8_t *data_ptr = &this->partial_buffer_2_[(this->get_buffer_length_() * 2) - 1];
for (int i = 0; i < this->get_height_internal(); i++) {
data = this->partial_buffer_2_[n--];
data = *(data_ptr--);
send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
hscan_start_(send);
for (int j = 0; j < (this->get_width_internal() / 4) - 1; j++) {
data = this->partial_buffer_2_[n--];
for (int j = 0, jm = (this->get_width_internal() / 4) - 1; j < jm; j++) {
data = *(data_ptr--);
send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
(((data & B11100000) >> 5) << 25) | clock;
GPIO.out_w1ts = send;
GPIO.out_w1tc = send;
}
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
GPIO.out_w1ts = send;
GPIO.out_w1tc = get_data_pin_mask_() | clock;
vscan_end_();
}
delayMicroseconds(230);
@ -484,9 +478,7 @@ bool Inkplate6::partial_update_() {
vscan_start_();
eink_off_();
for (int i = 0; i < this->get_buffer_length_(); i++) {
this->buffer_[i] = this->partial_buffer_[i];
}
memcpy(this->buffer_, this->partial_buffer_, this->get_buffer_length_());
ESP_LOGV(TAG, "Partial update finished (%lums)", millis() - start_time);
return true;
}
@ -567,21 +559,22 @@ void Inkplate6::clean_fast_(uint8_t c, uint8_t rep) {
uint32_t send = ((data & B00000011) << 4) | (((data & B00001100) >> 2) << 18) | (((data & B00010000) >> 4) << 23) |
(((data & B11100000) >> 5) << 25);
uint32_t clock = (1 << this->cl_pin_->get_pin());
for (int k = 0; k < rep; k++) {
vscan_start_();
for (int i = 0; i < this->get_height_internal(); i++) {
hscan_start_(send);
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
for (int j = 0; j < this->get_width_internal() / 8; j++) {
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
GPIO.out_w1ts = send | clock;
GPIO.out_w1tc = clock;
for (int j = 0, jm = this->get_width_internal() / 8; j < jm; j++) {
GPIO.out_w1ts = clock;
GPIO.out_w1tc = clock;
GPIO.out_w1ts = clock;
GPIO.out_w1tc = clock;
}
GPIO.out_w1ts = (send) | (1 << this->cl_pin_->get_pin());
GPIO.out_w1tc = get_data_pin_mask_() | (1 << this->cl_pin_->get_pin());
GPIO.out_w1ts = clock;
GPIO.out_w1tc = get_data_pin_mask_() | clock;
vscan_end_();
}
delayMicroseconds(230);

View File

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

View File

@ -4,36 +4,41 @@ from esphome import automation
from esphome.components import sensor
from esphome.const import CONF_ID, CONF_SENSOR, CONF_RESTORE
integration_ns = cg.esphome_ns.namespace('integration')
IntegrationSensor = integration_ns.class_('IntegrationSensor', sensor.Sensor, cg.Component)
ResetAction = integration_ns.class_('ResetAction', automation.Action)
integration_ns = cg.esphome_ns.namespace("integration")
IntegrationSensor = integration_ns.class_(
"IntegrationSensor", sensor.Sensor, cg.Component
)
ResetAction = integration_ns.class_("ResetAction", automation.Action)
IntegrationSensorTime = integration_ns.enum('IntegrationSensorTime')
IntegrationSensorTime = integration_ns.enum("IntegrationSensorTime")
INTEGRATION_TIMES = {
'ms': IntegrationSensorTime.INTEGRATION_SENSOR_TIME_MILLISECOND,
's': IntegrationSensorTime.INTEGRATION_SENSOR_TIME_SECOND,
'min': IntegrationSensorTime.INTEGRATION_SENSOR_TIME_MINUTE,
'h': IntegrationSensorTime.INTEGRATION_SENSOR_TIME_HOUR,
'd': IntegrationSensorTime.INTEGRATION_SENSOR_TIME_DAY,
"ms": IntegrationSensorTime.INTEGRATION_SENSOR_TIME_MILLISECOND,
"s": IntegrationSensorTime.INTEGRATION_SENSOR_TIME_SECOND,
"min": IntegrationSensorTime.INTEGRATION_SENSOR_TIME_MINUTE,
"h": IntegrationSensorTime.INTEGRATION_SENSOR_TIME_HOUR,
"d": IntegrationSensorTime.INTEGRATION_SENSOR_TIME_DAY,
}
IntegrationMethod = integration_ns.enum('IntegrationMethod')
IntegrationMethod = integration_ns.enum("IntegrationMethod")
INTEGRATION_METHODS = {
'trapezoid': IntegrationMethod.INTEGRATION_METHOD_TRAPEZOID,
'left': IntegrationMethod.INTEGRATION_METHOD_LEFT,
'right': IntegrationMethod.INTEGRATION_METHOD_RIGHT,
"trapezoid": IntegrationMethod.INTEGRATION_METHOD_TRAPEZOID,
"left": IntegrationMethod.INTEGRATION_METHOD_LEFT,
"right": IntegrationMethod.INTEGRATION_METHOD_RIGHT,
}
CONF_TIME_UNIT = 'time_unit'
CONF_INTEGRATION_METHOD = 'integration_method'
CONF_TIME_UNIT = "time_unit"
CONF_INTEGRATION_METHOD = "integration_method"
CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(IntegrationSensor),
cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor),
cv.Required(CONF_TIME_UNIT): cv.enum(INTEGRATION_TIMES, lower=True),
cv.Optional(CONF_INTEGRATION_METHOD, default='trapezoid'):
cv.enum(INTEGRATION_METHODS, lower=True),
cv.Optional(CONF_RESTORE, default=False): cv.boolean,
}).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(IntegrationSensor),
cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor),
cv.Required(CONF_TIME_UNIT): cv.enum(INTEGRATION_TIMES, lower=True),
cv.Optional(CONF_INTEGRATION_METHOD, default="trapezoid"): cv.enum(
INTEGRATION_METHODS, lower=True
),
cv.Optional(CONF_RESTORE, default=False): cv.boolean,
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config):
@ -49,9 +54,15 @@ def to_code(config):
cg.add(var.set_restore(config[CONF_RESTORE]))
@automation.register_action('sensor.integration.reset', ResetAction, automation.maybe_simple_id({
cv.Required(CONF_ID): cv.use_id(IntegrationSensor),
}))
@automation.register_action(
"sensor.integration.reset",
ResetAction,
automation.maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(IntegrationSensor),
}
),
)
def sensor_integration_reset_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(action_id, template_arg, paren)

View File

@ -3,15 +3,20 @@ import esphome.config_validation as cv
from esphome import automation
from esphome.const import CONF_ID, CONF_INTERVAL
CODEOWNERS = ['@esphome/core']
interval_ns = cg.esphome_ns.namespace('interval')
IntervalTrigger = interval_ns.class_('IntervalTrigger', automation.Trigger.template(),
cg.PollingComponent)
CODEOWNERS = ["@esphome/core"]
interval_ns = cg.esphome_ns.namespace("interval")
IntervalTrigger = interval_ns.class_(
"IntervalTrigger", automation.Trigger.template(), cg.PollingComponent
)
CONFIG_SCHEMA = automation.validate_automation(cv.Schema({
cv.GenerateID(): cv.declare_id(IntervalTrigger),
cv.Required(CONF_INTERVAL): cv.positive_time_period_milliseconds,
}).extend(cv.COMPONENT_SCHEMA))
CONFIG_SCHEMA = automation.validate_automation(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(IntervalTrigger),
cv.Required(CONF_INTERVAL): cv.positive_time_period_milliseconds,
}
).extend(cv.COMPONENT_SCHEMA)
)
def to_code(config):

View File

@ -1,12 +1,12 @@
import esphome.codegen as cg
from esphome.core import coroutine_with_priority
CODEOWNERS = ['@OttoWinter']
json_ns = cg.esphome_ns.namespace('json')
CODEOWNERS = ["@OttoWinter"]
json_ns = cg.esphome_ns.namespace("json")
@coroutine_with_priority(1.0)
def to_code(config):
cg.add_library('ArduinoJson-esphomelib', '5.13.3')
cg.add_define('USE_JSON')
cg.add_library("ArduinoJson-esphomelib", "5.13.3")
cg.add_define("USE_JSON")
cg.add_global(json_ns.using)

View File

@ -4,8 +4,8 @@ from esphome.components import display
from esphome.const import CONF_DIMENSIONS
from esphome.core import coroutine
lcd_base_ns = cg.esphome_ns.namespace('lcd_base')
LCDDisplay = lcd_base_ns.class_('LCDDisplay', cg.PollingComponent)
lcd_base_ns = cg.esphome_ns.namespace("lcd_base")
LCDDisplay = lcd_base_ns.class_("LCDDisplay", cg.PollingComponent)
def validate_lcd_dimensions(value):
@ -17,9 +17,11 @@ def validate_lcd_dimensions(value):
return value
LCD_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend({
cv.Required(CONF_DIMENSIONS): validate_lcd_dimensions,
}).extend(cv.polling_component_schema('1s'))
LCD_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend(
{
cv.Required(CONF_DIMENSIONS): validate_lcd_dimensions,
}
).extend(cv.polling_component_schema("1s"))
@coroutine

View File

@ -104,9 +104,7 @@ void HOT LCDDisplay::display() {
}
}
void LCDDisplay::update() {
for (uint8_t i = 0; i < this->rows_ * this->columns_; i++)
this->buffer_[i] = ' ';
this->clear();
this->call_writer();
this->display();
}
@ -149,9 +147,8 @@ void LCDDisplay::printf(const char *format, ...) {
this->print(0, 0, buffer);
}
void LCDDisplay::clear() {
// clear display, also sets DDRAM address to 0 (home)
this->command_(LCD_DISPLAY_COMMAND_CLEAR_DISPLAY);
delay(2);
for (uint8_t i = 0; i < this->rows_ * this->columns_; i++)
this->buffer_[i] = ' ';
}
#ifdef USE_TIME
void LCDDisplay::strftime(uint8_t column, uint8_t row, const char *format, time::ESPTime time) {

View File

@ -2,29 +2,41 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import lcd_base
from esphome.const import CONF_DATA_PINS, CONF_ENABLE_PIN, CONF_RS_PIN, CONF_RW_PIN, CONF_ID, \
CONF_LAMBDA
from esphome.const import (
CONF_DATA_PINS,
CONF_ENABLE_PIN,
CONF_RS_PIN,
CONF_RW_PIN,
CONF_ID,
CONF_LAMBDA,
)
AUTO_LOAD = ['lcd_base']
AUTO_LOAD = ["lcd_base"]
lcd_gpio_ns = cg.esphome_ns.namespace('lcd_gpio')
GPIOLCDDisplay = lcd_gpio_ns.class_('GPIOLCDDisplay', lcd_base.LCDDisplay)
lcd_gpio_ns = cg.esphome_ns.namespace("lcd_gpio")
GPIOLCDDisplay = lcd_gpio_ns.class_("GPIOLCDDisplay", lcd_base.LCDDisplay)
def validate_pin_length(value):
if len(value) != 4 and len(value) != 8:
raise cv.Invalid("LCD Displays can either operate in 4-pin or 8-pin mode,"
"not {}-pin mode".format(len(value)))
raise cv.Invalid(
"LCD Displays can either operate in 4-pin or 8-pin mode,"
"not {}-pin mode".format(len(value))
)
return value
CONFIG_SCHEMA = lcd_base.LCD_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(GPIOLCDDisplay),
cv.Required(CONF_DATA_PINS): cv.All([pins.gpio_output_pin_schema], validate_pin_length),
cv.Required(CONF_ENABLE_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_RS_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_RW_PIN): pins.gpio_output_pin_schema,
})
CONFIG_SCHEMA = lcd_base.LCD_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(GPIOLCDDisplay),
cv.Required(CONF_DATA_PINS): cv.All(
[pins.gpio_output_pin_schema], validate_pin_length
),
cv.Required(CONF_ENABLE_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_RS_PIN): pins.gpio_output_pin_schema,
cv.Optional(CONF_RW_PIN): pins.gpio_output_pin_schema,
}
)
def to_code(config):
@ -45,7 +57,9 @@ def to_code(config):
cg.add(var.set_rw_pin(rw))
if CONF_LAMBDA in config:
lambda_ = yield cg.process_lambda(config[CONF_LAMBDA],
[(GPIOLCDDisplay.operator('ref'), 'it')],
return_type=cg.void)
lambda_ = yield cg.process_lambda(
config[CONF_LAMBDA],
[(GPIOLCDDisplay.operator("ref"), "it")],
return_type=cg.void,
)
cg.add(var.set_writer(lambda_))

View File

@ -3,15 +3,19 @@ import esphome.config_validation as cv
from esphome.components import lcd_base, i2c
from esphome.const import CONF_ID, CONF_LAMBDA
DEPENDENCIES = ['i2c']
AUTO_LOAD = ['lcd_base']
DEPENDENCIES = ["i2c"]
AUTO_LOAD = ["lcd_base"]
lcd_pcf8574_ns = cg.esphome_ns.namespace('lcd_pcf8574')
PCF8574LCDDisplay = lcd_pcf8574_ns.class_('PCF8574LCDDisplay', lcd_base.LCDDisplay, i2c.I2CDevice)
lcd_pcf8574_ns = cg.esphome_ns.namespace("lcd_pcf8574")
PCF8574LCDDisplay = lcd_pcf8574_ns.class_(
"PCF8574LCDDisplay", lcd_base.LCDDisplay, i2c.I2CDevice
)
CONFIG_SCHEMA = lcd_base.LCD_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(PCF8574LCDDisplay),
}).extend(i2c.i2c_device_schema(0x3F))
CONFIG_SCHEMA = lcd_base.LCD_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(PCF8574LCDDisplay),
}
).extend(i2c.i2c_device_schema(0x3F))
def to_code(config):
@ -20,7 +24,9 @@ def to_code(config):
yield i2c.register_i2c_device(var, config)
if CONF_LAMBDA in config:
lambda_ = yield cg.process_lambda(config[CONF_LAMBDA],
[(PCF8574LCDDisplay.operator('ref'), 'it')],
return_type=cg.void)
lambda_ = yield cg.process_lambda(
config[CONF_LAMBDA],
[(PCF8574LCDDisplay.operator("ref"), "it")],
return_type=cg.void,
)
cg.add(var.set_writer(lambda_))

View File

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

View File

@ -2,19 +2,25 @@ from esphome import pins, automation
from esphome.components import output
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.const import CONF_BIT_DEPTH, CONF_CHANNEL, CONF_FREQUENCY, \
CONF_ID, CONF_PIN, ESP_PLATFORM_ESP32
from esphome.const import (
CONF_BIT_DEPTH,
CONF_CHANNEL,
CONF_FREQUENCY,
CONF_ID,
CONF_PIN,
ESP_PLATFORM_ESP32,
)
ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
def calc_max_frequency(bit_depth):
return 80e6 / (2**bit_depth)
return 80e6 / (2 ** bit_depth)
def calc_min_frequency(bit_depth):
max_div_num = ((2**20) - 1) / 256.0
return 80e6 / (max_div_num * (2**bit_depth))
max_div_num = ((2 ** 20) - 1) / 256.0
return 80e6 / (max_div_num * (2 ** bit_depth))
def validate_frequency(value):
@ -22,27 +28,34 @@ def validate_frequency(value):
min_freq = calc_min_frequency(20)
max_freq = calc_max_frequency(1)
if value < min_freq:
raise cv.Invalid("This frequency setting is not possible, please choose a higher "
"frequency (at least {}Hz)".format(int(min_freq)))
raise cv.Invalid(
"This frequency setting is not possible, please choose a higher "
"frequency (at least {}Hz)".format(int(min_freq))
)
if value > max_freq:
raise cv.Invalid("This frequency setting is not possible, please choose a lower "
"frequency (at most {}Hz)".format(int(max_freq)))
raise cv.Invalid(
"This frequency setting is not possible, please choose a lower "
"frequency (at most {}Hz)".format(int(max_freq))
)
return value
ledc_ns = cg.esphome_ns.namespace('ledc')
LEDCOutput = ledc_ns.class_('LEDCOutput', output.FloatOutput, cg.Component)
SetFrequencyAction = ledc_ns.class_('SetFrequencyAction', automation.Action)
ledc_ns = cg.esphome_ns.namespace("ledc")
LEDCOutput = ledc_ns.class_("LEDCOutput", output.FloatOutput, cg.Component)
SetFrequencyAction = ledc_ns.class_("SetFrequencyAction", automation.Action)
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({
cv.Required(CONF_ID): cv.declare_id(LEDCOutput),
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema,
cv.Optional(CONF_FREQUENCY, default='1kHz'): cv.frequency,
cv.Optional(CONF_CHANNEL): cv.int_range(min=0, max=15),
cv.Optional(CONF_BIT_DEPTH): cv.invalid("The bit_depth option has been removed in v1.14, the "
"best bit depth is now automatically calculated."),
}).extend(cv.COMPONENT_SCHEMA)
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
{
cv.Required(CONF_ID): cv.declare_id(LEDCOutput),
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_schema,
cv.Optional(CONF_FREQUENCY, default="1kHz"): cv.frequency,
cv.Optional(CONF_CHANNEL): cv.int_range(min=0, max=15),
cv.Optional(CONF_BIT_DEPTH): cv.invalid(
"The bit_depth option has been removed in v1.14, the "
"best bit depth is now automatically calculated."
),
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config):
@ -55,10 +68,16 @@ def to_code(config):
cg.add(var.set_frequency(config[CONF_FREQUENCY]))
@automation.register_action('output.ledc.set_frequency', SetFrequencyAction, cv.Schema({
cv.Required(CONF_ID): cv.use_id(LEDCOutput),
cv.Required(CONF_FREQUENCY): cv.templatable(validate_frequency),
}))
@automation.register_action(
"output.ledc.set_frequency",
SetFrequencyAction,
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(LEDCOutput),
cv.Required(CONF_FREQUENCY): cv.templatable(validate_frequency),
}
),
)
def ledc_set_frequency_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)

View File

@ -2,62 +2,104 @@ import esphome.codegen as cg
import esphome.config_validation as cv
import esphome.automation as auto
from esphome.components import mqtt, power_supply
from esphome.const import CONF_COLOR_CORRECT, \
CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, CONF_ID, \
CONF_INTERNAL, CONF_NAME, CONF_MQTT_ID, CONF_POWER_SUPPLY, CONF_RESTORE_MODE, \
CONF_ON_TURN_OFF, CONF_ON_TURN_ON, CONF_TRIGGER_ID
from esphome.const import (
CONF_COLOR_CORRECT,
CONF_DEFAULT_TRANSITION_LENGTH,
CONF_EFFECTS,
CONF_GAMMA_CORRECT,
CONF_ID,
CONF_INTERNAL,
CONF_NAME,
CONF_MQTT_ID,
CONF_POWER_SUPPLY,
CONF_RESTORE_MODE,
CONF_ON_TURN_OFF,
CONF_ON_TURN_ON,
CONF_TRIGGER_ID,
)
from esphome.core import coroutine, coroutine_with_priority
from .automation import light_control_to_code # noqa
from .effects import validate_effects, BINARY_EFFECTS, \
MONOCHROMATIC_EFFECTS, RGB_EFFECTS, ADDRESSABLE_EFFECTS, EFFECTS_REGISTRY
from .effects import (
validate_effects,
BINARY_EFFECTS,
MONOCHROMATIC_EFFECTS,
RGB_EFFECTS,
ADDRESSABLE_EFFECTS,
EFFECTS_REGISTRY,
)
from .types import ( # noqa
LightState, AddressableLightState, light_ns, LightOutput, AddressableLight, \
LightTurnOnTrigger, LightTurnOffTrigger)
LightState,
AddressableLightState,
light_ns,
LightOutput,
AddressableLight,
LightTurnOnTrigger,
LightTurnOffTrigger,
)
CODEOWNERS = ['@esphome/core']
CODEOWNERS = ["@esphome/core"]
IS_PLATFORM_COMPONENT = True
LightRestoreMode = light_ns.enum('LightRestoreMode')
LightRestoreMode = light_ns.enum("LightRestoreMode")
RESTORE_MODES = {
'RESTORE_DEFAULT_OFF': LightRestoreMode.LIGHT_RESTORE_DEFAULT_OFF,
'RESTORE_DEFAULT_ON': LightRestoreMode.LIGHT_RESTORE_DEFAULT_ON,
'ALWAYS_OFF': LightRestoreMode.LIGHT_ALWAYS_OFF,
'ALWAYS_ON': LightRestoreMode.LIGHT_ALWAYS_ON,
"RESTORE_DEFAULT_OFF": LightRestoreMode.LIGHT_RESTORE_DEFAULT_OFF,
"RESTORE_DEFAULT_ON": LightRestoreMode.LIGHT_RESTORE_DEFAULT_ON,
"ALWAYS_OFF": LightRestoreMode.LIGHT_ALWAYS_OFF,
"ALWAYS_ON": LightRestoreMode.LIGHT_ALWAYS_ON,
}
LIGHT_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(LightState),
cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTJSONLightComponent),
cv.Optional(CONF_RESTORE_MODE, default='restore_default_off'):
cv.enum(RESTORE_MODES, upper=True, space='_'),
cv.Optional(CONF_ON_TURN_ON): auto.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LightTurnOnTrigger),
}),
cv.Optional(CONF_ON_TURN_OFF): auto.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LightTurnOffTrigger),
}),
})
LIGHT_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(LightState),
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTJSONLightComponent),
cv.Optional(CONF_RESTORE_MODE, default="restore_default_off"): cv.enum(
RESTORE_MODES, upper=True, space="_"
),
cv.Optional(CONF_ON_TURN_ON): auto.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LightTurnOnTrigger),
}
),
cv.Optional(CONF_ON_TURN_OFF): auto.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LightTurnOffTrigger),
}
),
}
)
BINARY_LIGHT_SCHEMA = LIGHT_SCHEMA.extend({
cv.Optional(CONF_EFFECTS): validate_effects(BINARY_EFFECTS),
})
BINARY_LIGHT_SCHEMA = LIGHT_SCHEMA.extend(
{
cv.Optional(CONF_EFFECTS): validate_effects(BINARY_EFFECTS),
}
)
BRIGHTNESS_ONLY_LIGHT_SCHEMA = LIGHT_SCHEMA.extend({
cv.Optional(CONF_GAMMA_CORRECT, default=2.8): cv.positive_float,
cv.Optional(CONF_DEFAULT_TRANSITION_LENGTH, default='1s'): cv.positive_time_period_milliseconds,
cv.Optional(CONF_EFFECTS): validate_effects(MONOCHROMATIC_EFFECTS),
})
BRIGHTNESS_ONLY_LIGHT_SCHEMA = LIGHT_SCHEMA.extend(
{
cv.Optional(CONF_GAMMA_CORRECT, default=2.8): cv.positive_float,
cv.Optional(
CONF_DEFAULT_TRANSITION_LENGTH, default="1s"
): cv.positive_time_period_milliseconds,
cv.Optional(CONF_EFFECTS): validate_effects(MONOCHROMATIC_EFFECTS),
}
)
RGB_LIGHT_SCHEMA = BRIGHTNESS_ONLY_LIGHT_SCHEMA.extend({
cv.Optional(CONF_EFFECTS): validate_effects(RGB_EFFECTS),
})
RGB_LIGHT_SCHEMA = BRIGHTNESS_ONLY_LIGHT_SCHEMA.extend(
{
cv.Optional(CONF_EFFECTS): validate_effects(RGB_EFFECTS),
}
)
ADDRESSABLE_LIGHT_SCHEMA = RGB_LIGHT_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(AddressableLightState),
cv.Optional(CONF_EFFECTS): validate_effects(ADDRESSABLE_EFFECTS),
cv.Optional(CONF_COLOR_CORRECT): cv.All([cv.percentage], cv.Length(min=3, max=4)),
cv.Optional(CONF_POWER_SUPPLY): cv.use_id(power_supply.PowerSupply),
})
ADDRESSABLE_LIGHT_SCHEMA = RGB_LIGHT_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(AddressableLightState),
cv.Optional(CONF_EFFECTS): validate_effects(ADDRESSABLE_EFFECTS),
cv.Optional(CONF_COLOR_CORRECT): cv.All(
[cv.percentage], cv.Length(min=3, max=4)
),
cv.Optional(CONF_POWER_SUPPLY): cv.use_id(power_supply.PowerSupply),
}
)
@coroutine
@ -66,10 +108,16 @@ def setup_light_core_(light_var, output_var, config):
if CONF_INTERNAL in config:
cg.add(light_var.set_internal(config[CONF_INTERNAL]))
if CONF_DEFAULT_TRANSITION_LENGTH in config:
cg.add(light_var.set_default_transition_length(config[CONF_DEFAULT_TRANSITION_LENGTH]))
cg.add(
light_var.set_default_transition_length(
config[CONF_DEFAULT_TRANSITION_LENGTH]
)
)
if CONF_GAMMA_CORRECT in config:
cg.add(light_var.set_gamma_correct(config[CONF_GAMMA_CORRECT]))
effects = yield cg.build_registry_list(EFFECTS_REGISTRY, config.get(CONF_EFFECTS, []))
effects = yield cg.build_registry_list(
EFFECTS_REGISTRY, config.get(CONF_EFFECTS, [])
)
cg.add(light_var.add_effects(effects))
for conf in config.get(CONF_ON_TURN_ON, []):
@ -101,5 +149,5 @@ def register_light(output_var, config):
@coroutine_with_priority(100.0)
def to_code(config):
cg.add_define('USE_LIGHT')
cg.add_define("USE_LIGHT")
cg.add_global(light_ns.using)

View File

@ -6,10 +6,7 @@ namespace light {
static const char *TAG = "light.addressable";
const ESPColor ESPColor::BLACK = ESPColor(0, 0, 0, 0);
const ESPColor ESPColor::WHITE = ESPColor(255, 255, 255, 255);
ESPColor ESPHSVColor::to_rgb() const {
Color ESPHSVColor::to_rgb() const {
// based on FastLED's hsv rainbow to rgb
const uint8_t hue = this->hue;
const uint8_t sat = this->saturation;
@ -19,7 +16,7 @@ ESPColor ESPHSVColor::to_rgb() const {
// third of the offset, 255/3 = 85 (actually only up to 82; 164)
const uint8_t third = esp_scale8(offset8, 85);
const uint8_t two_thirds = esp_scale8(offset8, 170);
ESPColor rgb(255, 255, 255, 0);
Color rgb(255, 255, 255, 0);
switch (hue >> 5) {
case 0b000:
rgb.r = 255 - third;
@ -76,7 +73,7 @@ ESPColor ESPHSVColor::to_rgb() const {
return rgb;
}
void ESPRangeView::set(const ESPColor &color) {
void ESPRangeView::set(const Color &color) {
for (int32_t i = this->begin_; i < this->end_; i++) {
(*this->parent_)[i] = color;
}
@ -179,12 +176,12 @@ void AddressableLight::call_setup() {
#endif
}
ESPColor esp_color_from_light_color_values(LightColorValues val) {
Color esp_color_from_light_color_values(LightColorValues val) {
auto r = static_cast<uint8_t>(roundf(val.get_red() * 255.0f));
auto g = static_cast<uint8_t>(roundf(val.get_green() * 255.0f));
auto b = static_cast<uint8_t>(roundf(val.get_blue() * 255.0f));
auto w = static_cast<uint8_t>(roundf(val.get_white() * val.get_state() * 255.0f));
return ESPColor(r, g, b, w);
return Color(r, g, b, w);
}
void AddressableLight::write_state(LightState *state) {
@ -219,7 +216,7 @@ void AddressableLight::write_state(LightState *state) {
this->last_transition_progress_ = new_progress;
auto end_values = state->transformer_->get_end_values();
ESPColor target_color = esp_color_from_light_color_values(end_values);
Color target_color = esp_color_from_light_color_values(end_values);
// our transition will handle brightness, disable brightness in correction.
this->correction_.set_local_brightness(255);
@ -247,7 +244,7 @@ void AddressableLight::write_state(LightState *state) {
if (alpha8 != 0) {
uint8_t inv_alpha8 = 255 - alpha8;
ESPColor add = target_color * alpha8;
Color add = target_color * alpha8;
for (auto led : *this)
led = add + led.get() * inv_alpha8;

View File

@ -2,6 +2,7 @@
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/color.h"
#include "light_output.h"
#include "light_state.h"
@ -12,151 +13,7 @@
namespace esphome {
namespace light {
inline static uint8_t esp_scale8(uint8_t i, uint8_t scale) { return (uint16_t(i) * (1 + uint16_t(scale))) / 256; }
struct ESPColor {
union {
struct {
union {
uint8_t r;
uint8_t red;
};
union {
uint8_t g;
uint8_t green;
};
union {
uint8_t b;
uint8_t blue;
};
union {
uint8_t w;
uint8_t white;
};
};
uint8_t raw[4];
uint32_t raw_32;
};
inline ESPColor() ALWAYS_INLINE : r(0), g(0), b(0), w(0) {} // NOLINT
inline ESPColor(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) ALWAYS_INLINE : r(red),
g(green),
b(blue),
w(white) {}
inline ESPColor(uint8_t red, uint8_t green, uint8_t blue) ALWAYS_INLINE : r(red), g(green), b(blue), w(0) {}
inline ESPColor(uint32_t colorcode) ALWAYS_INLINE : r((colorcode >> 16) & 0xFF),
g((colorcode >> 8) & 0xFF),
b((colorcode >> 0) & 0xFF),
w((colorcode >> 24) & 0xFF) {}
inline ESPColor(const ESPColor &rhs) ALWAYS_INLINE {
this->r = rhs.r;
this->g = rhs.g;
this->b = rhs.b;
this->w = rhs.w;
}
inline bool is_on() ALWAYS_INLINE { return this->raw_32 != 0; }
inline ESPColor &operator=(const ESPColor &rhs) ALWAYS_INLINE {
this->r = rhs.r;
this->g = rhs.g;
this->b = rhs.b;
this->w = rhs.w;
return *this;
}
inline ESPColor &operator=(uint32_t colorcode) ALWAYS_INLINE {
this->w = (colorcode >> 24) & 0xFF;
this->r = (colorcode >> 16) & 0xFF;
this->g = (colorcode >> 8) & 0xFF;
this->b = (colorcode >> 0) & 0xFF;
return *this;
}
inline uint8_t &operator[](uint8_t x) ALWAYS_INLINE { return this->raw[x]; }
inline ESPColor operator*(uint8_t scale) const ALWAYS_INLINE {
return ESPColor(esp_scale8(this->red, scale), esp_scale8(this->green, scale), esp_scale8(this->blue, scale),
esp_scale8(this->white, scale));
}
inline ESPColor &operator*=(uint8_t scale) ALWAYS_INLINE {
this->red = esp_scale8(this->red, scale);
this->green = esp_scale8(this->green, scale);
this->blue = esp_scale8(this->blue, scale);
this->white = esp_scale8(this->white, scale);
return *this;
}
inline ESPColor operator*(const ESPColor &scale) const ALWAYS_INLINE {
return ESPColor(esp_scale8(this->red, scale.red), esp_scale8(this->green, scale.green),
esp_scale8(this->blue, scale.blue), esp_scale8(this->white, scale.white));
}
inline ESPColor &operator*=(const ESPColor &scale) ALWAYS_INLINE {
this->red = esp_scale8(this->red, scale.red);
this->green = esp_scale8(this->green, scale.green);
this->blue = esp_scale8(this->blue, scale.blue);
this->white = esp_scale8(this->white, scale.white);
return *this;
}
inline ESPColor operator+(const ESPColor &add) const ALWAYS_INLINE {
ESPColor ret;
if (uint8_t(add.r + this->r) < this->r)
ret.r = 255;
else
ret.r = this->r + add.r;
if (uint8_t(add.g + this->g) < this->g)
ret.g = 255;
else
ret.g = this->g + add.g;
if (uint8_t(add.b + this->b) < this->b)
ret.b = 255;
else
ret.b = this->b + add.b;
if (uint8_t(add.w + this->w) < this->w)
ret.w = 255;
else
ret.w = this->w + add.w;
return ret;
}
inline ESPColor &operator+=(const ESPColor &add) ALWAYS_INLINE { return *this = (*this) + add; }
inline ESPColor operator+(uint8_t add) const ALWAYS_INLINE { return (*this) + ESPColor(add, add, add, add); }
inline ESPColor &operator+=(uint8_t add) ALWAYS_INLINE { return *this = (*this) + add; }
inline ESPColor operator-(const ESPColor &subtract) const ALWAYS_INLINE {
ESPColor ret;
if (subtract.r > this->r)
ret.r = 0;
else
ret.r = this->r - subtract.r;
if (subtract.g > this->g)
ret.g = 0;
else
ret.g = this->g - subtract.g;
if (subtract.b > this->b)
ret.b = 0;
else
ret.b = this->b - subtract.b;
if (subtract.w > this->w)
ret.w = 0;
else
ret.w = this->w - subtract.w;
return ret;
}
inline ESPColor &operator-=(const ESPColor &subtract) ALWAYS_INLINE { return *this = (*this) - subtract; }
inline ESPColor operator-(uint8_t subtract) const ALWAYS_INLINE {
return (*this) - ESPColor(subtract, subtract, subtract, subtract);
}
inline ESPColor &operator-=(uint8_t subtract) ALWAYS_INLINE { return *this = (*this) - subtract; }
static ESPColor random_color() {
uint32_t rand = random_uint32();
uint8_t w = rand >> 24;
uint8_t r = rand >> 16;
uint8_t g = rand >> 8;
uint8_t b = rand >> 0;
const uint16_t max_rgb = std::max(r, std::max(g, b));
return ESPColor(uint8_t((uint16_t(r) * 255U / max_rgb)), uint8_t((uint16_t(g) * 255U / max_rgb)),
uint8_t((uint16_t(b) * 255U / max_rgb)), w);
}
ESPColor fade_to_white(uint8_t amnt) const { return ESPColor(255, 255, 255, 255) - (*this * amnt); }
ESPColor fade_to_black(uint8_t amnt) const { return *this * amnt; }
ESPColor lighten(uint8_t delta) const { return *this + delta; }
ESPColor darken(uint8_t delta) const { return *this - delta; }
static const ESPColor BLACK;
static const ESPColor WHITE;
};
using ESPColor = Color;
struct ESPHSVColor {
union {
@ -181,19 +38,19 @@ struct ESPHSVColor {
inline ESPHSVColor(uint8_t hue, uint8_t saturation, uint8_t value) ALWAYS_INLINE : hue(hue),
saturation(saturation),
value(value) {}
ESPColor to_rgb() const;
Color to_rgb() const;
};
class ESPColorCorrection {
public:
ESPColorCorrection() : max_brightness_(255, 255, 255, 255) {}
void set_max_brightness(const ESPColor &max_brightness) { this->max_brightness_ = max_brightness; }
void set_max_brightness(const Color &max_brightness) { this->max_brightness_ = max_brightness; }
void set_local_brightness(uint8_t local_brightness) { this->local_brightness_ = local_brightness; }
void calculate_gamma_table(float gamma);
inline ESPColor color_correct(ESPColor color) const ALWAYS_INLINE {
inline Color color_correct(Color color) const ALWAYS_INLINE {
// corrected = (uncorrected * max_brightness * local_brightness) ^ gamma
return ESPColor(this->color_correct_red(color.red), this->color_correct_green(color.green),
this->color_correct_blue(color.blue), this->color_correct_white(color.white));
return Color(this->color_correct_red(color.red), this->color_correct_green(color.green),
this->color_correct_blue(color.blue), this->color_correct_white(color.white));
}
inline uint8_t color_correct_red(uint8_t red) const ALWAYS_INLINE {
uint8_t res = esp_scale8(esp_scale8(red, this->max_brightness_.red), this->local_brightness_);
@ -212,10 +69,10 @@ class ESPColorCorrection {
uint8_t res = esp_scale8(white, this->max_brightness_.white);
return this->gamma_table_[res];
}
inline ESPColor color_uncorrect(ESPColor color) const ALWAYS_INLINE {
inline Color color_uncorrect(Color color) const ALWAYS_INLINE {
// uncorrected = corrected^(1/gamma) / (max_brightness * local_brightness)
return ESPColor(this->color_uncorrect_red(color.red), this->color_uncorrect_green(color.green),
this->color_uncorrect_blue(color.blue), this->color_uncorrect_white(color.white));
return Color(this->color_uncorrect_red(color.red), this->color_uncorrect_green(color.green),
this->color_uncorrect_blue(color.blue), this->color_uncorrect_white(color.white));
}
inline uint8_t color_uncorrect_red(uint8_t red) const ALWAYS_INLINE {
if (this->max_brightness_.red == 0 || this->local_brightness_ == 0)
@ -249,13 +106,13 @@ class ESPColorCorrection {
protected:
uint8_t gamma_table_[256];
uint8_t gamma_reverse_table_[256];
ESPColor max_brightness_;
Color max_brightness_;
uint8_t local_brightness_{255};
};
class ESPColorSettable {
public:
virtual void set(const ESPColor &color) = 0;
virtual void set(const Color &color) = 0;
virtual void set_red(uint8_t red) = 0;
virtual void set_green(uint8_t green) = 0;
virtual void set_blue(uint8_t blue) = 0;
@ -267,7 +124,7 @@ class ESPColorSettable {
virtual void darken(uint8_t delta) = 0;
void set(const ESPHSVColor &color) { this->set_hsv(color); }
void set_hsv(const ESPHSVColor &color) {
ESPColor rgb = color.to_rgb();
Color rgb = color.to_rgb();
this->set_rgb(rgb.r, rgb.g, rgb.b);
}
void set_rgb(uint8_t red, uint8_t green, uint8_t blue) {
@ -291,7 +148,7 @@ class ESPColorView : public ESPColorSettable {
white_(white),
effect_data_(effect_data),
color_correction_(color_correction) {}
ESPColorView &operator=(const ESPColor &rhs) {
ESPColorView &operator=(const Color &rhs) {
this->set(rhs);
return *this;
}
@ -299,7 +156,7 @@ class ESPColorView : public ESPColorSettable {
this->set_hsv(rhs);
return *this;
}
void set(const ESPColor &color) override { this->set_rgbw(color.r, color.g, color.b, color.w); }
void set(const Color &color) override { this->set_rgbw(color.r, color.g, color.b, color.w); }
void set_red(uint8_t red) override { *this->red_ = this->color_correction_->color_correct_red(red); }
void set_green(uint8_t green) override { *this->green_ = this->color_correction_->color_correct_green(green); }
void set_blue(uint8_t blue) override { *this->blue_ = this->color_correction_->color_correct_blue(blue); }
@ -317,7 +174,7 @@ class ESPColorView : public ESPColorSettable {
void fade_to_black(uint8_t amnt) override { this->set(this->get().fade_to_black(amnt)); }
void lighten(uint8_t delta) override { this->set(this->get().lighten(delta)); }
void darken(uint8_t delta) override { this->set(this->get().darken(delta)); }
ESPColor get() const { return ESPColor(this->get_red(), this->get_green(), this->get_blue(), this->get_white()); }
Color get() const { return Color(this->get_red(), this->get_green(), this->get_blue(), this->get_white()); }
uint8_t get_red() const { return this->color_correction_->color_uncorrect_red(*this->red_); }
uint8_t get_red_raw() const { return *this->red_; }
uint8_t get_green() const { return this->color_correction_->color_uncorrect_green(*this->green_); }
@ -370,8 +227,8 @@ class ESPRangeView : public ESPColorSettable {
ESPRangeIterator begin();
ESPRangeIterator end();
void set(const ESPColor &color) override;
ESPRangeView &operator=(const ESPColor &rhs) {
void set(const Color &color) override;
ESPRangeView &operator=(const Color &rhs) {
this->set(rhs);
return *this;
}
@ -454,8 +311,8 @@ class AddressableLight : public LightOutput, public Component {
void set_effect_active(bool effect_active) { this->effect_active_ = effect_active; }
void write_state(LightState *state) override;
void set_correction(float red, float green, float blue, float white = 1.0f) {
this->correction_.set_max_brightness(ESPColor(uint8_t(roundf(red * 255.0f)), uint8_t(roundf(green * 255.0f)),
uint8_t(roundf(blue * 255.0f)), uint8_t(roundf(white * 255.0f))));
this->correction_.set_max_brightness(Color(uint8_t(roundf(red * 255.0f)), uint8_t(roundf(green * 255.0f)),
uint8_t(roundf(blue * 255.0f)), uint8_t(roundf(white * 255.0f))));
}
void setup_state(LightState *state) override {
this->correction_.calculate_gamma_table(state->get_gamma_correct());

View File

@ -34,13 +34,13 @@ class AddressableLightEffect : public LightEffect {
this->start();
}
void stop() override { this->get_addressable_()->set_effect_active(false); }
virtual void apply(AddressableLight &it, const ESPColor &current_color) = 0;
virtual void apply(AddressableLight &it, const Color &current_color) = 0;
void apply() override {
LightColorValues color = this->state_->remote_values;
// not using any color correction etc. that will be handled by the addressable layer
ESPColor current_color =
ESPColor(static_cast<uint8_t>(color.get_red() * 255), static_cast<uint8_t>(color.get_green() * 255),
static_cast<uint8_t>(color.get_blue() * 255), static_cast<uint8_t>(color.get_white() * 255));
Color current_color =
Color(static_cast<uint8_t>(color.get_red() * 255), static_cast<uint8_t>(color.get_green() * 255),
static_cast<uint8_t>(color.get_blue() * 255), static_cast<uint8_t>(color.get_white() * 255));
this->apply(*this->get_addressable_(), current_color);
}
@ -51,11 +51,11 @@ class AddressableLightEffect : public LightEffect {
class AddressableLambdaLightEffect : public AddressableLightEffect {
public:
AddressableLambdaLightEffect(const std::string &name,
const std::function<void(AddressableLight &, ESPColor, bool initial_run)> &f,
const std::function<void(AddressableLight &, Color, bool initial_run)> &f,
uint32_t update_interval)
: AddressableLightEffect(name), f_(f), update_interval_(update_interval) {}
void start() override { this->initial_run_ = true; }
void apply(AddressableLight &it, const ESPColor &current_color) override {
void apply(AddressableLight &it, const Color &current_color) override {
const uint32_t now = millis();
if (now - this->last_run_ >= this->update_interval_) {
this->last_run_ = now;
@ -65,7 +65,7 @@ class AddressableLambdaLightEffect : public AddressableLightEffect {
}
protected:
std::function<void(AddressableLight &, ESPColor, bool initial_run)> f_;
std::function<void(AddressableLight &, Color, bool initial_run)> f_;
uint32_t update_interval_;
uint32_t last_run_{0};
bool initial_run_;
@ -74,7 +74,7 @@ class AddressableLambdaLightEffect : public AddressableLightEffect {
class AddressableRainbowLightEffect : public AddressableLightEffect {
public:
explicit AddressableRainbowLightEffect(const std::string &name) : AddressableLightEffect(name) {}
void apply(AddressableLight &it, const ESPColor &current_color) override {
void apply(AddressableLight &it, const Color &current_color) override {
ESPHSVColor hsv;
hsv.value = 255;
hsv.saturation = 240;
@ -106,7 +106,7 @@ class AddressableColorWipeEffect : public AddressableLightEffect {
void set_colors(const std::vector<AddressableColorWipeEffectColor> &colors) { this->colors_ = colors; }
void set_add_led_interval(uint32_t add_led_interval) { this->add_led_interval_ = add_led_interval; }
void set_reverse(bool reverse) { this->reverse_ = reverse; }
void apply(AddressableLight &it, const ESPColor &current_color) override {
void apply(AddressableLight &it, const Color &current_color) override {
const uint32_t now = millis();
if (now - this->last_add_ < this->add_led_interval_)
return;
@ -116,7 +116,7 @@ class AddressableColorWipeEffect : public AddressableLightEffect {
else
it.shift_right(1);
const AddressableColorWipeEffectColor color = this->colors_[this->at_color_];
const ESPColor esp_color = ESPColor(color.r, color.g, color.b, color.w);
const Color esp_color = Color(color.r, color.g, color.b, color.w);
if (this->reverse_)
it[-1] = esp_color;
else
@ -126,7 +126,7 @@ class AddressableColorWipeEffect : public AddressableLightEffect {
this->at_color_ = (this->at_color_ + 1) % this->colors_.size();
AddressableColorWipeEffectColor &new_color = this->colors_[this->at_color_];
if (new_color.random) {
ESPColor c = ESPColor::random_color();
Color c = Color::random_color();
new_color.r = c.r;
new_color.g = c.g;
new_color.b = c.b;
@ -148,8 +148,8 @@ class AddressableScanEffect : public AddressableLightEffect {
explicit AddressableScanEffect(const std::string &name) : AddressableLightEffect(name) {}
void set_move_interval(uint32_t move_interval) { this->move_interval_ = move_interval; }
void set_scan_width(uint32_t scan_width) { this->scan_width_ = scan_width; }
void apply(AddressableLight &it, const ESPColor &current_color) override {
it.all() = ESPColor::BLACK;
void apply(AddressableLight &it, const Color &current_color) override {
it.all() = COLOR_BLACK;
for (auto i = 0; i < this->scan_width_; i++) {
it[this->at_led_ + i] = current_color;
@ -181,7 +181,7 @@ class AddressableScanEffect : public AddressableLightEffect {
class AddressableTwinkleEffect : public AddressableLightEffect {
public:
explicit AddressableTwinkleEffect(const std::string &name) : AddressableLightEffect(name) {}
void apply(AddressableLight &addressable, const ESPColor &current_color) override {
void apply(AddressableLight &addressable, const Color &current_color) override {
const uint32_t now = millis();
uint8_t pos_add = 0;
if (now - this->last_progress_ > this->progress_interval_) {
@ -199,7 +199,7 @@ class AddressableTwinkleEffect : public AddressableLightEffect {
else
view.set_effect_data(new_pos);
} else {
view = ESPColor::BLACK;
view = COLOR_BLACK;
}
}
while (random_float() < this->twinkle_probability_) {
@ -221,7 +221,7 @@ class AddressableTwinkleEffect : public AddressableLightEffect {
class AddressableRandomTwinkleEffect : public AddressableLightEffect {
public:
explicit AddressableRandomTwinkleEffect(const std::string &name) : AddressableLightEffect(name) {}
void apply(AddressableLight &it, const ESPColor &current_color) override {
void apply(AddressableLight &it, const Color &current_color) override {
const uint32_t now = millis();
uint8_t pos_add = 0;
if (now - this->last_progress_ > this->progress_interval_) {
@ -237,7 +237,7 @@ class AddressableRandomTwinkleEffect : public AddressableLightEffect {
if (color == 0) {
view = current_color * sine;
} else {
view = ESPColor(((color >> 2) & 1) * sine, ((color >> 1) & 1) * sine, ((color >> 0) & 1) * sine);
view = Color(((color >> 2) & 1) * sine, ((color >> 1) & 1) * sine, ((color >> 0) & 1) * sine);
}
const uint8_t new_x = x + pos_add;
if (new_x > 0b11111)
@ -245,7 +245,7 @@ class AddressableRandomTwinkleEffect : public AddressableLightEffect {
else
view.set_effect_data((new_x << 3) | color);
} else {
view = ESPColor(0, 0, 0, 0);
view = Color(0, 0, 0, 0);
}
}
while (random_float() < this->twinkle_probability_) {
@ -270,9 +270,9 @@ class AddressableFireworksEffect : public AddressableLightEffect {
explicit AddressableFireworksEffect(const std::string &name) : AddressableLightEffect(name) {}
void start() override {
auto &it = *this->get_addressable_();
it.all() = ESPColor::BLACK;
it.all() = COLOR_BLACK;
}
void apply(AddressableLight &it, const ESPColor &current_color) override {
void apply(AddressableLight &it, const Color &current_color) override {
const uint32_t now = millis();
if (now - this->last_update_ < this->update_interval_)
return;
@ -280,7 +280,7 @@ class AddressableFireworksEffect : public AddressableLightEffect {
// "invert" the fade out parameter so that higher values make fade out faster
const uint8_t fade_out_mult = 255u - this->fade_out_rate_;
for (auto view : it) {
ESPColor target = view.get() * fade_out_mult;
Color target = view.get() * fade_out_mult;
if (target.r < 64)
target *= 170;
view = target;
@ -294,7 +294,7 @@ class AddressableFireworksEffect : public AddressableLightEffect {
if (random_float() < this->spark_probability_) {
const size_t pos = random_uint32() % it.size();
if (this->use_random_color_) {
it[pos] = ESPColor::random_color();
it[pos] = Color::random_color();
} else {
it[pos] = current_color;
}
@ -316,7 +316,7 @@ class AddressableFireworksEffect : public AddressableLightEffect {
class AddressableFlickerEffect : public AddressableLightEffect {
public:
explicit AddressableFlickerEffect(const std::string &name) : AddressableLightEffect(name) {}
void apply(AddressableLight &it, const ESPColor &current_color) override {
void apply(AddressableLight &it, const Color &current_color) override {
const uint32_t now = millis();
const uint8_t intensity = this->intensity_;
const uint8_t inv_intensity = 255 - intensity;

View File

@ -1,54 +1,102 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.const import CONF_ID, CONF_TRANSITION_LENGTH, CONF_STATE, CONF_FLASH_LENGTH, \
CONF_EFFECT, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, CONF_WHITE, \
CONF_COLOR_TEMPERATURE, CONF_RANGE_FROM, CONF_RANGE_TO
from .types import DimRelativeAction, ToggleAction, LightState, LightControlAction, \
AddressableLightState, AddressableSet, LightIsOnCondition, LightIsOffCondition
from esphome.const import (
CONF_ID,
CONF_TRANSITION_LENGTH,
CONF_STATE,
CONF_FLASH_LENGTH,
CONF_EFFECT,
CONF_BRIGHTNESS,
CONF_RED,
CONF_GREEN,
CONF_BLUE,
CONF_WHITE,
CONF_COLOR_TEMPERATURE,
CONF_RANGE_FROM,
CONF_RANGE_TO,
)
from .types import (
DimRelativeAction,
ToggleAction,
LightState,
LightControlAction,
AddressableLightState,
AddressableSet,
LightIsOnCondition,
LightIsOffCondition,
)
@automation.register_action('light.toggle', ToggleAction, automation.maybe_simple_id({
cv.Required(CONF_ID): cv.use_id(LightState),
cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds),
}))
@automation.register_action(
"light.toggle",
ToggleAction,
automation.maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(LightState),
cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable(
cv.positive_time_period_milliseconds
),
}
),
)
def light_toggle_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
if CONF_TRANSITION_LENGTH in config:
template_ = yield cg.templatable(config[CONF_TRANSITION_LENGTH], args, cg.uint32)
template_ = yield cg.templatable(
config[CONF_TRANSITION_LENGTH], args, cg.uint32
)
cg.add(var.set_transition_length(template_))
yield var
LIGHT_CONTROL_ACTION_SCHEMA = cv.Schema({
cv.Required(CONF_ID): cv.use_id(LightState),
cv.Optional(CONF_STATE): cv.templatable(cv.boolean),
cv.Exclusive(CONF_TRANSITION_LENGTH, 'transformer'):
cv.templatable(cv.positive_time_period_milliseconds),
cv.Exclusive(CONF_FLASH_LENGTH, 'transformer'):
cv.templatable(cv.positive_time_period_milliseconds),
cv.Exclusive(CONF_EFFECT, 'transformer'): cv.templatable(cv.string),
cv.Optional(CONF_BRIGHTNESS): cv.templatable(cv.percentage),
cv.Optional(CONF_RED): cv.templatable(cv.percentage),
cv.Optional(CONF_GREEN): cv.templatable(cv.percentage),
cv.Optional(CONF_BLUE): cv.templatable(cv.percentage),
cv.Optional(CONF_WHITE): cv.templatable(cv.percentage),
cv.Optional(CONF_COLOR_TEMPERATURE): cv.templatable(cv.color_temperature),
})
LIGHT_TURN_OFF_ACTION_SCHEMA = automation.maybe_simple_id({
cv.Required(CONF_ID): cv.use_id(LightState),
cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds),
cv.Optional(CONF_STATE, default=False): False,
})
LIGHT_TURN_ON_ACTION_SCHEMA = automation.maybe_simple_id(LIGHT_CONTROL_ACTION_SCHEMA.extend({
cv.Optional(CONF_STATE, default=True): True,
}))
LIGHT_CONTROL_ACTION_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(LightState),
cv.Optional(CONF_STATE): cv.templatable(cv.boolean),
cv.Exclusive(CONF_TRANSITION_LENGTH, "transformer"): cv.templatable(
cv.positive_time_period_milliseconds
),
cv.Exclusive(CONF_FLASH_LENGTH, "transformer"): cv.templatable(
cv.positive_time_period_milliseconds
),
cv.Exclusive(CONF_EFFECT, "transformer"): cv.templatable(cv.string),
cv.Optional(CONF_BRIGHTNESS): cv.templatable(cv.percentage),
cv.Optional(CONF_RED): cv.templatable(cv.percentage),
cv.Optional(CONF_GREEN): cv.templatable(cv.percentage),
cv.Optional(CONF_BLUE): cv.templatable(cv.percentage),
cv.Optional(CONF_WHITE): cv.templatable(cv.percentage),
cv.Optional(CONF_COLOR_TEMPERATURE): cv.templatable(cv.color_temperature),
}
)
LIGHT_TURN_OFF_ACTION_SCHEMA = automation.maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(LightState),
cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable(
cv.positive_time_period_milliseconds
),
cv.Optional(CONF_STATE, default=False): False,
}
)
LIGHT_TURN_ON_ACTION_SCHEMA = automation.maybe_simple_id(
LIGHT_CONTROL_ACTION_SCHEMA.extend(
{
cv.Optional(CONF_STATE, default=True): True,
}
)
)
@automation.register_action('light.turn_off', LightControlAction, LIGHT_TURN_OFF_ACTION_SCHEMA)
@automation.register_action('light.turn_on', LightControlAction, LIGHT_TURN_ON_ACTION_SCHEMA)
@automation.register_action('light.control', LightControlAction, LIGHT_CONTROL_ACTION_SCHEMA)
@automation.register_action(
"light.turn_off", LightControlAction, LIGHT_TURN_OFF_ACTION_SCHEMA
)
@automation.register_action(
"light.turn_on", LightControlAction, LIGHT_TURN_ON_ACTION_SCHEMA
)
@automation.register_action(
"light.control", LightControlAction, LIGHT_CONTROL_ACTION_SCHEMA
)
def light_control_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
@ -56,7 +104,9 @@ def light_control_to_code(config, action_id, template_arg, args):
template_ = yield cg.templatable(config[CONF_STATE], args, bool)
cg.add(var.set_state(template_))
if CONF_TRANSITION_LENGTH in config:
template_ = yield cg.templatable(config[CONF_TRANSITION_LENGTH], args, cg.uint32)
template_ = yield cg.templatable(
config[CONF_TRANSITION_LENGTH], args, cg.uint32
)
cg.add(var.set_transition_length(template_))
if CONF_FLASH_LENGTH in config:
template_ = yield cg.templatable(config[CONF_FLASH_LENGTH], args, cg.uint32)
@ -85,16 +135,23 @@ def light_control_to_code(config, action_id, template_arg, args):
yield var
CONF_RELATIVE_BRIGHTNESS = 'relative_brightness'
LIGHT_DIM_RELATIVE_ACTION_SCHEMA = cv.Schema({
cv.Required(CONF_ID): cv.use_id(LightState),
cv.Required(CONF_RELATIVE_BRIGHTNESS): cv.templatable(cv.possibly_negative_percentage),
cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds),
})
CONF_RELATIVE_BRIGHTNESS = "relative_brightness"
LIGHT_DIM_RELATIVE_ACTION_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(LightState),
cv.Required(CONF_RELATIVE_BRIGHTNESS): cv.templatable(
cv.possibly_negative_percentage
),
cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable(
cv.positive_time_period_milliseconds
),
}
)
@automation.register_action('light.dim_relative', DimRelativeAction,
LIGHT_DIM_RELATIVE_ACTION_SCHEMA)
@automation.register_action(
"light.dim_relative", DimRelativeAction, LIGHT_DIM_RELATIVE_ACTION_SCHEMA
)
def light_dim_relative_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
@ -106,19 +163,22 @@ def light_dim_relative_to_code(config, action_id, template_arg, args):
yield var
LIGHT_ADDRESSABLE_SET_ACTION_SCHEMA = cv.Schema({
cv.Required(CONF_ID): cv.use_id(AddressableLightState),
cv.Optional(CONF_RANGE_FROM): cv.templatable(cv.positive_int),
cv.Optional(CONF_RANGE_TO): cv.templatable(cv.positive_int),
cv.Optional(CONF_RED): cv.templatable(cv.percentage),
cv.Optional(CONF_GREEN): cv.templatable(cv.percentage),
cv.Optional(CONF_BLUE): cv.templatable(cv.percentage),
cv.Optional(CONF_WHITE): cv.templatable(cv.percentage),
})
LIGHT_ADDRESSABLE_SET_ACTION_SCHEMA = cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(AddressableLightState),
cv.Optional(CONF_RANGE_FROM): cv.templatable(cv.positive_int),
cv.Optional(CONF_RANGE_TO): cv.templatable(cv.positive_int),
cv.Optional(CONF_RED): cv.templatable(cv.percentage),
cv.Optional(CONF_GREEN): cv.templatable(cv.percentage),
cv.Optional(CONF_BLUE): cv.templatable(cv.percentage),
cv.Optional(CONF_WHITE): cv.templatable(cv.percentage),
}
)
@automation.register_action('light.addressable_set', AddressableSet,
LIGHT_ADDRESSABLE_SET_ACTION_SCHEMA)
@automation.register_action(
"light.addressable_set", AddressableSet, LIGHT_ADDRESSABLE_SET_ACTION_SCHEMA
)
def light_addressable_set_to_code(config, action_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID])
var = cg.new_Pvariable(action_id, template_arg, paren)
@ -133,28 +193,46 @@ def light_addressable_set_to_code(config, action_id, template_arg, args):
return int(round(x * 255))
if CONF_RED in config:
templ = yield cg.templatable(config[CONF_RED], args, cg.uint8, to_exp=rgbw_to_exp)
templ = yield cg.templatable(
config[CONF_RED], args, cg.uint8, to_exp=rgbw_to_exp
)
cg.add(var.set_red(templ))
if CONF_GREEN in config:
templ = yield cg.templatable(config[CONF_GREEN], args, cg.uint8, to_exp=rgbw_to_exp)
templ = yield cg.templatable(
config[CONF_GREEN], args, cg.uint8, to_exp=rgbw_to_exp
)
cg.add(var.set_green(templ))
if CONF_BLUE in config:
templ = yield cg.templatable(config[CONF_BLUE], args, cg.uint8, to_exp=rgbw_to_exp)
templ = yield cg.templatable(
config[CONF_BLUE], args, cg.uint8, to_exp=rgbw_to_exp
)
cg.add(var.set_blue(templ))
if CONF_WHITE in config:
templ = yield cg.templatable(config[CONF_WHITE], args, cg.uint8, to_exp=rgbw_to_exp)
templ = yield cg.templatable(
config[CONF_WHITE], args, cg.uint8, to_exp=rgbw_to_exp
)
cg.add(var.set_white(templ))
yield var
@automation.register_condition('light.is_on', LightIsOnCondition,
automation.maybe_simple_id({
cv.Required(CONF_ID): cv.use_id(LightState),
}))
@automation.register_condition('light.is_off', LightIsOffCondition,
automation.maybe_simple_id({
cv.Required(CONF_ID): cv.use_id(LightState),
}))
@automation.register_condition(
"light.is_on",
LightIsOnCondition,
automation.maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(LightState),
}
),
)
@automation.register_condition(
"light.is_off",
LightIsOffCondition,
automation.maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(LightState),
}
),
)
def light_is_on_off_to_code(config, condition_id, template_arg, args):
paren = yield cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(condition_id, template_arg, paren)

View File

@ -1,38 +1,70 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.const import CONF_NAME, CONF_LAMBDA, CONF_UPDATE_INTERVAL, CONF_TRANSITION_LENGTH, \
CONF_COLORS, CONF_STATE, CONF_DURATION, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, \
CONF_WHITE, CONF_ALPHA, CONF_INTENSITY, CONF_SPEED, CONF_WIDTH, CONF_NUM_LEDS, CONF_RANDOM, \
CONF_SEQUENCE
from esphome.util import Registry
from .types import LambdaLightEffect, RandomLightEffect, StrobeLightEffect, \
StrobeLightEffectColor, LightColorValues, AddressableLightRef, AddressableLambdaLightEffect, \
FlickerLightEffect, AddressableRainbowLightEffect, AddressableColorWipeEffect, \
AddressableColorWipeEffectColor, AddressableScanEffect, AddressableTwinkleEffect, \
AddressableRandomTwinkleEffect, AddressableFireworksEffect, AddressableFlickerEffect, \
AutomationLightEffect, ESPColor
CONF_ADD_LED_INTERVAL = 'add_led_interval'
CONF_REVERSE = 'reverse'
CONF_MOVE_INTERVAL = 'move_interval'
CONF_SCAN_WIDTH = 'scan_width'
CONF_TWINKLE_PROBABILITY = 'twinkle_probability'
CONF_PROGRESS_INTERVAL = 'progress_interval'
CONF_SPARK_PROBABILITY = 'spark_probability'
CONF_USE_RANDOM_COLOR = 'use_random_color'
CONF_FADE_OUT_RATE = 'fade_out_rate'
CONF_STROBE = 'strobe'
CONF_FLICKER = 'flicker'
CONF_ADDRESSABLE_LAMBDA = 'addressable_lambda'
CONF_ADDRESSABLE_RAINBOW = 'addressable_rainbow'
CONF_ADDRESSABLE_COLOR_WIPE = 'addressable_color_wipe'
CONF_ADDRESSABLE_SCAN = 'addressable_scan'
CONF_ADDRESSABLE_TWINKLE = 'addressable_twinkle'
CONF_ADDRESSABLE_RANDOM_TWINKLE = 'addressable_random_twinkle'
CONF_ADDRESSABLE_FIREWORKS = 'addressable_fireworks'
CONF_ADDRESSABLE_FLICKER = 'addressable_flicker'
CONF_AUTOMATION = 'automation'
from esphome.const import (
CONF_NAME,
CONF_LAMBDA,
CONF_UPDATE_INTERVAL,
CONF_TRANSITION_LENGTH,
CONF_COLORS,
CONF_STATE,
CONF_DURATION,
CONF_BRIGHTNESS,
CONF_RED,
CONF_GREEN,
CONF_BLUE,
CONF_WHITE,
CONF_ALPHA,
CONF_INTENSITY,
CONF_SPEED,
CONF_WIDTH,
CONF_NUM_LEDS,
CONF_RANDOM,
CONF_SEQUENCE,
)
from esphome.util import Registry
from .types import (
LambdaLightEffect,
RandomLightEffect,
StrobeLightEffect,
StrobeLightEffectColor,
LightColorValues,
AddressableLightRef,
AddressableLambdaLightEffect,
FlickerLightEffect,
AddressableRainbowLightEffect,
AddressableColorWipeEffect,
AddressableColorWipeEffectColor,
AddressableScanEffect,
AddressableTwinkleEffect,
AddressableRandomTwinkleEffect,
AddressableFireworksEffect,
AddressableFlickerEffect,
AutomationLightEffect,
Color,
)
CONF_ADD_LED_INTERVAL = "add_led_interval"
CONF_REVERSE = "reverse"
CONF_MOVE_INTERVAL = "move_interval"
CONF_SCAN_WIDTH = "scan_width"
CONF_TWINKLE_PROBABILITY = "twinkle_probability"
CONF_PROGRESS_INTERVAL = "progress_interval"
CONF_SPARK_PROBABILITY = "spark_probability"
CONF_USE_RANDOM_COLOR = "use_random_color"
CONF_FADE_OUT_RATE = "fade_out_rate"
CONF_STROBE = "strobe"
CONF_FLICKER = "flicker"
CONF_ADDRESSABLE_LAMBDA = "addressable_lambda"
CONF_ADDRESSABLE_RAINBOW = "addressable_rainbow"
CONF_ADDRESSABLE_COLOR_WIPE = "addressable_color_wipe"
CONF_ADDRESSABLE_SCAN = "addressable_scan"
CONF_ADDRESSABLE_TWINKLE = "addressable_twinkle"
CONF_ADDRESSABLE_RANDOM_TWINKLE = "addressable_random_twinkle"
CONF_ADDRESSABLE_FIREWORKS = "addressable_fireworks"
CONF_ADDRESSABLE_FLICKER = "addressable_flicker"
CONF_AUTOMATION = "automation"
BINARY_EFFECTS = []
MONOCHROMATIC_EFFECTS = []
@ -43,9 +75,11 @@ EFFECTS_REGISTRY = Registry()
def register_effect(name, effect_type, default_name, schema, *extra_validators):
schema = cv.Schema(schema).extend({
cv.Optional(CONF_NAME, default=default_name): cv.string_strict,
})
schema = cv.Schema(schema).extend(
{
cv.Optional(CONF_NAME, default=default_name): cv.string_strict,
}
)
validator = cv.All(schema, *extra_validators)
return EFFECTS_REGISTRY.register(name, effect_type, validator)
@ -60,7 +94,9 @@ def register_binary_effect(name, effect_type, default_name, schema, *extra_valid
return register_effect(name, effect_type, default_name, schema, *extra_validators)
def register_monochromatic_effect(name, effect_type, default_name, schema, *extra_validators):
def register_monochromatic_effect(
name, effect_type, default_name, schema, *extra_validators
):
# monochromatic effect can be used for all lights expect binary
MONOCHROMATIC_EFFECTS.append(name)
RGB_EFFECTS.append(name)
@ -77,36 +113,58 @@ def register_rgb_effect(name, effect_type, default_name, schema, *extra_validato
return register_effect(name, effect_type, default_name, schema, *extra_validators)
def register_addressable_effect(name, effect_type, default_name, schema, *extra_validators):
def register_addressable_effect(
name, effect_type, default_name, schema, *extra_validators
):
# addressable effect can be used only in addressable
ADDRESSABLE_EFFECTS.append(name)
return register_effect(name, effect_type, default_name, schema, *extra_validators)
@register_binary_effect('lambda', LambdaLightEffect, "Lambda", {
cv.Required(CONF_LAMBDA): cv.lambda_,
cv.Optional(CONF_UPDATE_INTERVAL, default='0ms'): cv.update_interval,
})
@register_binary_effect(
"lambda",
LambdaLightEffect,
"Lambda",
{
cv.Required(CONF_LAMBDA): cv.lambda_,
cv.Optional(CONF_UPDATE_INTERVAL, default="0ms"): cv.update_interval,
},
)
def lambda_effect_to_code(config, effect_id):
lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [], return_type=cg.void)
yield cg.new_Pvariable(effect_id, config[CONF_NAME], lambda_,
config[CONF_UPDATE_INTERVAL])
yield cg.new_Pvariable(
effect_id, config[CONF_NAME], lambda_, config[CONF_UPDATE_INTERVAL]
)
@register_binary_effect('automation', AutomationLightEffect, "Automation", {
cv.Required(CONF_SEQUENCE): automation.validate_automation(single=True),
})
@register_binary_effect(
"automation",
AutomationLightEffect,
"Automation",
{
cv.Required(CONF_SEQUENCE): automation.validate_automation(single=True),
},
)
def automation_effect_to_code(config, effect_id):
var = yield cg.new_Pvariable(effect_id, config[CONF_NAME])
yield automation.build_automation(var.get_trig(), [], config[CONF_SEQUENCE])
yield var
@register_rgb_effect('random', RandomLightEffect, "Random", {
cv.Optional(CONF_TRANSITION_LENGTH, default='7.5s'): cv.positive_time_period_milliseconds,
cv.Optional(CONF_UPDATE_INTERVAL, default='10s'): cv.positive_time_period_milliseconds,
})
@register_rgb_effect(
"random",
RandomLightEffect,
"Random",
{
cv.Optional(
CONF_TRANSITION_LENGTH, default="7.5s"
): cv.positive_time_period_milliseconds,
cv.Optional(
CONF_UPDATE_INTERVAL, default="10s"
): cv.positive_time_period_milliseconds,
},
)
def random_effect_to_code(config, effect_id):
effect = cg.new_Pvariable(effect_id, config[CONF_NAME])
cg.add(effect.set_transition_length(config[CONF_TRANSITION_LENGTH]))
@ -114,40 +172,79 @@ def random_effect_to_code(config, effect_id):
yield effect
@register_binary_effect('strobe', StrobeLightEffect, "Strobe", {
cv.Optional(CONF_COLORS, default=[
{CONF_STATE: True, CONF_DURATION: '0.5s'},
{CONF_STATE: False, CONF_DURATION: '0.5s'},
]): cv.All(cv.ensure_list(cv.Schema({
cv.Optional(CONF_STATE, default=True): cv.boolean,
cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage,
cv.Optional(CONF_RED, default=1.0): cv.percentage,
cv.Optional(CONF_GREEN, default=1.0): cv.percentage,
cv.Optional(CONF_BLUE, default=1.0): cv.percentage,
cv.Optional(CONF_WHITE, default=1.0): cv.percentage,
cv.Required(CONF_DURATION): cv.positive_time_period_milliseconds,
}), cv.has_at_least_one_key(CONF_STATE, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE,
CONF_WHITE)), cv.Length(min=2)),
})
@register_binary_effect(
"strobe",
StrobeLightEffect,
"Strobe",
{
cv.Optional(
CONF_COLORS,
default=[
{CONF_STATE: True, CONF_DURATION: "0.5s"},
{CONF_STATE: False, CONF_DURATION: "0.5s"},
],
): cv.All(
cv.ensure_list(
cv.Schema(
{
cv.Optional(CONF_STATE, default=True): cv.boolean,
cv.Optional(CONF_BRIGHTNESS, default=1.0): cv.percentage,
cv.Optional(CONF_RED, default=1.0): cv.percentage,
cv.Optional(CONF_GREEN, default=1.0): cv.percentage,
cv.Optional(CONF_BLUE, default=1.0): cv.percentage,
cv.Optional(CONF_WHITE, default=1.0): cv.percentage,
cv.Required(
CONF_DURATION
): cv.positive_time_period_milliseconds,
}
),
cv.has_at_least_one_key(
CONF_STATE,
CONF_BRIGHTNESS,
CONF_RED,
CONF_GREEN,
CONF_BLUE,
CONF_WHITE,
),
),
cv.Length(min=2),
),
},
)
def strobe_effect_to_code(config, effect_id):
var = cg.new_Pvariable(effect_id, config[CONF_NAME])
colors = []
for color in config.get(CONF_COLORS, []):
colors.append(cg.StructInitializer(
StrobeLightEffectColor,
('color', LightColorValues(color[CONF_STATE], color[CONF_BRIGHTNESS],
color[CONF_RED], color[CONF_GREEN], color[CONF_BLUE],
color[CONF_WHITE])),
('duration', color[CONF_DURATION]),
))
colors.append(
cg.StructInitializer(
StrobeLightEffectColor,
(
"color",
LightColorValues(
color[CONF_STATE],
color[CONF_BRIGHTNESS],
color[CONF_RED],
color[CONF_GREEN],
color[CONF_BLUE],
color[CONF_WHITE],
),
),
("duration", color[CONF_DURATION]),
)
)
cg.add(var.set_colors(colors))
yield var
@register_monochromatic_effect('flicker', FlickerLightEffect, "Flicker", {
cv.Optional(CONF_ALPHA, default=0.95): cv.percentage,
cv.Optional(CONF_INTENSITY, default=0.015): cv.percentage,
})
@register_monochromatic_effect(
"flicker",
FlickerLightEffect,
"Flicker",
{
cv.Optional(CONF_ALPHA, default=0.95): cv.percentage,
cv.Optional(CONF_INTENSITY, default=0.015): cv.percentage,
},
)
def flicker_effect_to_code(config, effect_id):
var = cg.new_Pvariable(effect_id, config[CONF_NAME])
cg.add(var.set_alpha(config[CONF_ALPHA]))
@ -156,23 +253,38 @@ def flicker_effect_to_code(config, effect_id):
@register_addressable_effect(
'addressable_lambda', AddressableLambdaLightEffect, "Addressable Lambda", {
"addressable_lambda",
AddressableLambdaLightEffect,
"Addressable Lambda",
{
cv.Required(CONF_LAMBDA): cv.lambda_,
cv.Optional(CONF_UPDATE_INTERVAL, default='0ms'): cv.positive_time_period_milliseconds,
}
cv.Optional(
CONF_UPDATE_INTERVAL, default="0ms"
): cv.positive_time_period_milliseconds,
},
)
def addressable_lambda_effect_to_code(config, effect_id):
args = [(AddressableLightRef, 'it'), (ESPColor, 'current_color'), (bool, 'initial_run')]
args = [
(AddressableLightRef, "it"),
(Color, "current_color"),
(bool, "initial_run"),
]
lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], args, return_type=cg.void)
var = cg.new_Pvariable(effect_id, config[CONF_NAME], lambda_,
config[CONF_UPDATE_INTERVAL])
var = cg.new_Pvariable(
effect_id, config[CONF_NAME], lambda_, config[CONF_UPDATE_INTERVAL]
)
yield var
@register_addressable_effect('addressable_rainbow', AddressableRainbowLightEffect, "Rainbow", {
cv.Optional(CONF_SPEED, default=10): cv.uint32_t,
cv.Optional(CONF_WIDTH, default=50): cv.uint32_t,
})
@register_addressable_effect(
"addressable_rainbow",
AddressableRainbowLightEffect,
"Rainbow",
{
cv.Optional(CONF_SPEED, default=10): cv.uint32_t,
cv.Optional(CONF_WIDTH, default=50): cv.uint32_t,
},
)
def addressable_rainbow_effect_to_code(config, effect_id):
var = cg.new_Pvariable(effect_id, config[CONF_NAME])
cg.add(var.set_speed(config[CONF_SPEED]))
@ -180,41 +292,61 @@ def addressable_rainbow_effect_to_code(config, effect_id):
yield var
@register_addressable_effect('addressable_color_wipe', AddressableColorWipeEffect, "Color Wipe", {
cv.Optional(CONF_COLORS, default=[{CONF_NUM_LEDS: 1, CONF_RANDOM: True}]): cv.ensure_list({
cv.Optional(CONF_RED, default=1.0): cv.percentage,
cv.Optional(CONF_GREEN, default=1.0): cv.percentage,
cv.Optional(CONF_BLUE, default=1.0): cv.percentage,
cv.Optional(CONF_WHITE, default=1.0): cv.percentage,
cv.Optional(CONF_RANDOM, default=False): cv.boolean,
cv.Required(CONF_NUM_LEDS): cv.All(cv.uint32_t, cv.Range(min=1)),
}),
cv.Optional(CONF_ADD_LED_INTERVAL, default='0.1s'): cv.positive_time_period_milliseconds,
cv.Optional(CONF_REVERSE, default=False): cv.boolean,
})
@register_addressable_effect(
"addressable_color_wipe",
AddressableColorWipeEffect,
"Color Wipe",
{
cv.Optional(
CONF_COLORS, default=[{CONF_NUM_LEDS: 1, CONF_RANDOM: True}]
): cv.ensure_list(
{
cv.Optional(CONF_RED, default=1.0): cv.percentage,
cv.Optional(CONF_GREEN, default=1.0): cv.percentage,
cv.Optional(CONF_BLUE, default=1.0): cv.percentage,
cv.Optional(CONF_WHITE, default=1.0): cv.percentage,
cv.Optional(CONF_RANDOM, default=False): cv.boolean,
cv.Required(CONF_NUM_LEDS): cv.All(cv.uint32_t, cv.Range(min=1)),
}
),
cv.Optional(
CONF_ADD_LED_INTERVAL, default="0.1s"
): cv.positive_time_period_milliseconds,
cv.Optional(CONF_REVERSE, default=False): cv.boolean,
},
)
def addressable_color_wipe_effect_to_code(config, effect_id):
var = cg.new_Pvariable(effect_id, config[CONF_NAME])
cg.add(var.set_add_led_interval(config[CONF_ADD_LED_INTERVAL]))
cg.add(var.set_reverse(config[CONF_REVERSE]))
colors = []
for color in config.get(CONF_COLORS, []):
colors.append(cg.StructInitializer(
AddressableColorWipeEffectColor,
('r', int(round(color[CONF_RED] * 255))),
('g', int(round(color[CONF_GREEN] * 255))),
('b', int(round(color[CONF_BLUE] * 255))),
('w', int(round(color[CONF_WHITE] * 255))),
('random', color[CONF_RANDOM]),
('num_leds', color[CONF_NUM_LEDS]),
))
colors.append(
cg.StructInitializer(
AddressableColorWipeEffectColor,
("r", int(round(color[CONF_RED] * 255))),
("g", int(round(color[CONF_GREEN] * 255))),
("b", int(round(color[CONF_BLUE] * 255))),
("w", int(round(color[CONF_WHITE] * 255))),
("random", color[CONF_RANDOM]),
("num_leds", color[CONF_NUM_LEDS]),
)
)
cg.add(var.set_colors(colors))
yield var
@register_addressable_effect('addressable_scan', AddressableScanEffect, "Scan", {
cv.Optional(CONF_MOVE_INTERVAL, default='0.1s'): cv.positive_time_period_milliseconds,
cv.Optional(CONF_SCAN_WIDTH, default=1): cv.int_range(min=1),
})
@register_addressable_effect(
"addressable_scan",
AddressableScanEffect,
"Scan",
{
cv.Optional(
CONF_MOVE_INTERVAL, default="0.1s"
): cv.positive_time_period_milliseconds,
cv.Optional(CONF_SCAN_WIDTH, default=1): cv.int_range(min=1),
},
)
def addressable_scan_effect_to_code(config, effect_id):
var = cg.new_Pvariable(effect_id, config[CONF_NAME])
cg.add(var.set_move_interval(config[CONF_MOVE_INTERVAL]))
@ -222,10 +354,17 @@ def addressable_scan_effect_to_code(config, effect_id):
yield var
@register_addressable_effect('addressable_twinkle', AddressableTwinkleEffect, "Twinkle", {
cv.Optional(CONF_TWINKLE_PROBABILITY, default='5%'): cv.percentage,
cv.Optional(CONF_PROGRESS_INTERVAL, default='4ms'): cv.positive_time_period_milliseconds,
})
@register_addressable_effect(
"addressable_twinkle",
AddressableTwinkleEffect,
"Twinkle",
{
cv.Optional(CONF_TWINKLE_PROBABILITY, default="5%"): cv.percentage,
cv.Optional(
CONF_PROGRESS_INTERVAL, default="4ms"
): cv.positive_time_period_milliseconds,
},
)
def addressable_twinkle_effect_to_code(config, effect_id):
var = cg.new_Pvariable(effect_id, config[CONF_NAME])
cg.add(var.set_twinkle_probability(config[CONF_TWINKLE_PROBABILITY]))
@ -234,10 +373,15 @@ def addressable_twinkle_effect_to_code(config, effect_id):
@register_addressable_effect(
'addressable_random_twinkle', AddressableRandomTwinkleEffect, "Random Twinkle", {
cv.Optional(CONF_TWINKLE_PROBABILITY, default='5%'): cv.percentage,
cv.Optional(CONF_PROGRESS_INTERVAL, default='32ms'): cv.positive_time_period_milliseconds,
}
"addressable_random_twinkle",
AddressableRandomTwinkleEffect,
"Random Twinkle",
{
cv.Optional(CONF_TWINKLE_PROBABILITY, default="5%"): cv.percentage,
cv.Optional(
CONF_PROGRESS_INTERVAL, default="32ms"
): cv.positive_time_period_milliseconds,
},
)
def addressable_random_twinkle_effect_to_code(config, effect_id):
var = cg.new_Pvariable(effect_id, config[CONF_NAME])
@ -246,12 +390,19 @@ def addressable_random_twinkle_effect_to_code(config, effect_id):
yield var
@register_addressable_effect('addressable_fireworks', AddressableFireworksEffect, "Fireworks", {
cv.Optional(CONF_UPDATE_INTERVAL, default='32ms'): cv.positive_time_period_milliseconds,
cv.Optional(CONF_SPARK_PROBABILITY, default='10%'): cv.percentage,
cv.Optional(CONF_USE_RANDOM_COLOR, default=False): cv.boolean,
cv.Optional(CONF_FADE_OUT_RATE, default=120): cv.uint8_t,
})
@register_addressable_effect(
"addressable_fireworks",
AddressableFireworksEffect,
"Fireworks",
{
cv.Optional(
CONF_UPDATE_INTERVAL, default="32ms"
): cv.positive_time_period_milliseconds,
cv.Optional(CONF_SPARK_PROBABILITY, default="10%"): cv.percentage,
cv.Optional(CONF_USE_RANDOM_COLOR, default=False): cv.boolean,
cv.Optional(CONF_FADE_OUT_RATE, default=120): cv.uint8_t,
},
)
def addressable_fireworks_effect_to_code(config, effect_id):
var = cg.new_Pvariable(effect_id, config[CONF_NAME])
cg.add(var.set_update_interval(config[CONF_UPDATE_INTERVAL]))
@ -262,10 +413,15 @@ def addressable_fireworks_effect_to_code(config, effect_id):
@register_addressable_effect(
'addressable_flicker', AddressableFlickerEffect, "Addressable Flicker", {
cv.Optional(CONF_UPDATE_INTERVAL, default='16ms'): cv.positive_time_period_milliseconds,
cv.Optional(CONF_INTENSITY, default='5%'): cv.percentage,
}
"addressable_flicker",
AddressableFlickerEffect,
"Addressable Flicker",
{
cv.Optional(
CONF_UPDATE_INTERVAL, default="16ms"
): cv.positive_time_period_milliseconds,
cv.Optional(CONF_INTENSITY, default="5%"): cv.percentage,
},
)
def addressable_flicker_effect_to_code(config, effect_id):
var = cg.new_Pvariable(effect_id, config[CONF_NAME])
@ -276,22 +432,28 @@ def addressable_flicker_effect_to_code(config, effect_id):
def validate_effects(allowed_effects):
def validator(value):
value = cv.validate_registry('effect', EFFECTS_REGISTRY)(value)
value = cv.validate_registry("effect", EFFECTS_REGISTRY)(value)
errors = []
names = set()
for i, x in enumerate(value):
key = next(it for it in x.keys())
if key not in allowed_effects:
errors.append(
cv.Invalid("The effect '{}' is not allowed for this "
"light type".format(key), [i])
cv.Invalid(
"The effect '{}' is not allowed for this "
"light type".format(key),
[i],
)
)
continue
name = x[key][CONF_NAME]
if name in names:
errors.append(
cv.Invalid("Found the effect name '{}' twice. All effects must have "
"unique names".format(name), [i])
cv.Invalid(
"Found the effect name '{}' twice. All effects must have "
"unique names".format(name),
[i],
)
)
continue
names.add(name)

View File

@ -2,47 +2,62 @@ import esphome.codegen as cg
from esphome import automation
# Base
light_ns = cg.esphome_ns.namespace('light')
LightState = light_ns.class_('LightState', cg.Nameable, cg.Component)
light_ns = cg.esphome_ns.namespace("light")
LightState = light_ns.class_("LightState", cg.Nameable, cg.Component)
# Fake class for addressable lights
AddressableLightState = light_ns.class_('LightState', LightState)
LightOutput = light_ns.class_('LightOutput')
AddressableLight = light_ns.class_('AddressableLight', cg.Component)
AddressableLightRef = AddressableLight.operator('ref')
AddressableLightState = light_ns.class_("LightState", LightState)
LightOutput = light_ns.class_("LightOutput")
AddressableLight = light_ns.class_("AddressableLight", cg.Component)
AddressableLightRef = AddressableLight.operator("ref")
ESPColor = light_ns.class_('ESPColor')
LightColorValues = light_ns.class_('LightColorValues')
Color = cg.esphome_ns.class_("Color")
LightColorValues = light_ns.class_("LightColorValues")
# Actions
ToggleAction = light_ns.class_('ToggleAction', automation.Action)
LightControlAction = light_ns.class_('LightControlAction', automation.Action)
DimRelativeAction = light_ns.class_('DimRelativeAction', automation.Action)
AddressableSet = light_ns.class_('AddressableSet', automation.Action)
LightIsOnCondition = light_ns.class_('LightIsOnCondition', automation.Condition)
LightIsOffCondition = light_ns.class_('LightIsOffCondition', automation.Condition)
ToggleAction = light_ns.class_("ToggleAction", automation.Action)
LightControlAction = light_ns.class_("LightControlAction", automation.Action)
DimRelativeAction = light_ns.class_("DimRelativeAction", automation.Action)
AddressableSet = light_ns.class_("AddressableSet", automation.Action)
LightIsOnCondition = light_ns.class_("LightIsOnCondition", automation.Condition)
LightIsOffCondition = light_ns.class_("LightIsOffCondition", automation.Condition)
# Triggers
LightTurnOnTrigger = light_ns.class_('LightTurnOnTrigger', automation.Trigger.template())
LightTurnOffTrigger = light_ns.class_('LightTurnOffTrigger', automation.Trigger.template())
LightTurnOnTrigger = light_ns.class_(
"LightTurnOnTrigger", automation.Trigger.template()
)
LightTurnOffTrigger = light_ns.class_(
"LightTurnOffTrigger", automation.Trigger.template()
)
# Effects
LightEffect = light_ns.class_('LightEffect')
RandomLightEffect = light_ns.class_('RandomLightEffect', LightEffect)
LambdaLightEffect = light_ns.class_('LambdaLightEffect', LightEffect)
AutomationLightEffect = light_ns.class_('AutomationLightEffect', LightEffect)
StrobeLightEffect = light_ns.class_('StrobeLightEffect', LightEffect)
StrobeLightEffectColor = light_ns.class_('StrobeLightEffectColor', LightEffect)
FlickerLightEffect = light_ns.class_('FlickerLightEffect', LightEffect)
AddressableLightEffect = light_ns.class_('AddressableLightEffect', LightEffect)
AddressableLambdaLightEffect = light_ns.class_('AddressableLambdaLightEffect',
AddressableLightEffect)
AddressableRainbowLightEffect = light_ns.class_('AddressableRainbowLightEffect',
AddressableLightEffect)
AddressableColorWipeEffect = light_ns.class_('AddressableColorWipeEffect', AddressableLightEffect)
AddressableColorWipeEffectColor = light_ns.struct('AddressableColorWipeEffectColor')
AddressableScanEffect = light_ns.class_('AddressableScanEffect', AddressableLightEffect)
AddressableTwinkleEffect = light_ns.class_('AddressableTwinkleEffect', AddressableLightEffect)
AddressableRandomTwinkleEffect = light_ns.class_('AddressableRandomTwinkleEffect',
AddressableLightEffect)
AddressableFireworksEffect = light_ns.class_('AddressableFireworksEffect', AddressableLightEffect)
AddressableFlickerEffect = light_ns.class_('AddressableFlickerEffect', AddressableLightEffect)
LightEffect = light_ns.class_("LightEffect")
RandomLightEffect = light_ns.class_("RandomLightEffect", LightEffect)
LambdaLightEffect = light_ns.class_("LambdaLightEffect", LightEffect)
AutomationLightEffect = light_ns.class_("AutomationLightEffect", LightEffect)
StrobeLightEffect = light_ns.class_("StrobeLightEffect", LightEffect)
StrobeLightEffectColor = light_ns.class_("StrobeLightEffectColor", LightEffect)
FlickerLightEffect = light_ns.class_("FlickerLightEffect", LightEffect)
AddressableLightEffect = light_ns.class_("AddressableLightEffect", LightEffect)
AddressableLambdaLightEffect = light_ns.class_(
"AddressableLambdaLightEffect", AddressableLightEffect
)
AddressableRainbowLightEffect = light_ns.class_(
"AddressableRainbowLightEffect", AddressableLightEffect
)
AddressableColorWipeEffect = light_ns.class_(
"AddressableColorWipeEffect", AddressableLightEffect
)
AddressableColorWipeEffectColor = light_ns.struct("AddressableColorWipeEffectColor")
AddressableScanEffect = light_ns.class_("AddressableScanEffect", AddressableLightEffect)
AddressableTwinkleEffect = light_ns.class_(
"AddressableTwinkleEffect", AddressableLightEffect
)
AddressableRandomTwinkleEffect = light_ns.class_(
"AddressableRandomTwinkleEffect", AddressableLightEffect
)
AddressableFireworksEffect = light_ns.class_(
"AddressableFireworksEffect", AddressableLightEffect
)
AddressableFlickerEffect = light_ns.class_(
"AddressableFlickerEffect", AddressableLightEffect
)

View File

@ -4,49 +4,69 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.automation import LambdaAction
from esphome.const import CONF_ARGS, CONF_BAUD_RATE, CONF_FORMAT, CONF_HARDWARE_UART, CONF_ID, \
CONF_LEVEL, CONF_LOGS, CONF_ON_MESSAGE, CONF_TAG, CONF_TRIGGER_ID, CONF_TX_BUFFER_SIZE
from esphome.const import (
CONF_ARGS,
CONF_BAUD_RATE,
CONF_FORMAT,
CONF_HARDWARE_UART,
CONF_ID,
CONF_LEVEL,
CONF_LOGS,
CONF_ON_MESSAGE,
CONF_TAG,
CONF_TRIGGER_ID,
CONF_TX_BUFFER_SIZE,
)
from esphome.core import CORE, EsphomeError, Lambda, coroutine_with_priority
CODEOWNERS = ['@esphome/core']
logger_ns = cg.esphome_ns.namespace('logger')
CODEOWNERS = ["@esphome/core"]
logger_ns = cg.esphome_ns.namespace("logger")
LOG_LEVELS = {
'NONE': cg.global_ns.ESPHOME_LOG_LEVEL_NONE,
'ERROR': cg.global_ns.ESPHOME_LOG_LEVEL_ERROR,
'WARN': cg.global_ns.ESPHOME_LOG_LEVEL_WARN,
'INFO': cg.global_ns.ESPHOME_LOG_LEVEL_INFO,
'DEBUG': cg.global_ns.ESPHOME_LOG_LEVEL_DEBUG,
'VERBOSE': cg.global_ns.ESPHOME_LOG_LEVEL_VERBOSE,
'VERY_VERBOSE': cg.global_ns.ESPHOME_LOG_LEVEL_VERY_VERBOSE,
"NONE": cg.global_ns.ESPHOME_LOG_LEVEL_NONE,
"ERROR": cg.global_ns.ESPHOME_LOG_LEVEL_ERROR,
"WARN": cg.global_ns.ESPHOME_LOG_LEVEL_WARN,
"INFO": cg.global_ns.ESPHOME_LOG_LEVEL_INFO,
"DEBUG": cg.global_ns.ESPHOME_LOG_LEVEL_DEBUG,
"VERBOSE": cg.global_ns.ESPHOME_LOG_LEVEL_VERBOSE,
"VERY_VERBOSE": cg.global_ns.ESPHOME_LOG_LEVEL_VERY_VERBOSE,
}
LOG_LEVEL_TO_ESP_LOG = {
'ERROR': cg.global_ns.ESP_LOGE,
'WARN': cg.global_ns.ESP_LOGW,
'INFO': cg.global_ns.ESP_LOGI,
'DEBUG': cg.global_ns.ESP_LOGD,
'VERBOSE': cg.global_ns.ESP_LOGV,
'VERY_VERBOSE': cg.global_ns.ESP_LOGVV,
"ERROR": cg.global_ns.ESP_LOGE,
"WARN": cg.global_ns.ESP_LOGW,
"INFO": cg.global_ns.ESP_LOGI,
"DEBUG": cg.global_ns.ESP_LOGD,
"VERBOSE": cg.global_ns.ESP_LOGV,
"VERY_VERBOSE": cg.global_ns.ESP_LOGVV,
}
LOG_LEVEL_SEVERITY = ['NONE', 'ERROR', 'WARN', 'INFO', 'CONFIG', 'DEBUG', 'VERBOSE', 'VERY_VERBOSE']
LOG_LEVEL_SEVERITY = [
"NONE",
"ERROR",
"WARN",
"INFO",
"CONFIG",
"DEBUG",
"VERBOSE",
"VERY_VERBOSE",
]
UART_SELECTION_ESP32 = ['UART0', 'UART1', 'UART2']
UART_SELECTION_ESP32 = ["UART0", "UART1", "UART2"]
UART_SELECTION_ESP8266 = ['UART0', 'UART0_SWAP', 'UART1']
UART_SELECTION_ESP8266 = ["UART0", "UART0_SWAP", "UART1"]
HARDWARE_UART_TO_UART_SELECTION = {
'UART0': logger_ns.UART_SELECTION_UART0,
'UART0_SWAP': logger_ns.UART_SELECTION_UART0_SWAP,
'UART1': logger_ns.UART_SELECTION_UART1,
'UART2': logger_ns.UART_SELECTION_UART2,
"UART0": logger_ns.UART_SELECTION_UART0,
"UART0_SWAP": logger_ns.UART_SELECTION_UART0_SWAP,
"UART1": logger_ns.UART_SELECTION_UART1,
"UART2": logger_ns.UART_SELECTION_UART2,
}
HARDWARE_UART_TO_SERIAL = {
'UART0': cg.global_ns.Serial,
'UART0_SWAP': cg.global_ns.Serial,
'UART1': cg.global_ns.Serial1,
'UART2': cg.global_ns.Serial2,
"UART0": cg.global_ns.Serial,
"UART0_SWAP": cg.global_ns.Serial,
"UART1": cg.global_ns.Serial1,
"UART2": cg.global_ns.Serial2,
}
is_log_level = cv.one_of(*LOG_LEVELS, upper=True)
@ -61,45 +81,59 @@ def uart_selection(value):
def validate_local_no_higher_than_global(value):
global_level = value.get(CONF_LEVEL, 'DEBUG')
global_level = value.get(CONF_LEVEL, "DEBUG")
for tag, level in value.get(CONF_LOGS, {}).items():
if LOG_LEVEL_SEVERITY.index(level) > LOG_LEVEL_SEVERITY.index(global_level):
raise EsphomeError("The local log level {} for {} must be less severe than the "
"global log level {}.".format(level, tag, global_level))
raise EsphomeError(
"The local log level {} for {} must be less severe than the "
"global log level {}.".format(level, tag, global_level)
)
return value
Logger = logger_ns.class_('Logger', cg.Component)
LoggerMessageTrigger = logger_ns.class_('LoggerMessageTrigger',
automation.Trigger.template(cg.int_, cg.const_char_ptr,
cg.const_char_ptr))
Logger = logger_ns.class_("Logger", cg.Component)
LoggerMessageTrigger = logger_ns.class_(
"LoggerMessageTrigger",
automation.Trigger.template(cg.int_, cg.const_char_ptr, cg.const_char_ptr),
)
CONF_ESP8266_STORE_LOG_STRINGS_IN_FLASH = 'esp8266_store_log_strings_in_flash'
CONFIG_SCHEMA = cv.All(cv.Schema({
cv.GenerateID(): cv.declare_id(Logger),
cv.Optional(CONF_BAUD_RATE, default=115200): cv.positive_int,
cv.Optional(CONF_TX_BUFFER_SIZE, default=512): cv.validate_bytes,
cv.Optional(CONF_HARDWARE_UART, default='UART0'): uart_selection,
cv.Optional(CONF_LEVEL, default='DEBUG'): is_log_level,
cv.Optional(CONF_LOGS, default={}): cv.Schema({
cv.string: is_log_level,
}),
cv.Optional(CONF_ON_MESSAGE): automation.validate_automation({
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LoggerMessageTrigger),
cv.Optional(CONF_LEVEL, default='WARN'): is_log_level,
}),
cv.SplitDefault(CONF_ESP8266_STORE_LOG_STRINGS_IN_FLASH, esp8266=True):
cv.All(cv.only_on_esp8266, cv.boolean),
}).extend(cv.COMPONENT_SCHEMA), validate_local_no_higher_than_global)
CONF_ESP8266_STORE_LOG_STRINGS_IN_FLASH = "esp8266_store_log_strings_in_flash"
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(): cv.declare_id(Logger),
cv.Optional(CONF_BAUD_RATE, default=115200): cv.positive_int,
cv.Optional(CONF_TX_BUFFER_SIZE, default=512): cv.validate_bytes,
cv.Optional(CONF_HARDWARE_UART, default="UART0"): uart_selection,
cv.Optional(CONF_LEVEL, default="DEBUG"): is_log_level,
cv.Optional(CONF_LOGS, default={}): cv.Schema(
{
cv.string: is_log_level,
}
),
cv.Optional(CONF_ON_MESSAGE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LoggerMessageTrigger),
cv.Optional(CONF_LEVEL, default="WARN"): is_log_level,
}
),
cv.SplitDefault(
CONF_ESP8266_STORE_LOG_STRINGS_IN_FLASH, esp8266=True
): cv.All(cv.only_on_esp8266, cv.boolean),
}
).extend(cv.COMPONENT_SCHEMA),
validate_local_no_higher_than_global,
)
@coroutine_with_priority(90.0)
def to_code(config):
baud_rate = config[CONF_BAUD_RATE]
rhs = Logger.new(baud_rate,
config[CONF_TX_BUFFER_SIZE],
HARDWARE_UART_TO_UART_SELECTION[config[CONF_HARDWARE_UART]])
rhs = Logger.new(
baud_rate,
config[CONF_TX_BUFFER_SIZE],
HARDWARE_UART_TO_UART_SELECTION[config[CONF_HARDWARE_UART]],
)
log = cg.Pvariable(config[CONF_ID], rhs)
cg.add(log.pre_setup())
@ -107,12 +141,12 @@ def to_code(config):
cg.add(log.set_log_level(tag, LOG_LEVELS[level]))
level = config[CONF_LEVEL]
cg.add_define('USE_LOGGER')
cg.add_define("USE_LOGGER")
this_severity = LOG_LEVEL_SEVERITY.index(level)
cg.add_build_flag('-DESPHOME_LOG_LEVEL={}'.format(LOG_LEVELS[level]))
cg.add_build_flag("-DESPHOME_LOG_LEVEL={}".format(LOG_LEVELS[level]))
verbose_severity = LOG_LEVEL_SEVERITY.index('VERBOSE')
very_verbose_severity = LOG_LEVEL_SEVERITY.index('VERY_VERBOSE')
verbose_severity = LOG_LEVEL_SEVERITY.index("VERBOSE")
very_verbose_severity = LOG_LEVEL_SEVERITY.index("VERY_VERBOSE")
is_at_least_verbose = this_severity >= verbose_severity
is_at_least_very_verbose = this_severity >= very_verbose_severity
has_serial_logging = baud_rate != 0
@ -122,35 +156,42 @@ def to_code(config):
cg.add_build_flag(f"-DDEBUG_ESP_PORT={debug_serial_port}")
cg.add_build_flag("-DLWIP_DEBUG")
DEBUG_COMPONENTS = {
'HTTP_CLIENT',
'HTTP_SERVER',
'HTTP_UPDATE',
'OTA',
'SSL',
'TLS_MEM',
'UPDATER',
'WIFI',
"HTTP_CLIENT",
"HTTP_SERVER",
"HTTP_UPDATE",
"OTA",
"SSL",
"TLS_MEM",
"UPDATER",
"WIFI",
# Spams logs too much:
# 'MDNS_RESPONDER',
}
for comp in DEBUG_COMPONENTS:
cg.add_build_flag(f"-DDEBUG_ESP_{comp}")
if CORE.is_esp32 and is_at_least_verbose:
cg.add_build_flag('-DCORE_DEBUG_LEVEL=5')
cg.add_build_flag("-DCORE_DEBUG_LEVEL=5")
if CORE.is_esp32 and is_at_least_very_verbose:
cg.add_build_flag('-DENABLE_I2C_DEBUG_BUFFER')
cg.add_build_flag("-DENABLE_I2C_DEBUG_BUFFER")
if config.get(CONF_ESP8266_STORE_LOG_STRINGS_IN_FLASH):
cg.add_build_flag('-DUSE_STORE_LOG_STR_IN_FLASH')
cg.add_build_flag("-DUSE_STORE_LOG_STR_IN_FLASH")
# Register at end for safe mode
yield cg.register_component(log, config)
for conf in config.get(CONF_ON_MESSAGE, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], log,
LOG_LEVEL_SEVERITY.index(conf[CONF_LEVEL]))
yield automation.build_automation(trigger, [(cg.int_, 'level'),
(cg.const_char_ptr, 'tag'),
(cg.const_char_ptr, 'message')], conf)
trigger = cg.new_Pvariable(
conf[CONF_TRIGGER_ID], log, LOG_LEVEL_SEVERITY.index(conf[CONF_LEVEL])
)
yield automation.build_automation(
trigger,
[
(cg.int_, "level"),
(cg.const_char_ptr, "tag"),
(cg.const_char_ptr, "message"),
],
conf,
)
def maybe_simple_message(schema):
@ -179,18 +220,27 @@ def validate_printf(value):
""" # noqa
matches = re.findall(cfmt, value[CONF_FORMAT], flags=re.X)
if len(matches) != len(value[CONF_ARGS]):
raise cv.Invalid("Found {} printf-patterns ({}), but {} args were given!"
"".format(len(matches), ', '.join(matches), len(value[CONF_ARGS])))
raise cv.Invalid(
"Found {} printf-patterns ({}), but {} args were given!"
"".format(len(matches), ", ".join(matches), len(value[CONF_ARGS]))
)
return value
CONF_LOGGER_LOG = 'logger.log'
LOGGER_LOG_ACTION_SCHEMA = cv.All(maybe_simple_message({
cv.Required(CONF_FORMAT): cv.string,
cv.Optional(CONF_ARGS, default=list): cv.ensure_list(cv.lambda_),
cv.Optional(CONF_LEVEL, default="DEBUG"): cv.one_of(*LOG_LEVEL_TO_ESP_LOG, upper=True),
cv.Optional(CONF_TAG, default="main"): cv.string,
}), validate_printf)
CONF_LOGGER_LOG = "logger.log"
LOGGER_LOG_ACTION_SCHEMA = cv.All(
maybe_simple_message(
{
cv.Required(CONF_FORMAT): cv.string,
cv.Optional(CONF_ARGS, default=list): cv.ensure_list(cv.lambda_),
cv.Optional(CONF_LEVEL, default="DEBUG"): cv.one_of(
*LOG_LEVEL_TO_ESP_LOG, upper=True
),
cv.Optional(CONF_TAG, default="main"): cv.string,
}
),
validate_printf,
)
@automation.register_action(CONF_LOGGER_LOG, LambdaAction, LOGGER_LOG_ACTION_SCHEMA)

View File

@ -1,17 +1,32 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, spi
from esphome.const import CONF_ID, CONF_REFERENCE_TEMPERATURE, ICON_THERMOMETER, UNIT_CELSIUS
from esphome.const import (
CONF_ID,
CONF_REFERENCE_TEMPERATURE,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
UNIT_CELSIUS,
)
max31855_ns = cg.esphome_ns.namespace('max31855')
MAX31855Sensor = max31855_ns.class_('MAX31855Sensor', sensor.Sensor, cg.PollingComponent,
spi.SPIDevice)
max31855_ns = cg.esphome_ns.namespace("max31855")
MAX31855Sensor = max31855_ns.class_(
"MAX31855Sensor", sensor.Sensor, cg.PollingComponent, spi.SPIDevice
)
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({
cv.GenerateID(): cv.declare_id(MAX31855Sensor),
cv.Optional(CONF_REFERENCE_TEMPERATURE):
sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 2),
}).extend(cv.polling_component_schema('60s')).extend(spi.spi_device_schema())
CONFIG_SCHEMA = (
sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE)
.extend(
{
cv.GenerateID(): cv.declare_id(MAX31855Sensor),
cv.Optional(CONF_REFERENCE_TEMPERATURE): sensor.sensor_schema(
UNIT_CELSIUS, ICON_EMPTY, 2, DEVICE_CLASS_TEMPERATURE
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(spi.spi_device_schema())
)
def to_code(config):

View File

@ -1,22 +1,38 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, spi
from esphome.const import CONF_ID, CONF_MAINS_FILTER, ICON_THERMOMETER, UNIT_CELSIUS
from esphome.const import (
CONF_ID,
CONF_MAINS_FILTER,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
UNIT_CELSIUS,
)
max31856_ns = cg.esphome_ns.namespace('max31856')
MAX31856Sensor = max31856_ns.class_('MAX31856Sensor', sensor.Sensor, cg.PollingComponent,
spi.SPIDevice)
max31856_ns = cg.esphome_ns.namespace("max31856")
MAX31856Sensor = max31856_ns.class_(
"MAX31856Sensor", sensor.Sensor, cg.PollingComponent, spi.SPIDevice
)
MAX31865ConfigFilter = max31856_ns.enum('MAX31856ConfigFilter')
MAX31865ConfigFilter = max31856_ns.enum("MAX31856ConfigFilter")
FILTER = {
'50HZ': MAX31865ConfigFilter.FILTER_50HZ,
'60HZ': MAX31865ConfigFilter.FILTER_60HZ,
"50HZ": MAX31865ConfigFilter.FILTER_50HZ,
"60HZ": MAX31865ConfigFilter.FILTER_60HZ,
}
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({
cv.GenerateID(): cv.declare_id(MAX31856Sensor),
cv.Optional(CONF_MAINS_FILTER, default='60HZ'): cv.enum(FILTER, upper=True, space=''),
}).extend(cv.polling_component_schema('60s')).extend(spi.spi_device_schema())
CONFIG_SCHEMA = (
sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE)
.extend(
{
cv.GenerateID(): cv.declare_id(MAX31856Sensor),
cv.Optional(CONF_MAINS_FILTER, default="60HZ"): cv.enum(
FILTER, upper=True, space=""
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(spi.spi_device_schema())
)
def to_code(config):

View File

@ -1,26 +1,48 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, spi
from esphome.const import CONF_ID, CONF_MAINS_FILTER, CONF_REFERENCE_RESISTANCE, \
CONF_RTD_NOMINAL_RESISTANCE, CONF_RTD_WIRES, ICON_THERMOMETER, UNIT_CELSIUS
from esphome.const import (
CONF_ID,
CONF_MAINS_FILTER,
CONF_REFERENCE_RESISTANCE,
CONF_RTD_NOMINAL_RESISTANCE,
CONF_RTD_WIRES,
DEVICE_CLASS_TEMPERATURE,
ICON_EMPTY,
UNIT_CELSIUS,
)
max31865_ns = cg.esphome_ns.namespace('max31865')
MAX31865Sensor = max31865_ns.class_('MAX31865Sensor', sensor.Sensor, cg.PollingComponent,
spi.SPIDevice)
max31865_ns = cg.esphome_ns.namespace("max31865")
MAX31865Sensor = max31865_ns.class_(
"MAX31865Sensor", sensor.Sensor, cg.PollingComponent, spi.SPIDevice
)
MAX31865ConfigFilter = max31865_ns.enum('MAX31865ConfigFilter')
MAX31865ConfigFilter = max31865_ns.enum("MAX31865ConfigFilter")
FILTER = {
'50HZ': MAX31865ConfigFilter.FILTER_50HZ,
'60HZ': MAX31865ConfigFilter.FILTER_60HZ,
"50HZ": MAX31865ConfigFilter.FILTER_50HZ,
"60HZ": MAX31865ConfigFilter.FILTER_60HZ,
}
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 2).extend({
cv.GenerateID(): cv.declare_id(MAX31865Sensor),
cv.Required(CONF_REFERENCE_RESISTANCE): cv.All(cv.resistance, cv.Range(min=100, max=10000)),
cv.Required(CONF_RTD_NOMINAL_RESISTANCE): cv.All(cv.resistance, cv.Range(min=100, max=1000)),
cv.Optional(CONF_MAINS_FILTER, default='60HZ'): cv.enum(FILTER, upper=True, space=''),
cv.Optional(CONF_RTD_WIRES, default=4): cv.int_range(min=2, max=4),
}).extend(cv.polling_component_schema('60s')).extend(spi.spi_device_schema())
CONFIG_SCHEMA = (
sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 2, DEVICE_CLASS_TEMPERATURE)
.extend(
{
cv.GenerateID(): cv.declare_id(MAX31865Sensor),
cv.Required(CONF_REFERENCE_RESISTANCE): cv.All(
cv.resistance, cv.Range(min=100, max=10000)
),
cv.Required(CONF_RTD_NOMINAL_RESISTANCE): cv.All(
cv.resistance, cv.Range(min=100, max=1000)
),
cv.Optional(CONF_MAINS_FILTER, default="60HZ"): cv.enum(
FILTER, upper=True, space=""
),
cv.Optional(CONF_RTD_WIRES, default=4): cv.int_range(min=2, max=4),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(spi.spi_device_schema())
)
def to_code(config):

View File

@ -1,15 +1,23 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, spi
from esphome.const import CONF_ID, ICON_THERMOMETER, UNIT_CELSIUS
from esphome.const import CONF_ID, DEVICE_CLASS_TEMPERATURE, ICON_EMPTY, UNIT_CELSIUS
max6675_ns = cg.esphome_ns.namespace('max6675')
MAX6675Sensor = max6675_ns.class_('MAX6675Sensor', sensor.Sensor, cg.PollingComponent,
spi.SPIDevice)
max6675_ns = cg.esphome_ns.namespace("max6675")
MAX6675Sensor = max6675_ns.class_(
"MAX6675Sensor", sensor.Sensor, cg.PollingComponent, spi.SPIDevice
)
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1).extend({
cv.GenerateID(): cv.declare_id(MAX6675Sensor),
}).extend(cv.polling_component_schema('60s')).extend(spi.spi_device_schema())
CONFIG_SCHEMA = (
sensor.sensor_schema(UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE)
.extend(
{
cv.GenerateID(): cv.declare_id(MAX6675Sensor),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(spi.spi_device_schema())
)
def to_code(config):

View File

@ -3,21 +3,28 @@ import esphome.config_validation as cv
from esphome.components import display, spi
from esphome.const import CONF_ID, CONF_INTENSITY, CONF_LAMBDA, CONF_NUM_CHIPS
DEPENDENCIES = ['spi']
DEPENDENCIES = ["spi"]
max7219_ns = cg.esphome_ns.namespace('max7219')
MAX7219Component = max7219_ns.class_('MAX7219Component', cg.PollingComponent, spi.SPIDevice)
MAX7219ComponentRef = MAX7219Component.operator('ref')
max7219_ns = cg.esphome_ns.namespace("max7219")
MAX7219Component = max7219_ns.class_(
"MAX7219Component", cg.PollingComponent, spi.SPIDevice
)
MAX7219ComponentRef = MAX7219Component.operator("ref")
CONF_REVERSE_ENABLE = 'reverse_enable'
CONF_REVERSE_ENABLE = "reverse_enable"
CONFIG_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(MAX7219Component),
cv.Optional(CONF_NUM_CHIPS, default=1): cv.int_range(min=1, max=255),
cv.Optional(CONF_INTENSITY, default=15): cv.int_range(min=0, max=15),
cv.Optional(CONF_REVERSE_ENABLE, default=False): cv.boolean,
}).extend(cv.polling_component_schema('1s')).extend(spi.spi_device_schema())
CONFIG_SCHEMA = (
display.BASIC_DISPLAY_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(MAX7219Component),
cv.Optional(CONF_NUM_CHIPS, default=1): cv.int_range(min=1, max=255),
cv.Optional(CONF_INTENSITY, default=15): cv.int_range(min=0, max=15),
cv.Optional(CONF_REVERSE_ENABLE, default=False): cv.boolean,
}
)
.extend(cv.polling_component_schema("1s"))
.extend(spi.spi_device_schema())
)
def to_code(config):
@ -31,6 +38,7 @@ def to_code(config):
cg.add(var.set_reverse(config[CONF_REVERSE_ENABLE]))
if CONF_LAMBDA in config:
lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(MAX7219ComponentRef, 'it')],
return_type=cg.void)
lambda_ = yield cg.process_lambda(
config[CONF_LAMBDA], [(MAX7219ComponentRef, "it")], return_type=cg.void
)
cg.add(var.set_writer(lambda_))

View File

@ -3,45 +3,61 @@ import esphome.config_validation as cv
from esphome.components import display, spi
from esphome.const import CONF_ID, CONF_INTENSITY, CONF_LAMBDA, CONF_NUM_CHIPS
DEPENDENCIES = ['spi']
CODEOWNERS = ["@rspaargaren"]
DEPENDENCIES = ["spi"]
CONF_ROTATE_CHIP = 'rotate_chip'
CONF_SCROLL_SPEED = 'scroll_speed'
CONF_SCROLL_DWELL = 'scroll_dwell'
CONF_SCROLL_DELAY = 'scroll_delay'
CONF_SCROLL_ENABLE = 'scroll_enable'
CONF_SCROLL_MODE = 'scroll_mode'
CONF_REVERSE_ENABLE = 'reverse_enable'
CONF_ROTATE_CHIP = "rotate_chip"
CONF_SCROLL_SPEED = "scroll_speed"
CONF_SCROLL_DWELL = "scroll_dwell"
CONF_SCROLL_DELAY = "scroll_delay"
CONF_SCROLL_ENABLE = "scroll_enable"
CONF_SCROLL_MODE = "scroll_mode"
CONF_REVERSE_ENABLE = "reverse_enable"
SCROLL_MODES = {
'CONTINUOUS': 0,
'STOP': 1,
"CONTINUOUS": 0,
"STOP": 1,
}
CHIP_MODES = {
'0': 0,
'90': 1,
'180': 2,
'270': 3,
"0": 0,
"90": 1,
"180": 2,
"270": 3,
}
max7219_ns = cg.esphome_ns.namespace('max7219digit')
MAX7219Component = max7219_ns.class_('MAX7219Component', cg.PollingComponent, spi.SPIDevice,
display.DisplayBuffer)
MAX7219ComponentRef = MAX7219Component.operator('ref')
max7219_ns = cg.esphome_ns.namespace("max7219digit")
MAX7219Component = max7219_ns.class_(
"MAX7219Component", cg.PollingComponent, spi.SPIDevice, display.DisplayBuffer
)
MAX7219ComponentRef = MAX7219Component.operator("ref")
CONFIG_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend({
cv.GenerateID(): cv.declare_id(MAX7219Component),
cv.Optional(CONF_NUM_CHIPS, default=4): cv.int_range(min=1, max=255),
cv.Optional(CONF_INTENSITY, default=15): cv.int_range(min=0, max=15),
cv.Optional(CONF_ROTATE_CHIP, default='0'): cv.enum(CHIP_MODES, upper=True),
cv.Optional(CONF_SCROLL_MODE, default='CONTINUOUS'): cv.enum(SCROLL_MODES, upper=True),
cv.Optional(CONF_SCROLL_ENABLE, default=True): cv.boolean,
cv.Optional(CONF_SCROLL_SPEED, default='250ms'): cv.positive_time_period_milliseconds,
cv.Optional(CONF_SCROLL_DELAY, default='1000ms'): cv.positive_time_period_milliseconds,
cv.Optional(CONF_SCROLL_DWELL, default='1000ms'): cv.positive_time_period_milliseconds,
cv.Optional(CONF_REVERSE_ENABLE, default=False): cv.boolean,
}).extend(cv.polling_component_schema('500ms')).extend(spi.spi_device_schema(cs_pin_required=True))
CONFIG_SCHEMA = (
display.BASIC_DISPLAY_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(MAX7219Component),
cv.Optional(CONF_NUM_CHIPS, default=4): cv.int_range(min=1, max=255),
cv.Optional(CONF_INTENSITY, default=15): cv.int_range(min=0, max=15),
cv.Optional(CONF_ROTATE_CHIP, default="0"): cv.enum(CHIP_MODES, upper=True),
cv.Optional(CONF_SCROLL_MODE, default="CONTINUOUS"): cv.enum(
SCROLL_MODES, upper=True
),
cv.Optional(CONF_SCROLL_ENABLE, default=True): cv.boolean,
cv.Optional(
CONF_SCROLL_SPEED, default="250ms"
): cv.positive_time_period_milliseconds,
cv.Optional(
CONF_SCROLL_DELAY, default="1000ms"
): cv.positive_time_period_milliseconds,
cv.Optional(
CONF_SCROLL_DWELL, default="1000ms"
): cv.positive_time_period_milliseconds,
cv.Optional(CONF_REVERSE_ENABLE, default=False): cv.boolean,
}
)
.extend(cv.polling_component_schema("500ms"))
.extend(spi.spi_device_schema(cs_pin_required=True))
)
def to_code(config):
@ -61,6 +77,7 @@ def to_code(config):
cg.add(var.set_reverse(config[CONF_REVERSE_ENABLE]))
if CONF_LAMBDA in config:
lambda_ = yield cg.process_lambda(config[CONF_LAMBDA], [(MAX7219ComponentRef, 'it')],
return_type=cg.void)
lambda_ = yield cg.process_lambda(
config[CONF_LAMBDA], [(MAX7219ComponentRef, "it")], return_type=cg.void
)
cg.add(var.set_writer(lambda_))

View File

@ -1,53 +1,28 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import i2c
from esphome.const import CONF_ID, CONF_NUMBER, CONF_MODE, CONF_INVERTED, CONF_OPEN_DRAIN_INTERRUPT
from esphome.components import i2c, mcp23x08_base, mcp23xxx_base
from esphome.const import CONF_ID
DEPENDENCIES = ['i2c']
AUTO_LOAD = ["mcp23x08_base"]
CODEOWNERS = ["@jesserockz"]
DEPENDENCIES = ["i2c"]
MULTI_CONF = True
mcp23008_ns = cg.esphome_ns.namespace('mcp23008')
MCP23008GPIOMode = mcp23008_ns.enum('MCP23008GPIOMode')
MCP23008_GPIO_MODES = {
'INPUT': MCP23008GPIOMode.MCP23008_INPUT,
'INPUT_PULLUP': MCP23008GPIOMode.MCP23008_INPUT_PULLUP,
'OUTPUT': MCP23008GPIOMode.MCP23008_OUTPUT,
}
mcp23008_ns = cg.esphome_ns.namespace("mcp23008")
MCP23008 = mcp23008_ns.class_('MCP23008', cg.Component, i2c.I2CDevice)
MCP23008GPIOPin = mcp23008_ns.class_('MCP23008GPIOPin', cg.GPIOPin)
MCP23008 = mcp23008_ns.class_("MCP23008", mcp23x08_base.MCP23X08Base, i2c.I2CDevice)
CONFIG_SCHEMA = cv.Schema({
cv.Required(CONF_ID): cv.declare_id(MCP23008),
cv.Optional(CONF_OPEN_DRAIN_INTERRUPT, default=False): cv.boolean,
}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x20))
CONFIG_SCHEMA = (
cv.Schema(
{
cv.Required(CONF_ID): cv.declare_id(MCP23008),
}
)
.extend(mcp23xxx_base.MCP23XXX_CONFIG_SCHEMA)
.extend(i2c.i2c_device_schema(0x20))
)
def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
yield cg.register_component(var, config)
var = yield mcp23xxx_base.register_mcp23xxx(config)
yield i2c.register_i2c_device(var, config)
cg.add(var.set_open_drain_ints(config[CONF_OPEN_DRAIN_INTERRUPT]))
CONF_MCP23008 = 'mcp23008'
MCP23008_OUTPUT_PIN_SCHEMA = cv.Schema({
cv.Required(CONF_MCP23008): cv.use_id(MCP23008),
cv.Required(CONF_NUMBER): cv.int_,
cv.Optional(CONF_MODE, default="OUTPUT"): cv.enum(MCP23008_GPIO_MODES, upper=True),
cv.Optional(CONF_INVERTED, default=False): cv.boolean,
})
MCP23008_INPUT_PIN_SCHEMA = cv.Schema({
cv.Required(CONF_MCP23008): cv.use_id(MCP23008),
cv.Required(CONF_NUMBER): cv.int_,
cv.Optional(CONF_MODE, default="INPUT"): cv.enum(MCP23008_GPIO_MODES, upper=True),
cv.Optional(CONF_INVERTED, default=False): cv.boolean,
})
@pins.PIN_SCHEMA_REGISTRY.register(CONF_MCP23008,
(MCP23008_OUTPUT_PIN_SCHEMA, MCP23008_INPUT_PIN_SCHEMA))
def mcp23008_pin_to_code(config):
parent = yield cg.get_variable(config[CONF_MCP23008])
yield MCP23008GPIOPin.new(parent, config[CONF_NUMBER], config[CONF_MODE], config[CONF_INVERTED])

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