* Add black

Update pre commit

Update pre commit

add empty line

* Format with black
This commit is contained in:
Guillermo Ruffino 2021-03-07 16:03:16 -03:00 committed by GitHub
parent 2b60b0f1fa
commit 69879920eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
398 changed files with 21624 additions and 12644 deletions

View File

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

View File

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

File diff suppressed because one or more lines are too long

View File

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

View File

@ -1,7 +1,15 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import CONF_AUTOMATION_ID, CONF_CONDITION, CONF_ELSE, CONF_ID, CONF_THEN, \ from esphome.const import (
CONF_TRIGGER_ID, CONF_TYPE_ID, CONF_TIME CONF_AUTOMATION_ID,
CONF_CONDITION,
CONF_ELSE,
CONF_ID,
CONF_THEN,
CONF_TRIGGER_ID,
CONF_TYPE_ID,
CONF_TIME,
)
from esphome.core import coroutine from esphome.core import coroutine
from esphome.util import Registry from esphome.util import Registry
@ -30,36 +38,34 @@ def register_condition(name, condition_type, schema):
return CONDITION_REGISTRY.register(name, condition_type, schema) return CONDITION_REGISTRY.register(name, condition_type, schema)
Action = cg.esphome_ns.class_('Action') Action = cg.esphome_ns.class_("Action")
Trigger = cg.esphome_ns.class_('Trigger') Trigger = cg.esphome_ns.class_("Trigger")
ACTION_REGISTRY = Registry() ACTION_REGISTRY = Registry()
Condition = cg.esphome_ns.class_('Condition') Condition = cg.esphome_ns.class_("Condition")
CONDITION_REGISTRY = Registry() CONDITION_REGISTRY = Registry()
validate_action = cv.validate_registry_entry('action', ACTION_REGISTRY) validate_action = cv.validate_registry_entry("action", ACTION_REGISTRY)
validate_action_list = cv.validate_registry('action', ACTION_REGISTRY) validate_action_list = cv.validate_registry("action", ACTION_REGISTRY)
validate_condition = cv.validate_registry_entry('condition', CONDITION_REGISTRY) validate_condition = cv.validate_registry_entry("condition", CONDITION_REGISTRY)
validate_condition_list = cv.validate_registry('condition', CONDITION_REGISTRY) validate_condition_list = cv.validate_registry("condition", CONDITION_REGISTRY)
def validate_potentially_and_condition(value): def validate_potentially_and_condition(value):
if isinstance(value, list): if isinstance(value, list):
with cv.remove_prepend_path(['and']): with cv.remove_prepend_path(["and"]):
return validate_condition({ return validate_condition({"and": value})
'and': value
})
return validate_condition(value) return validate_condition(value)
DelayAction = cg.esphome_ns.class_('DelayAction', Action, cg.Component) DelayAction = cg.esphome_ns.class_("DelayAction", Action, cg.Component)
LambdaAction = cg.esphome_ns.class_('LambdaAction', Action) LambdaAction = cg.esphome_ns.class_("LambdaAction", Action)
IfAction = cg.esphome_ns.class_('IfAction', Action) IfAction = cg.esphome_ns.class_("IfAction", Action)
WhileAction = cg.esphome_ns.class_('WhileAction', Action) WhileAction = cg.esphome_ns.class_("WhileAction", Action)
WaitUntilAction = cg.esphome_ns.class_('WaitUntilAction', Action, cg.Component) WaitUntilAction = cg.esphome_ns.class_("WaitUntilAction", Action, cg.Component)
UpdateComponentAction = cg.esphome_ns.class_('UpdateComponentAction', Action) UpdateComponentAction = cg.esphome_ns.class_("UpdateComponentAction", Action)
Automation = cg.esphome_ns.class_('Automation') Automation = cg.esphome_ns.class_("Automation")
LambdaCondition = cg.esphome_ns.class_('LambdaCondition', Condition) LambdaCondition = cg.esphome_ns.class_("LambdaCondition", Condition)
ForCondition = cg.esphome_ns.class_('ForCondition', Condition, cg.Component) ForCondition = cg.esphome_ns.class_("ForCondition", Condition, cg.Component)
def validate_automation(extra_schema=None, extra_validators=None, single=False): def validate_automation(extra_schema=None, extra_validators=None, single=False):
@ -83,10 +89,10 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False):
try: try:
return cv.Schema([schema])(value) return cv.Schema([schema])(value)
except cv.Invalid as err2: except cv.Invalid as err2:
if 'extra keys not allowed' in str(err2) and len(err2.path) == 2: if "extra keys not allowed" in str(err2) and len(err2.path) == 2:
# pylint: disable=raise-missing-from # pylint: disable=raise-missing-from
raise err raise err
if 'Unable to find action' in str(err): if "Unable to find action" in str(err):
raise err2 raise err2
raise cv.MultipleInvalid([err, err2]) raise cv.MultipleInvalid([err, err2])
elif isinstance(value, dict): elif isinstance(value, dict):
@ -110,47 +116,59 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False):
return validator return validator
AUTOMATION_SCHEMA = cv.Schema({ AUTOMATION_SCHEMA = cv.Schema(
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Trigger), {
cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_id(Automation), cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Trigger),
cv.Required(CONF_THEN): validate_action_list, cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_id(Automation),
}) cv.Required(CONF_THEN): validate_action_list,
}
)
AndCondition = cg.esphome_ns.class_('AndCondition', Condition) AndCondition = cg.esphome_ns.class_("AndCondition", Condition)
OrCondition = cg.esphome_ns.class_('OrCondition', Condition) OrCondition = cg.esphome_ns.class_("OrCondition", Condition)
NotCondition = cg.esphome_ns.class_('NotCondition', Condition) NotCondition = cg.esphome_ns.class_("NotCondition", Condition)
@register_condition('and', AndCondition, validate_condition_list) @register_condition("and", AndCondition, validate_condition_list)
def and_condition_to_code(config, condition_id, template_arg, args): def and_condition_to_code(config, condition_id, template_arg, args):
conditions = yield build_condition_list(config, template_arg, args) conditions = yield build_condition_list(config, template_arg, args)
yield cg.new_Pvariable(condition_id, template_arg, conditions) yield cg.new_Pvariable(condition_id, template_arg, conditions)
@register_condition('or', OrCondition, validate_condition_list) @register_condition("or", OrCondition, validate_condition_list)
def or_condition_to_code(config, condition_id, template_arg, args): def or_condition_to_code(config, condition_id, template_arg, args):
conditions = yield build_condition_list(config, template_arg, args) conditions = yield build_condition_list(config, template_arg, args)
yield cg.new_Pvariable(condition_id, template_arg, conditions) yield cg.new_Pvariable(condition_id, template_arg, conditions)
@register_condition('not', NotCondition, validate_potentially_and_condition) @register_condition("not", NotCondition, validate_potentially_and_condition)
def not_condition_to_code(config, condition_id, template_arg, args): def not_condition_to_code(config, condition_id, template_arg, args):
condition = yield build_condition(config, template_arg, args) condition = yield build_condition(config, template_arg, args)
yield cg.new_Pvariable(condition_id, template_arg, condition) yield cg.new_Pvariable(condition_id, template_arg, condition)
@register_condition('lambda', LambdaCondition, cv.lambda_) @register_condition("lambda", LambdaCondition, cv.lambda_)
def lambda_condition_to_code(config, condition_id, template_arg, args): def lambda_condition_to_code(config, condition_id, template_arg, args):
lambda_ = yield cg.process_lambda(config, args, return_type=bool) lambda_ = yield cg.process_lambda(config, args, return_type=bool)
yield cg.new_Pvariable(condition_id, template_arg, lambda_) yield cg.new_Pvariable(condition_id, template_arg, lambda_)
@register_condition('for', ForCondition, cv.Schema({ @register_condition(
cv.Required(CONF_TIME): cv.templatable(cv.positive_time_period_milliseconds), "for",
cv.Required(CONF_CONDITION): validate_potentially_and_condition, ForCondition,
}).extend(cv.COMPONENT_SCHEMA)) cv.Schema(
{
cv.Required(CONF_TIME): cv.templatable(
cv.positive_time_period_milliseconds
),
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
}
).extend(cv.COMPONENT_SCHEMA),
)
def for_condition_to_code(config, condition_id, template_arg, args): def for_condition_to_code(config, condition_id, template_arg, args):
condition = yield build_condition(config[CONF_CONDITION], cg.TemplateArguments(), []) condition = yield build_condition(
config[CONF_CONDITION], cg.TemplateArguments(), []
)
var = cg.new_Pvariable(condition_id, template_arg, condition) var = cg.new_Pvariable(condition_id, template_arg, condition)
yield cg.register_component(var, config) yield cg.register_component(var, config)
templ = yield cg.templatable(config[CONF_TIME], args, cg.uint32) templ = yield cg.templatable(config[CONF_TIME], args, cg.uint32)
@ -158,7 +176,9 @@ def for_condition_to_code(config, condition_id, template_arg, args):
yield var yield var
@register_action('delay', DelayAction, cv.templatable(cv.positive_time_period_milliseconds)) @register_action(
"delay", DelayAction, cv.templatable(cv.positive_time_period_milliseconds)
)
def delay_action_to_code(config, action_id, template_arg, args): def delay_action_to_code(config, action_id, template_arg, args):
var = cg.new_Pvariable(action_id, template_arg) var = cg.new_Pvariable(action_id, template_arg)
yield cg.register_component(var, {}) yield cg.register_component(var, {})
@ -167,11 +187,18 @@ def delay_action_to_code(config, action_id, template_arg, args):
yield var yield var
@register_action('if', IfAction, cv.All({ @register_action(
cv.Required(CONF_CONDITION): validate_potentially_and_condition, "if",
cv.Optional(CONF_THEN): validate_action_list, IfAction,
cv.Optional(CONF_ELSE): validate_action_list, cv.All(
}, cv.has_at_least_one_key(CONF_THEN, CONF_ELSE))) {
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
cv.Optional(CONF_THEN): validate_action_list,
cv.Optional(CONF_ELSE): validate_action_list,
},
cv.has_at_least_one_key(CONF_THEN, CONF_ELSE),
),
)
def if_action_to_code(config, action_id, template_arg, args): def if_action_to_code(config, action_id, template_arg, args):
conditions = yield build_condition(config[CONF_CONDITION], template_arg, args) conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
var = cg.new_Pvariable(action_id, template_arg, conditions) var = cg.new_Pvariable(action_id, template_arg, conditions)
@ -184,10 +211,16 @@ def if_action_to_code(config, action_id, template_arg, args):
yield var yield var
@register_action('while', WhileAction, cv.Schema({ @register_action(
cv.Required(CONF_CONDITION): validate_potentially_and_condition, "while",
cv.Required(CONF_THEN): validate_action_list, WhileAction,
})) cv.Schema(
{
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
cv.Required(CONF_THEN): validate_action_list,
}
),
)
def while_action_to_code(config, action_id, template_arg, args): def while_action_to_code(config, action_id, template_arg, args):
conditions = yield build_condition(config[CONF_CONDITION], template_arg, args) conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
var = cg.new_Pvariable(action_id, template_arg, conditions) var = cg.new_Pvariable(action_id, template_arg, conditions)
@ -197,15 +230,17 @@ def while_action_to_code(config, action_id, template_arg, args):
def validate_wait_until(value): def validate_wait_until(value):
schema = cv.Schema({ schema = cv.Schema(
cv.Required(CONF_CONDITION): validate_potentially_and_condition, {
}) cv.Required(CONF_CONDITION): validate_potentially_and_condition,
}
)
if isinstance(value, dict) and CONF_CONDITION in value: if isinstance(value, dict) and CONF_CONDITION in value:
return schema(value) return schema(value)
return validate_wait_until({CONF_CONDITION: value}) return validate_wait_until({CONF_CONDITION: value})
@register_action('wait_until', WaitUntilAction, validate_wait_until) @register_action("wait_until", WaitUntilAction, validate_wait_until)
def wait_until_action_to_code(config, action_id, template_arg, args): def wait_until_action_to_code(config, action_id, template_arg, args):
conditions = yield build_condition(config[CONF_CONDITION], template_arg, args) conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
var = cg.new_Pvariable(action_id, template_arg, conditions) var = cg.new_Pvariable(action_id, template_arg, conditions)
@ -213,15 +248,21 @@ def wait_until_action_to_code(config, action_id, template_arg, args):
yield var yield var
@register_action('lambda', LambdaAction, cv.lambda_) @register_action("lambda", LambdaAction, cv.lambda_)
def lambda_action_to_code(config, action_id, template_arg, args): def lambda_action_to_code(config, action_id, template_arg, args):
lambda_ = yield cg.process_lambda(config, args, return_type=cg.void) lambda_ = yield cg.process_lambda(config, args, return_type=cg.void)
yield cg.new_Pvariable(action_id, template_arg, lambda_) yield cg.new_Pvariable(action_id, template_arg, lambda_)
@register_action('component.update', UpdateComponentAction, maybe_simple_id({ @register_action(
cv.Required(CONF_ID): cv.use_id(cg.PollingComponent), "component.update",
})) UpdateComponentAction,
maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(cg.PollingComponent),
}
),
)
def component_update_action_to_code(config, action_id, template_arg, args): def component_update_action_to_code(config, action_id, template_arg, args):
comp = yield cg.get_variable(config[CONF_ID]) comp = yield cg.get_variable(config[CONF_ID])
yield cg.new_Pvariable(action_id, template_arg, comp) yield cg.new_Pvariable(action_id, template_arg, comp)
@ -229,7 +270,9 @@ def component_update_action_to_code(config, action_id, template_arg, args):
@coroutine @coroutine
def build_action(full_config, template_arg, args): def build_action(full_config, template_arg, args):
registry_entry, config = cg.extract_registry_entry_config(ACTION_REGISTRY, full_config) registry_entry, config = cg.extract_registry_entry_config(
ACTION_REGISTRY, full_config
)
action_id = full_config[CONF_TYPE_ID] action_id = full_config[CONF_TYPE_ID]
builder = registry_entry.coroutine_fun builder = registry_entry.coroutine_fun
yield builder(config, action_id, template_arg, args) yield builder(config, action_id, template_arg, args)
@ -246,7 +289,9 @@ def build_action_list(config, templ, arg_type):
@coroutine @coroutine
def build_condition(full_config, template_arg, args): def build_condition(full_config, template_arg, args):
registry_entry, config = cg.extract_registry_entry_config(CONDITION_REGISTRY, full_config) registry_entry, config = cg.extract_registry_entry_config(
CONDITION_REGISTRY, full_config
)
action_id = full_config[CONF_TYPE_ID] action_id = full_config[CONF_TYPE_ID]
builder = registry_entry.coroutine_fun builder = registry_entry.coroutine_fun
yield builder(config, action_id, template_arg, args) yield builder(config, action_id, template_arg, args)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -14,14 +14,14 @@ CONF_WHITE_INT = "white_int"
CONFIG_SCHEMA = cv.Schema( CONFIG_SCHEMA = cv.Schema(
{ {
cv.Required(CONF_ID): cv.declare_id(ColorStruct), cv.Required(CONF_ID): cv.declare_id(ColorStruct),
cv.Exclusive(CONF_RED, 'red'): cv.percentage, cv.Exclusive(CONF_RED, "red"): cv.percentage,
cv.Exclusive(CONF_RED_INT, 'red'): cv.uint8_t, cv.Exclusive(CONF_RED_INT, "red"): cv.uint8_t,
cv.Exclusive(CONF_GREEN, 'green'): cv.percentage, cv.Exclusive(CONF_GREEN, "green"): cv.percentage,
cv.Exclusive(CONF_GREEN_INT, 'green'): cv.uint8_t, cv.Exclusive(CONF_GREEN_INT, "green"): cv.uint8_t,
cv.Exclusive(CONF_BLUE, 'blue'): cv.percentage, cv.Exclusive(CONF_BLUE, "blue"): cv.percentage,
cv.Exclusive(CONF_BLUE_INT, 'blue'): cv.uint8_t, cv.Exclusive(CONF_BLUE_INT, "blue"): cv.uint8_t,
cv.Exclusive(CONF_WHITE, 'white'): cv.percentage, cv.Exclusive(CONF_WHITE, "white"): cv.percentage,
cv.Exclusive(CONF_WHITE_INT, 'white'): cv.uint8_t, cv.Exclusive(CONF_WHITE_INT, "white"): cv.uint8_t,
} }
).extend(cv.COMPONENT_SCHEMA) ).extend(cv.COMPONENT_SCHEMA)
@ -29,31 +29,29 @@ CONFIG_SCHEMA = cv.Schema(
def to_code(config): def to_code(config):
r = 0 r = 0
if CONF_RED in config: if CONF_RED in config:
r = int(config[CONF_RED]*255) r = int(config[CONF_RED] * 255)
elif CONF_RED_INT in config: elif CONF_RED_INT in config:
r = config[CONF_RED_INT] r = config[CONF_RED_INT]
g = 0 g = 0
if CONF_GREEN in config: if CONF_GREEN in config:
g = int(config[CONF_GREEN]*255) g = int(config[CONF_GREEN] * 255)
elif CONF_GREEN_INT in config: elif CONF_GREEN_INT in config:
g = config[CONF_GREEN_INT] g = config[CONF_GREEN_INT]
b = 0 b = 0
if CONF_BLUE in config: if CONF_BLUE in config:
b = int(config[CONF_BLUE]*255) b = int(config[CONF_BLUE] * 255)
elif CONF_BLUE_INT in config: elif CONF_BLUE_INT in config:
b = config[CONF_BLUE_INT] b = config[CONF_BLUE_INT]
w = 0 w = 0
if CONF_WHITE in config: if CONF_WHITE in config:
w = int(config[CONF_WHITE]*255) w = int(config[CONF_WHITE] * 255)
elif CONF_WHITE_INT in config: elif CONF_WHITE_INT in config:
w = config[CONF_WHITE_INT] w = config[CONF_WHITE_INT]
cg.variable(config[CONF_ID], cg.StructInitializer( cg.variable(
ColorStruct, config[CONF_ID],
('r', r), cg.StructInitializer(ColorStruct, ("r", r), ("g", g), ("b", b), ("w", w)),
('g', g), )
('b', b),
('w', w)))

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,16 +2,31 @@ import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import pins from esphome import pins
from esphome.components import sensor from esphome.components import sensor
from esphome.const import CONF_ID, CONF_PIN, DEVICE_CLASS_EMPTY, 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') duty_cycle_ns = cg.esphome_ns.namespace("duty_cycle")
DutyCycleSensor = duty_cycle_ns.class_('DutyCycleSensor', sensor.Sensor, cg.PollingComponent) DutyCycleSensor = duty_cycle_ns.class_(
"DutyCycleSensor", sensor.Sensor, cg.PollingComponent
)
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PERCENT, ICON_PERCENT, 1, DEVICE_CLASS_EMPTY).extend({ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(DutyCycleSensor), sensor.sensor_schema(UNIT_PERCENT, ICON_PERCENT, 1, DEVICE_CLASS_EMPTY)
cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema, .extend(
pins.validate_has_interrupt), {
}).extend(cv.polling_component_schema('60s')) 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): 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.components.light.effects import register_addressable_effect
from esphome.const import CONF_ID, CONF_NAME, CONF_METHOD, CONF_CHANNELS from esphome.const import CONF_ID, CONF_NAME, CONF_METHOD, CONF_CHANNELS
e131_ns = cg.esphome_ns.namespace('e131') e131_ns = cg.esphome_ns.namespace("e131")
E131AddressableLightEffect = e131_ns.class_('E131AddressableLightEffect', AddressableLightEffect) E131AddressableLightEffect = e131_ns.class_(
E131Component = e131_ns.class_('E131Component', cg.Component) "E131AddressableLightEffect", AddressableLightEffect
)
E131Component = e131_ns.class_("E131Component", cg.Component)
METHODS = { METHODS = {"UNICAST": e131_ns.E131_UNICAST, "MULTICAST": e131_ns.E131_MULTICAST}
'UNICAST': e131_ns.E131_UNICAST,
'MULTICAST': e131_ns.E131_MULTICAST
}
CHANNELS = { CHANNELS = {
'MONO': e131_ns.E131_MONO, "MONO": e131_ns.E131_MONO,
'RGB': e131_ns.E131_RGB, "RGB": e131_ns.E131_RGB,
'RGBW': e131_ns.E131_RGBW "RGBW": e131_ns.E131_RGBW,
} }
CONF_UNIVERSE = 'universe' CONF_UNIVERSE = "universe"
CONF_E131_ID = 'e131_id' CONF_E131_ID = "e131_id"
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(E131Component), {
cv.Optional(CONF_METHOD, default='MULTICAST'): cv.one_of(*METHODS, upper=True), cv.GenerateID(): cv.declare_id(E131Component),
}) cv.Optional(CONF_METHOD, default="MULTICAST"): cv.one_of(*METHODS, upper=True),
}
)
def to_code(config): def to_code(config):
@ -34,11 +35,16 @@ def to_code(config):
cg.add(var.set_method(METHODS[config[CONF_METHOD]])) cg.add(var.set_method(METHODS[config[CONF_METHOD]]))
@register_addressable_effect('e131', E131AddressableLightEffect, "E1.31", { @register_addressable_effect(
cv.GenerateID(CONF_E131_ID): cv.use_id(E131Component), "e131",
cv.Required(CONF_UNIVERSE): cv.int_range(min=1, max=512), E131AddressableLightEffect,
cv.Optional(CONF_CHANNELS, default='RGB'): cv.one_of(*CHANNELS, upper=True) "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): def e131_light_effect_to_code(config, effect_id):
parent = yield cg.get_variable(config[CONF_E131_ID]) parent = yield cg.get_variable(config[CONF_E131_ID])

View File

@ -2,27 +2,34 @@ import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome import automation from esphome import automation
from esphome.components import binary_sensor, cover from esphome.components import binary_sensor, cover
from esphome.const import CONF_CLOSE_ACTION, CONF_CLOSE_DURATION, \ from esphome.const import (
CONF_CLOSE_ENDSTOP, CONF_ID, CONF_OPEN_ACTION, CONF_OPEN_DURATION, \ CONF_CLOSE_ACTION,
CONF_OPEN_ENDSTOP, CONF_STOP_ACTION, CONF_MAX_DURATION 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') endstop_ns = cg.esphome_ns.namespace("endstop")
EndstopCover = endstop_ns.class_('EndstopCover', cover.Cover, cg.Component) EndstopCover = endstop_ns.class_("EndstopCover", cover.Cover, cg.Component)
CONFIG_SCHEMA = cover.COVER_SCHEMA.extend({ CONFIG_SCHEMA = cover.COVER_SCHEMA.extend(
cv.GenerateID(): cv.declare_id(EndstopCover), {
cv.Required(CONF_STOP_ACTION): automation.validate_automation(single=True), 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_ENDSTOP): cv.use_id(binary_sensor.BinarySensor),
cv.Required(CONF_OPEN_ACTION): automation.validate_automation(single=True), cv.Required(CONF_OPEN_ACTION): automation.validate_automation(single=True),
cv.Required(CONF_OPEN_DURATION): cv.positive_time_period_milliseconds, cv.Required(CONF_OPEN_DURATION): cv.positive_time_period_milliseconds,
cv.Required(CONF_CLOSE_ACTION): automation.validate_automation(single=True),
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_ENDSTOP): cv.use_id(binary_sensor.BinarySensor), cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds,
cv.Required(CONF_CLOSE_DURATION): cv.positive_time_period_milliseconds, cv.Optional(CONF_MAX_DURATION): cv.positive_time_period_milliseconds,
}
cv.Optional(CONF_MAX_DURATION): cv.positive_time_period_milliseconds, ).extend(cv.COMPONENT_SCHEMA)
}).extend(cv.COMPONENT_SCHEMA)
def to_code(config): def to_code(config):
@ -30,17 +37,23 @@ def to_code(config):
yield cg.register_component(var, config) yield cg.register_component(var, config)
yield cover.register_cover(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]) bin = yield cg.get_variable(config[CONF_OPEN_ENDSTOP])
cg.add(var.set_open_endstop(bin)) cg.add(var.set_open_endstop(bin))
cg.add(var.set_open_duration(config[CONF_OPEN_DURATION])) 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]) bin = yield cg.get_variable(config[CONF_CLOSE_ENDSTOP])
cg.add(var.set_close_endstop(bin)) cg.add(var.set_close_endstop(bin))
cg.add(var.set_close_duration(config[CONF_CLOSE_DURATION])) 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: if CONF_MAX_DURATION in config:
cg.add(var.set_max_duration(config[CONF_MAX_DURATION])) 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 from esphome.const import CONF_ID, CONF_TYPE, CONF_UUID, ESP_PLATFORM_ESP32
ESP_PLATFORMS = [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') esp32_ble_beacon_ns = cg.esphome_ns.namespace("esp32_ble_beacon")
ESP32BLEBeacon = esp32_ble_beacon_ns.class_('ESP32BLEBeacon', cg.Component) ESP32BLEBeacon = esp32_ble_beacon_ns.class_("ESP32BLEBeacon", cg.Component)
CONF_MAJOR = 'major' CONF_MAJOR = "major"
CONF_MINOR = 'minor' CONF_MINOR = "minor"
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(ESP32BLEBeacon), {
cv.Required(CONF_TYPE): cv.one_of('IBEACON', upper=True), cv.GenerateID(): cv.declare_id(ESP32BLEBeacon),
cv.Required(CONF_UUID): cv.uuid, cv.Required(CONF_TYPE): cv.one_of("IBEACON", upper=True),
cv.Optional(CONF_MAJOR, default=10167): cv.uint16_t, cv.Required(CONF_UUID): cv.uuid,
cv.Optional(CONF_MINOR, default=61958): cv.uint16_t, cv.Optional(CONF_MAJOR, default=10167): cv.uint16_t,
}).extend(cv.COMPONENT_SCHEMA) cv.Optional(CONF_MINOR, default=61958): cv.uint16_t,
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config): def to_code(config):
uuid = config[CONF_UUID].hex 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) var = cg.new_Pvariable(config[CONF_ID], uuid_arr)
yield cg.register_component(var, config) yield cg.register_component(var, config)
cg.add(var.set_major(config[CONF_MAJOR])) cg.add(var.set_major(config[CONF_MAJOR]))

View File

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

View File

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

View File

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

View File

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

View File

@ -1,15 +1,23 @@
import esphome.codegen as cg import esphome.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import CONF_HIGH_VOLTAGE_REFERENCE, CONF_ID, CONF_IIR_FILTER, \ from esphome.const import (
CONF_LOW_VOLTAGE_REFERENCE, CONF_MEASUREMENT_DURATION, CONF_SETUP_MODE, CONF_SLEEP_DURATION, \ CONF_HIGH_VOLTAGE_REFERENCE,
CONF_VOLTAGE_ATTENUATION, ESP_PLATFORM_ESP32 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 from esphome.core import TimePeriod
AUTO_LOAD = ['binary_sensor'] AUTO_LOAD = ["binary_sensor"]
ESP_PLATFORMS = [ESP_PLATFORM_ESP32] ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
esp32_touch_ns = cg.esphome_ns.namespace('esp32_touch') esp32_touch_ns = cg.esphome_ns.namespace("esp32_touch")
ESP32TouchComponent = esp32_touch_ns.class_('ESP32TouchComponent', cg.Component) ESP32TouchComponent = esp32_touch_ns.class_("ESP32TouchComponent", cg.Component)
def validate_voltage(values): def validate_voltage(values):
@ -17,46 +25,56 @@ def validate_voltage(values):
if isinstance(value, float) and value.is_integer(): if isinstance(value, float) and value.is_integer():
value = int(value) value = int(value)
value = cv.string(value) value = cv.string(value)
if not value.endswith('V'): if not value.endswith("V"):
value += 'V' value += "V"
return cv.one_of(*values)(value) return cv.one_of(*values)(value)
return validator return validator
LOW_VOLTAGE_REFERENCE = { LOW_VOLTAGE_REFERENCE = {
'0.5V': cg.global_ns.TOUCH_LVOLT_0V5, "0.5V": cg.global_ns.TOUCH_LVOLT_0V5,
'0.6V': cg.global_ns.TOUCH_LVOLT_0V6, "0.6V": cg.global_ns.TOUCH_LVOLT_0V6,
'0.7V': cg.global_ns.TOUCH_LVOLT_0V7, "0.7V": cg.global_ns.TOUCH_LVOLT_0V7,
'0.8V': cg.global_ns.TOUCH_LVOLT_0V8, "0.8V": cg.global_ns.TOUCH_LVOLT_0V8,
} }
HIGH_VOLTAGE_REFERENCE = { HIGH_VOLTAGE_REFERENCE = {
'2.4V': cg.global_ns.TOUCH_HVOLT_2V4, "2.4V": cg.global_ns.TOUCH_HVOLT_2V4,
'2.5V': cg.global_ns.TOUCH_HVOLT_2V5, "2.5V": cg.global_ns.TOUCH_HVOLT_2V5,
'2.6V': cg.global_ns.TOUCH_HVOLT_2V6, "2.6V": cg.global_ns.TOUCH_HVOLT_2V6,
'2.7V': cg.global_ns.TOUCH_HVOLT_2V7, "2.7V": cg.global_ns.TOUCH_HVOLT_2V7,
} }
VOLTAGE_ATTENUATION = { VOLTAGE_ATTENUATION = {
'1.5V': cg.global_ns.TOUCH_HVOLT_ATTEN_1V5, "1.5V": cg.global_ns.TOUCH_HVOLT_ATTEN_1V5,
'1V': cg.global_ns.TOUCH_HVOLT_ATTEN_1V, "1V": cg.global_ns.TOUCH_HVOLT_ATTEN_1V,
'0.5V': cg.global_ns.TOUCH_HVOLT_ATTEN_0V5, "0.5V": cg.global_ns.TOUCH_HVOLT_ATTEN_0V5,
'0V': cg.global_ns.TOUCH_HVOLT_ATTEN_0V, "0V": cg.global_ns.TOUCH_HVOLT_ATTEN_0V,
} }
CONFIG_SCHEMA = cv.Schema({ CONFIG_SCHEMA = cv.Schema(
cv.GenerateID(): cv.declare_id(ESP32TouchComponent), {
cv.Optional(CONF_SETUP_MODE, default=False): cv.boolean, cv.GenerateID(): cv.declare_id(ESP32TouchComponent),
cv.Optional(CONF_IIR_FILTER, default='0ms'): cv.positive_time_period_milliseconds, cv.Optional(CONF_SETUP_MODE, default=False): cv.boolean,
cv.Optional(CONF_SLEEP_DURATION, default='27306us'): cv.Optional(
cv.All(cv.positive_time_period, cv.Range(max=TimePeriod(microseconds=436906))), CONF_IIR_FILTER, default="0ms"
cv.Optional(CONF_MEASUREMENT_DURATION, default='8192us'): ): cv.positive_time_period_milliseconds,
cv.All(cv.positive_time_period, cv.Range(max=TimePeriod(microseconds=8192))), cv.Optional(CONF_SLEEP_DURATION, default="27306us"): cv.All(
cv.Optional(CONF_LOW_VOLTAGE_REFERENCE, default='0.5V'): cv.positive_time_period, cv.Range(max=TimePeriod(microseconds=436906))
validate_voltage(LOW_VOLTAGE_REFERENCE), ),
cv.Optional(CONF_HIGH_VOLTAGE_REFERENCE, default='2.7V'): cv.Optional(CONF_MEASUREMENT_DURATION, default="8192us"): cv.All(
validate_voltage(HIGH_VOLTAGE_REFERENCE), cv.positive_time_period, cv.Range(max=TimePeriod(microseconds=8192))
cv.Optional(CONF_VOLTAGE_ATTENUATION, default='0V'): validate_voltage(VOLTAGE_ATTENUATION), ),
}).extend(cv.COMPONENT_SCHEMA) 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): def to_code(config):
@ -69,12 +87,23 @@ def to_code(config):
sleep_duration = int(round(config[CONF_SLEEP_DURATION].total_microseconds * 0.15)) sleep_duration = int(round(config[CONF_SLEEP_DURATION].total_microseconds * 0.15))
cg.add(touch.set_sleep_duration(sleep_duration)) cg.add(touch.set_sleep_duration(sleep_duration))
measurement_duration = int(round(config[CONF_MEASUREMENT_DURATION].total_microseconds * measurement_duration = int(
7.99987793)) round(config[CONF_MEASUREMENT_DURATION].total_microseconds * 7.99987793)
)
cg.add(touch.set_measurement_duration(measurement_duration)) cg.add(touch.set_measurement_duration(measurement_duration))
cg.add(touch.set_low_voltage_reference( cg.add(
LOW_VOLTAGE_REFERENCE[config[CONF_LOW_VOLTAGE_REFERENCE]])) touch.set_low_voltage_reference(
cg.add(touch.set_high_voltage_reference( LOW_VOLTAGE_REFERENCE[config[CONF_LOW_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_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.codegen as cg
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.components import binary_sensor 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 esphome.pins import validate_gpio_pin
from . import esp32_touch_ns, ESP32TouchComponent from . import esp32_touch_ns, ESP32TouchComponent
ESP_PLATFORMS = [ESP_PLATFORM_ESP32] 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 = { TOUCH_PADS = {
4: cg.global_ns.TOUCH_PAD_NUM0, 4: cg.global_ns.TOUCH_PAD_NUM0,
@ -31,19 +37,27 @@ def validate_touch_pad(value):
return 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({ 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.GenerateID(): cv.declare_id(ESP32TouchBinarySensor),
cv.Required(CONF_PIN): validate_touch_pad, cv.GenerateID(CONF_ESP32_TOUCH_ID): cv.use_id(ESP32TouchComponent),
cv.Required(CONF_THRESHOLD): cv.uint16_t, cv.Required(CONF_PIN): validate_touch_pad,
}) cv.Required(CONF_THRESHOLD): cv.uint16_t,
}
)
def to_code(config): def to_code(config):
hub = yield cg.get_variable(config[CONF_ESP32_TOUCH_ID]) 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]], var = cg.new_Pvariable(
config[CONF_THRESHOLD]) config[CONF_ID],
config[CONF_NAME],
TOUCH_PADS[config[CONF_PIN]],
config[CONF_THRESHOLD],
)
yield binary_sensor.register_binary_sensor(var, config) yield binary_sensor.register_binary_sensor(var, config)
cg.add(hub.register_touch_pad(var)) cg.add(hub.register_touch_pad(var))

View File

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

View File

@ -1,47 +1,60 @@
from esphome import pins from esphome import pins
import esphome.config_validation as cv import esphome.config_validation as cv
import esphome.codegen as cg import esphome.codegen as cg
from esphome.const import CONF_DOMAIN, CONF_ID, CONF_MANUAL_IP, CONF_STATIC_IP, CONF_TYPE, \ from esphome.const import (
CONF_USE_ADDRESS, ESP_PLATFORM_ESP32, CONF_GATEWAY, CONF_SUBNET, CONF_DNS1, CONF_DNS2 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 from esphome.core import CORE, coroutine_with_priority
CONFLICTS_WITH = ['wifi'] CONFLICTS_WITH = ["wifi"]
ESP_PLATFORMS = [ESP_PLATFORM_ESP32] ESP_PLATFORMS = [ESP_PLATFORM_ESP32]
AUTO_LOAD = ['network'] AUTO_LOAD = ["network"]
ethernet_ns = cg.esphome_ns.namespace('ethernet') ethernet_ns = cg.esphome_ns.namespace("ethernet")
CONF_PHY_ADDR = 'phy_addr' CONF_PHY_ADDR = "phy_addr"
CONF_MDC_PIN = 'mdc_pin' CONF_MDC_PIN = "mdc_pin"
CONF_MDIO_PIN = 'mdio_pin' CONF_MDIO_PIN = "mdio_pin"
CONF_CLK_MODE = 'clk_mode' CONF_CLK_MODE = "clk_mode"
CONF_POWER_PIN = 'power_pin' CONF_POWER_PIN = "power_pin"
EthernetType = ethernet_ns.enum('EthernetType') EthernetType = ethernet_ns.enum("EthernetType")
ETHERNET_TYPES = { ETHERNET_TYPES = {
'LAN8720': EthernetType.ETHERNET_TYPE_LAN8720, "LAN8720": EthernetType.ETHERNET_TYPE_LAN8720,
'TLK110': EthernetType.ETHERNET_TYPE_TLK110, "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 = { CLK_MODES = {
'GPIO0_IN': eth_clock_mode_t.ETH_CLOCK_GPIO0_IN, "GPIO0_IN": eth_clock_mode_t.ETH_CLOCK_GPIO0_IN,
'GPIO0_OUT': eth_clock_mode_t.ETH_CLOCK_GPIO0_OUT, "GPIO0_OUT": eth_clock_mode_t.ETH_CLOCK_GPIO0_OUT,
'GPIO16_OUT': eth_clock_mode_t.ETH_CLOCK_GPIO16_OUT, "GPIO16_OUT": eth_clock_mode_t.ETH_CLOCK_GPIO16_OUT,
'GPIO17_OUT': eth_clock_mode_t.ETH_CLOCK_GPIO17_OUT, "GPIO17_OUT": eth_clock_mode_t.ETH_CLOCK_GPIO17_OUT,
} }
MANUAL_IP_SCHEMA = cv.Schema({ MANUAL_IP_SCHEMA = cv.Schema(
cv.Required(CONF_STATIC_IP): cv.ipv4, {
cv.Required(CONF_GATEWAY): cv.ipv4, cv.Required(CONF_STATIC_IP): cv.ipv4,
cv.Required(CONF_SUBNET): cv.ipv4, cv.Required(CONF_GATEWAY): cv.ipv4,
cv.Optional(CONF_DNS1, default="0.0.0.0"): cv.ipv4, cv.Required(CONF_SUBNET): cv.ipv4,
cv.Optional(CONF_DNS2, default="0.0.0.0"): 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) EthernetComponent = ethernet_ns.class_("EthernetComponent", cg.Component)
IPAddress = cg.global_ns.class_('IPAddress') IPAddress = cg.global_ns.class_("IPAddress")
ManualIP = ethernet_ns.struct('ManualIP') ManualIP = ethernet_ns.struct("ManualIP")
def validate(config): def validate(config):
@ -54,30 +67,38 @@ def validate(config):
return config return config
CONFIG_SCHEMA = cv.All(cv.Schema({ CONFIG_SCHEMA = cv.All(
cv.GenerateID(): cv.declare_id(EthernetComponent), cv.Schema(
cv.Required(CONF_TYPE): cv.enum(ETHERNET_TYPES, upper=True), {
cv.Required(CONF_MDC_PIN): pins.output_pin, cv.GenerateID(): cv.declare_id(EthernetComponent),
cv.Required(CONF_MDIO_PIN): pins.input_output_pin, cv.Required(CONF_TYPE): cv.enum(ETHERNET_TYPES, upper=True),
cv.Optional(CONF_CLK_MODE, default='GPIO0_IN'): cv.enum(CLK_MODES, upper=True, space='_'), cv.Required(CONF_MDC_PIN): pins.output_pin,
cv.Optional(CONF_PHY_ADDR, default=0): cv.int_range(min=0, max=31), cv.Required(CONF_MDIO_PIN): pins.input_output_pin,
cv.Optional(CONF_POWER_PIN): pins.gpio_output_pin_schema, cv.Optional(CONF_CLK_MODE, default="GPIO0_IN"): cv.enum(
cv.Optional(CONF_MANUAL_IP): MANUAL_IP_SCHEMA, CLK_MODES, upper=True, space="_"
cv.Optional(CONF_DOMAIN, default='.local'): cv.domain_name, ),
cv.Optional(CONF_USE_ADDRESS): cv.string_strict, 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('hostname'): cv.invalid("The hostname option has been removed in 1.11.0"), cv.Optional(CONF_MANUAL_IP): MANUAL_IP_SCHEMA,
}).extend(cv.COMPONENT_SCHEMA), validate) 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): def manual_ip(config):
return cg.StructInitializer( return cg.StructInitializer(
ManualIP, ManualIP,
('static_ip', IPAddress(*config[CONF_STATIC_IP].args)), ("static_ip", IPAddress(*config[CONF_STATIC_IP].args)),
('gateway', IPAddress(*config[CONF_GATEWAY].args)), ("gateway", IPAddress(*config[CONF_GATEWAY].args)),
('subnet', IPAddress(*config[CONF_SUBNET].args)), ("subnet", IPAddress(*config[CONF_SUBNET].args)),
('dns1', IPAddress(*config[CONF_DNS1].args)), ("dns1", IPAddress(*config[CONF_DNS1].args)),
('dns2', IPAddress(*config[CONF_DNS2].args)), ("dns2", IPAddress(*config[CONF_DNS2].args)),
) )
@ -100,4 +121,4 @@ def to_code(config):
if CONF_MANUAL_IP in config: if CONF_MANUAL_IP in config:
cg.add(var.set_manual_ip(manual_ip(config[CONF_MANUAL_IP]))) 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.components import esp32_ble_tracker
from esphome.const import CONF_TRIGGER_ID from esphome.const import CONF_TRIGGER_ID
CODEOWNERS = ['@OttoWinter'] CODEOWNERS = ["@OttoWinter"]
DEPENDENCIES = ['esp32_ble_tracker'] DEPENDENCIES = ["esp32_ble_tracker"]
exposure_notifications_ns = cg.esphome_ns.namespace('exposure_notifications') exposure_notifications_ns = cg.esphome_ns.namespace("exposure_notifications")
ExposureNotification = exposure_notifications_ns.struct('ExposureNotification') ExposureNotification = exposure_notifications_ns.struct("ExposureNotification")
ExposureNotificationTrigger = exposure_notifications_ns.class_( ExposureNotificationTrigger = exposure_notifications_ns.class_(
'ExposureNotificationTrigger', esp32_ble_tracker.ESPBTDeviceListener, "ExposureNotificationTrigger",
automation.Trigger.template(ExposureNotification)) 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({ CONFIG_SCHEMA = cv.Schema(
cv.Required(CONF_ON_EXPOSURE_NOTIFICATION): automation.validate_automation(cv.Schema({ {
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ExposureNotificationTrigger), cv.Required(CONF_ON_EXPOSURE_NOTIFICATION): automation.validate_automation(
}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)), cv.Schema(
}) {
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
ExposureNotificationTrigger
),
}
).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
),
}
)
def to_code(config): def to_code(config):
for conf in config.get(CONF_ON_EXPOSURE_NOTIFICATION, []): for conf in config.get(CONF_ON_EXPOSURE_NOTIFICATION, []):
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID]) 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) 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.components import i2c, sensor
from esphome.const import CONF_ID 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({ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(EZOSensor), sensor.SENSOR_SCHEMA.extend(
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(None)) {
cv.GenerateID(): cv.declare_id(EZOSensor),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(None))
)
def to_code(config): def to_code(config):

View File

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

View File

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

View File

@ -4,46 +4,51 @@ from esphome import pins
from esphome.components import fastled_base from esphome.components import fastled_base
from esphome.const import CONF_CHIPSET, CONF_NUM_LEDS, CONF_PIN, CONF_RGB_ORDER from esphome.const import CONF_CHIPSET, CONF_NUM_LEDS, CONF_PIN, CONF_RGB_ORDER
AUTO_LOAD = ['fastled_base'] AUTO_LOAD = ["fastled_base"]
CHIPSETS = [ CHIPSETS = [
'NEOPIXEL', "NEOPIXEL",
'TM1829', "TM1829",
'TM1809', "TM1809",
'TM1804', "TM1804",
'TM1803', "TM1803",
'UCS1903', "UCS1903",
'UCS1903B', "UCS1903B",
'UCS1904', "UCS1904",
'UCS2903', "UCS2903",
'WS2812', "WS2812",
'WS2852', "WS2852",
'WS2812B', "WS2812B",
'SK6812', "SK6812",
'SK6822', "SK6822",
'APA106', "APA106",
'PL9823', "PL9823",
'WS2811', "WS2811",
'WS2813', "WS2813",
'APA104', "APA104",
'WS2811_400', "WS2811_400",
'GW6205', "GW6205",
'GW6205_400', "GW6205_400",
'LPD1886', "LPD1886",
'LPD1886_8BIT', "LPD1886_8BIT",
] ]
def validate(value): 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") raise cv.Invalid("NEOPIXEL doesn't support RGB order")
return value return value
CONFIG_SCHEMA = cv.All(fastled_base.BASE_SCHEMA.extend({ CONFIG_SCHEMA = cv.All(
cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True), fastled_base.BASE_SCHEMA.extend(
cv.Required(CONF_PIN): pins.output_pin, {
}), validate) cv.Required(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True),
cv.Required(CONF_PIN): pins.output_pin,
}
),
validate,
)
def to_code(config): def to_code(config):
@ -52,6 +57,7 @@ def to_code(config):
rgb_order = None rgb_order = None
if CONF_RGB_ORDER in config: if CONF_RGB_ORDER in config:
rgb_order = cg.RawExpression(config[CONF_RGB_ORDER]) rgb_order = cg.RawExpression(config[CONF_RGB_ORDER])
template_args = cg.TemplateArguments(cg.RawExpression(config[CONF_CHIPSET]), template_args = cg.TemplateArguments(
config[CONF_PIN], rgb_order) cg.RawExpression(config[CONF_CHIPSET]), config[CONF_PIN], rgb_order
)
cg.add(var.add_leds(template_args, config[CONF_NUM_LEDS])) 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 import esphome.config_validation as cv
from esphome import pins from esphome import pins
from esphome.components import fastled_base from esphome.components import fastled_base
from esphome.const import CONF_CHIPSET, CONF_CLOCK_PIN, CONF_DATA_PIN, CONF_DATA_RATE, \ from esphome.const import (
CONF_NUM_LEDS, CONF_RGB_ORDER 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 = [ CHIPSETS = [
'LPD8806', "LPD8806",
'WS2801', "WS2801",
'WS2803', "WS2803",
'SM16716', "SM16716",
'P9813', "P9813",
'APA102', "APA102",
'SK9822', "SK9822",
'DOTSTAR', "DOTSTAR",
] ]
CONFIG_SCHEMA = fastled_base.BASE_SCHEMA.extend({ 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_CHIPSET): cv.one_of(*CHIPSETS, upper=True),
cv.Required(CONF_CLOCK_PIN): pins.output_pin, cv.Required(CONF_DATA_PIN): pins.output_pin,
cv.Optional(CONF_DATA_RATE): cv.frequency, cv.Required(CONF_CLOCK_PIN): pins.output_pin,
}) cv.Optional(CONF_DATA_RATE): cv.frequency,
}
)
def to_code(config): def to_code(config):
var = yield fastled_base.new_fastled_light(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 data_rate = None
if CONF_DATA_RATE in config: if CONF_DATA_RATE in config:
@ -39,7 +49,11 @@ def to_code(config):
else: else:
data_rate_mhz = int(data_rate_khz / 1000) data_rate_mhz = int(data_rate_khz / 1000)
data_rate = cg.RawExpression(f"DATA_RATE_MHZ({data_rate_mhz})") data_rate = cg.RawExpression(f"DATA_RATE_MHZ({data_rate_mhz})")
template_args = cg.TemplateArguments(cg.RawExpression(config[CONF_CHIPSET]), template_args = cg.TemplateArguments(
config[CONF_DATA_PIN], config[CONF_CLOCK_PIN], rgb_order, cg.RawExpression(config[CONF_CHIPSET]),
data_rate) config[CONF_DATA_PIN],
config[CONF_CLOCK_PIN],
rgb_order,
data_rate,
)
cg.add(var.add_leds(template_args, config[CONF_NUM_LEDS])) 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.const import CONF_FILE, CONF_GLYPHS, CONF_ID, CONF_SIZE
from esphome.core import CORE, HexInt from esphome.core import CORE, HexInt
DEPENDENCIES = ['display'] DEPENDENCIES = ["display"]
MULTI_CONF = True MULTI_CONF = True
Font = display.display_ns.class_('Font') Font = display.display_ns.class_("Font")
Glyph = display.display_ns.class_('Glyph') Glyph = display.display_ns.class_("Glyph")
def validate_glyphs(value): def validate_glyphs(value):
@ -20,8 +20,8 @@ def validate_glyphs(value):
value = cv.Schema([cv.string])(list(value)) value = cv.Schema([cv.string])(list(value))
def comparator(x, y): def comparator(x, y):
x_ = x.encode('utf-8') x_ = x.encode("utf-8")
y_ = y.encode('utf-8') y_ = y.encode("utf-8")
for c in range(min(len(x_), len(y_))): for c in range(min(len(x_), len(y_))):
if x_[c] < y_[c]: if x_[c] < y_[c]:
@ -43,36 +43,48 @@ def validate_pillow_installed(value):
try: try:
import PIL import PIL
except ImportError as err: except ImportError as err:
raise cv.Invalid("Please install the pillow python package to use this feature. " raise cv.Invalid(
"(pip install pillow)") from err "Please install the pillow python package to use this feature. "
"(pip install pillow)"
) from err
if PIL.__version__[0] < '4': if PIL.__version__[0] < "4":
raise cv.Invalid("Please update your pillow installation to at least 4.0.x. " raise cv.Invalid(
"(pip install -U pillow)") "Please update your pillow installation to at least 4.0.x. "
"(pip install -U pillow)"
)
return value return value
def validate_truetype_file(value): def validate_truetype_file(value):
if value.endswith('.zip'): # for Google Fonts downloads if value.endswith(".zip"): # for Google Fonts downloads
raise cv.Invalid("Please unzip the font archive '{}' first and then use the .ttf files " raise cv.Invalid(
"inside.".format(value)) "Please unzip the font archive '{}' first and then use the .ttf files "
if not value.endswith('.ttf'): "inside.".format(value)
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 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) return cv.file_(value)
DEFAULT_GLYPHS = ' !"%()+,-.:0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz°' DEFAULT_GLYPHS = (
CONF_RAW_DATA_ID = 'raw_data_id' ' !"%()+,-.:0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz°'
)
CONF_RAW_DATA_ID = "raw_data_id"
FONT_SCHEMA = cv.Schema({ FONT_SCHEMA = cv.Schema(
cv.Required(CONF_ID): cv.declare_id(Font), {
cv.Required(CONF_FILE): validate_truetype_file, cv.Required(CONF_ID): cv.declare_id(Font),
cv.Optional(CONF_GLYPHS, default=DEFAULT_GLYPHS): validate_glyphs, cv.Required(CONF_FILE): validate_truetype_file,
cv.Optional(CONF_SIZE, default=20): cv.int_range(min=1), cv.Optional(CONF_GLYPHS, default=DEFAULT_GLYPHS): validate_glyphs,
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), 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) CONFIG_SCHEMA = cv.All(validate_pillow_installed, FONT_SCHEMA)
@ -91,7 +103,7 @@ def to_code(config):
glyph_args = {} glyph_args = {}
data = [] data = []
for glyph in config[CONF_GLYPHS]: 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) _, (offset_x, offset_y) = font.font.getsize(glyph)
width, height = mask.size width, height = mask.size
width8 = ((width + 7) // 8) * 8 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.components import climate_ir
from esphome.const import CONF_ID from esphome.const import CONF_ID
AUTO_LOAD = ['climate_ir'] AUTO_LOAD = ["climate_ir"]
fujitsu_general_ns = cg.esphome_ns.namespace('fujitsu_general') fujitsu_general_ns = cg.esphome_ns.namespace("fujitsu_general")
FujitsuGeneralClimate = fujitsu_general_ns.class_('FujitsuGeneralClimate', climate_ir.ClimateIR) FujitsuGeneralClimate = fujitsu_general_ns.class_(
"FujitsuGeneralClimate", climate_ir.ClimateIR
)
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_SCHEMA.extend({ CONFIG_SCHEMA = climate_ir.CLIMATE_IR_SCHEMA.extend(
cv.GenerateID(): cv.declare_id(FujitsuGeneralClimate), {
}) cv.GenerateID(): cv.declare_id(FujitsuGeneralClimate),
}
)
def to_code(config): def to_code(config):

View File

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

View File

@ -1,4 +1,4 @@
import esphome.codegen as cg import esphome.codegen as cg
CODEOWNERS = ['@esphome/core'] CODEOWNERS = ["@esphome/core"]
gpio_ns = cg.esphome_ns.namespace('gpio') 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 esphome.const import CONF_ID, CONF_PIN
from .. import gpio_ns 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({ CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
cv.GenerateID(): cv.declare_id(GPIOBinarySensor), {
cv.Required(CONF_PIN): pins.gpio_input_pin_schema cv.GenerateID(): cv.declare_id(GPIOBinarySensor),
}).extend(cv.COMPONENT_SCHEMA) cv.Required(CONF_PIN): pins.gpio_input_pin_schema,
}
).extend(cv.COMPONENT_SCHEMA)
def to_code(config): def to_code(config):

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