mirror of
https://github.com/esphome/esphome.git
synced 2025-09-28 16:12:24 +01:00
Merge branch 'dev' into bump-1.15.0b1
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import functools
|
||||
import logging
|
||||
@@ -14,40 +12,27 @@ from esphome.const import CONF_BAUD_RATE, CONF_BROKER, CONF_LOGGER, CONF_OTA, \
|
||||
CONF_PASSWORD, CONF_PORT, CONF_ESPHOME, CONF_PLATFORMIO_OPTIONS
|
||||
from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority
|
||||
from esphome.helpers import color, indent
|
||||
from esphome.py_compat import IS_PY2, safe_input
|
||||
from esphome.util import run_external_command, run_external_process, safe_print, list_yaml_files
|
||||
from esphome.util import run_external_command, run_external_process, safe_print, list_yaml_files, \
|
||||
get_serial_ports
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_serial_ports():
|
||||
# from https://github.com/pyserial/pyserial/blob/master/serial/tools/list_ports.py
|
||||
from serial.tools.list_ports import comports
|
||||
result = []
|
||||
for port, desc, info in comports(include_links=True):
|
||||
if not port:
|
||||
continue
|
||||
if "VID:PID" in info:
|
||||
result.append((port, desc))
|
||||
result.sort(key=lambda x: x[0])
|
||||
return result
|
||||
|
||||
|
||||
def choose_prompt(options):
|
||||
if not options:
|
||||
raise EsphomeError("Found no valid options for upload/logging, please make sure relevant "
|
||||
"sections (ota, mqtt, ...) are in your configuration and/or the device "
|
||||
"is plugged in.")
|
||||
"sections (ota, api, mqtt, ...) are in your configuration and/or the "
|
||||
"device is plugged in.")
|
||||
|
||||
if len(options) == 1:
|
||||
return options[0][1]
|
||||
|
||||
safe_print(u"Found multiple options, please choose one:")
|
||||
safe_print("Found multiple options, please choose one:")
|
||||
for i, (desc, _) in enumerate(options):
|
||||
safe_print(u" [{}] {}".format(i + 1, desc))
|
||||
safe_print(f" [{i+1}] {desc}")
|
||||
|
||||
while True:
|
||||
opt = safe_input('(number): ')
|
||||
opt = input('(number): ')
|
||||
if opt in options:
|
||||
opt = options.index(opt)
|
||||
break
|
||||
@@ -57,20 +42,20 @@ def choose_prompt(options):
|
||||
raise ValueError
|
||||
break
|
||||
except ValueError:
|
||||
safe_print(color('red', u"Invalid option: '{}'".format(opt)))
|
||||
safe_print(color('red', f"Invalid option: '{opt}'"))
|
||||
return options[opt - 1][1]
|
||||
|
||||
|
||||
def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api):
|
||||
options = []
|
||||
for res, desc in get_serial_ports():
|
||||
options.append((u"{} ({})".format(res, desc), res))
|
||||
for port in get_serial_ports():
|
||||
options.append((f"{port.path} ({port.description})", port.path))
|
||||
if (show_ota and 'ota' in CORE.config) or (show_api and 'api' in CORE.config):
|
||||
options.append((u"Over The Air ({})".format(CORE.address), CORE.address))
|
||||
options.append((f"Over The Air ({CORE.address})", CORE.address))
|
||||
if default == 'OTA':
|
||||
return CORE.address
|
||||
if show_mqtt and 'mqtt' in CORE.config:
|
||||
options.append((u"MQTT ({})".format(CORE.config['mqtt'][CONF_BROKER]), 'MQTT'))
|
||||
options.append(("MQTT ({})".format(CORE.config['mqtt'][CONF_BROKER]), 'MQTT'))
|
||||
if default == 'OTA':
|
||||
return 'MQTT'
|
||||
if default is not None:
|
||||
@@ -108,11 +93,7 @@ def run_miniterm(config, port):
|
||||
except serial.SerialException:
|
||||
_LOGGER.error("Serial port closed!")
|
||||
return
|
||||
if IS_PY2:
|
||||
line = raw.replace('\r', '').replace('\n', '')
|
||||
else:
|
||||
line = raw.replace(b'\r', b'').replace(b'\n', b'').decode('utf8',
|
||||
'backslashreplace')
|
||||
line = raw.replace(b'\r', b'').replace(b'\n', b'').decode('utf8', 'backslashreplace')
|
||||
time = datetime.now().time().strftime('[%H:%M:%S]')
|
||||
message = time + line
|
||||
safe_print(message)
|
||||
@@ -127,11 +108,9 @@ def wrap_to_code(name, comp):
|
||||
@functools.wraps(comp.to_code)
|
||||
@coroutine_with_priority(coro.priority)
|
||||
def wrapped(conf):
|
||||
cg.add(cg.LineComment(u"{}:".format(name)))
|
||||
cg.add(cg.LineComment(f"{name}:"))
|
||||
if comp.config_schema is not None:
|
||||
conf_str = yaml_util.dump(conf)
|
||||
if IS_PY2:
|
||||
conf_str = conf_str.decode('utf-8')
|
||||
conf_str = conf_str.replace('//', '')
|
||||
cg.add(cg.LineComment(indent(conf_str)))
|
||||
yield coro(conf)
|
||||
@@ -140,6 +119,11 @@ def wrap_to_code(name, comp):
|
||||
|
||||
|
||||
def write_cpp(config):
|
||||
generate_cpp_contents(config)
|
||||
return write_cpp_file()
|
||||
|
||||
|
||||
def generate_cpp_contents(config):
|
||||
_LOGGER.info("Generating C++ source...")
|
||||
|
||||
for name, component, conf in iter_components(CORE.config):
|
||||
@@ -149,6 +133,8 @@ def write_cpp(config):
|
||||
|
||||
CORE.flush_tasks()
|
||||
|
||||
|
||||
def write_cpp_file():
|
||||
writer.write_platformio_project()
|
||||
|
||||
code_s = indent(CORE.cpp_main_section)
|
||||
@@ -165,16 +151,27 @@ def compile_program(args, config):
|
||||
|
||||
def upload_using_esptool(config, port):
|
||||
path = CORE.firmware_bin
|
||||
cmd = ['esptool.py', '--before', 'default_reset', '--after', 'hard_reset',
|
||||
'--baud', str(config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get('upload_speed', 460800)),
|
||||
'--chip', 'esp8266', '--port', port, 'write_flash', '0x0', path]
|
||||
first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get('upload_speed', 460800)
|
||||
|
||||
if os.environ.get('ESPHOME_USE_SUBPROCESS') is None:
|
||||
import esptool
|
||||
# pylint: disable=protected-access
|
||||
return run_external_command(esptool._main, *cmd)
|
||||
def run_esptool(baud_rate):
|
||||
cmd = ['esptool.py', '--before', 'default_reset', '--after', 'hard_reset',
|
||||
'--baud', str(baud_rate),
|
||||
'--chip', 'esp8266', '--port', port, 'write_flash', '0x0', path]
|
||||
|
||||
return run_external_process(*cmd)
|
||||
if os.environ.get('ESPHOME_USE_SUBPROCESS') is None:
|
||||
import esptool
|
||||
# pylint: disable=protected-access
|
||||
return run_external_command(esptool._main, *cmd)
|
||||
|
||||
return run_external_process(*cmd)
|
||||
|
||||
rc = run_esptool(first_baudrate)
|
||||
if rc == 0 or first_baudrate == 115200:
|
||||
return rc
|
||||
# Try with 115200 baud rate, with some serial chips the faster baud rates do not work well
|
||||
_LOGGER.info("Upload with baud rate %s failed. Trying again with baud rate 115200.",
|
||||
first_baudrate)
|
||||
return run_esptool(115200)
|
||||
|
||||
|
||||
def upload_program(config, args, host):
|
||||
@@ -188,6 +185,10 @@ def upload_program(config, args, host):
|
||||
|
||||
from esphome import espota2
|
||||
|
||||
if CONF_OTA not in config:
|
||||
raise EsphomeError("Cannot upload Over the Air as the config does not include the ota: "
|
||||
"component")
|
||||
|
||||
ota_conf = config[CONF_OTA]
|
||||
remote_port = ota_conf[CONF_PORT]
|
||||
password = ota_conf[CONF_PASSWORD]
|
||||
@@ -228,7 +229,7 @@ def setup_log(debug=False, quiet=False):
|
||||
log_level = logging.INFO
|
||||
logging.basicConfig(level=log_level)
|
||||
fmt = "%(levelname)s %(message)s"
|
||||
colorfmt = "%(log_color)s{}%(reset)s".format(fmt)
|
||||
colorfmt = f"%(log_color)s{fmt}%(reset)s"
|
||||
datefmt = '%H:%M:%S'
|
||||
|
||||
logging.getLogger('urllib3').setLevel(logging.WARNING)
|
||||
@@ -277,12 +278,12 @@ def command_compile(args, config):
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
if args.only_generate:
|
||||
_LOGGER.info(u"Successfully generated source code.")
|
||||
_LOGGER.info("Successfully generated source code.")
|
||||
return 0
|
||||
exit_code = compile_program(args, config)
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
_LOGGER.info(u"Successfully compiled program.")
|
||||
_LOGGER.info("Successfully compiled program.")
|
||||
return 0
|
||||
|
||||
|
||||
@@ -292,7 +293,7 @@ def command_upload(args, config):
|
||||
exit_code = upload_program(config, args, port)
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
_LOGGER.info(u"Successfully uploaded program.")
|
||||
_LOGGER.info("Successfully uploaded program.")
|
||||
return 0
|
||||
|
||||
|
||||
@@ -309,13 +310,13 @@ def command_run(args, config):
|
||||
exit_code = compile_program(args, config)
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
_LOGGER.info(u"Successfully compiled program.")
|
||||
_LOGGER.info("Successfully compiled program.")
|
||||
port = choose_upload_log_host(default=args.upload_port, check_default=None,
|
||||
show_ota=True, show_mqtt=False, show_api=True)
|
||||
exit_code = upload_program(config, args, port)
|
||||
if exit_code != 0:
|
||||
return exit_code
|
||||
_LOGGER.info(u"Successfully uploaded program.")
|
||||
_LOGGER.info("Successfully uploaded program.")
|
||||
if args.no_logs:
|
||||
return 0
|
||||
port = choose_upload_log_host(default=args.upload_port, check_default=port,
|
||||
@@ -334,7 +335,7 @@ def command_mqtt_fingerprint(args, config):
|
||||
|
||||
|
||||
def command_version(args):
|
||||
safe_print(u"Version: {}".format(const.__version__))
|
||||
safe_print(f"Version: {const.__version__}")
|
||||
return 0
|
||||
|
||||
|
||||
@@ -362,16 +363,17 @@ def command_update_all(args):
|
||||
twidth = 60
|
||||
|
||||
def print_bar(middle_text):
|
||||
middle_text = " {} ".format(middle_text)
|
||||
middle_text = f" {middle_text} "
|
||||
width = len(click.unstyle(middle_text))
|
||||
half_line = "=" * ((twidth - width) // 2)
|
||||
click.echo("%s%s%s" % (half_line, middle_text, half_line))
|
||||
click.echo(f"{half_line}{middle_text}{half_line}")
|
||||
|
||||
for f in files:
|
||||
print("Updating {}".format(color('cyan', f)))
|
||||
print('-' * twidth)
|
||||
print()
|
||||
rc = run_external_process('esphome', '--dashboard', f, 'run', '--no-logs')
|
||||
rc = run_external_process('esphome', '--dashboard', f, 'run', '--no-logs', '--upload-port',
|
||||
'OTA')
|
||||
if rc == 0:
|
||||
print_bar("[{}] {}".format(color('bold_green', 'SUCCESS'), f))
|
||||
success[f] = True
|
||||
@@ -415,12 +417,14 @@ POST_CONFIG_ACTIONS = {
|
||||
|
||||
|
||||
def parse_args(argv):
|
||||
parser = argparse.ArgumentParser(description='ESPHome v{}'.format(const.__version__))
|
||||
parser = argparse.ArgumentParser(description=f'ESPHome v{const.__version__}')
|
||||
parser.add_argument('-v', '--verbose', help="Enable verbose esphome logs.",
|
||||
action='store_true')
|
||||
parser.add_argument('-q', '--quiet', help="Disable all esphome logs.",
|
||||
action='store_true')
|
||||
parser.add_argument('--dashboard', help=argparse.SUPPRESS, action='store_true')
|
||||
parser.add_argument('-s', '--substitution', nargs=2, action='append',
|
||||
help='Add a substitution', metavar=('key', 'value'))
|
||||
parser.add_argument('configuration', help='Your YAML configuration file.', nargs='*')
|
||||
|
||||
subparsers = parser.add_subparsers(help='Commands', dest='command')
|
||||
@@ -509,10 +513,10 @@ def run_esphome(argv):
|
||||
_LOGGER.error("Missing configuration parameter, see esphome --help.")
|
||||
return 1
|
||||
|
||||
if IS_PY2:
|
||||
_LOGGER.warning("You're using ESPHome with python 2. Support for python 2 is deprecated "
|
||||
"and will be removed in 1.15.0. Please reinstall ESPHome with python 3.6 "
|
||||
"or higher.")
|
||||
if sys.version_info < (3, 6, 0):
|
||||
_LOGGER.error("You're running ESPHome with Python <3.6. ESPHome is no longer compatible "
|
||||
"with this Python version. Please reinstall ESPHome with Python 3.6+")
|
||||
return 1
|
||||
|
||||
if args.command in PRE_CONFIG_ACTIONS:
|
||||
try:
|
||||
@@ -525,13 +529,13 @@ def run_esphome(argv):
|
||||
CORE.config_path = conf_path
|
||||
CORE.dashboard = args.dashboard
|
||||
|
||||
config = read_config()
|
||||
config = read_config(dict(args.substitution) if args.substitution else {})
|
||||
if config is None:
|
||||
return 1
|
||||
CORE.config = config
|
||||
|
||||
if args.command not in POST_CONFIG_ACTIONS:
|
||||
safe_print(u"Unknown command {}".format(args.command))
|
||||
safe_print(f"Unknown command {args.command}")
|
||||
|
||||
try:
|
||||
rc = POST_CONFIG_ACTIONS[args.command](args, config)
|
||||
|
@@ -14,7 +14,6 @@ import esphome.api.api_pb2 as pb
|
||||
from esphome.const import CONF_PASSWORD, CONF_PORT
|
||||
from esphome.core import EsphomeError
|
||||
from esphome.helpers import resolve_ip_address, indent, color
|
||||
from esphome.py_compat import text_type, IS_PY2, byte_to_bytes, char_to_byte
|
||||
from esphome.util import safe_print
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -67,16 +66,16 @@ MESSAGE_TYPE_TO_PROTO = {
|
||||
|
||||
def _varuint_to_bytes(value):
|
||||
if value <= 0x7F:
|
||||
return byte_to_bytes(value)
|
||||
return bytes([value])
|
||||
|
||||
ret = bytes()
|
||||
while value:
|
||||
temp = value & 0x7F
|
||||
value >>= 7
|
||||
if value:
|
||||
ret += byte_to_bytes(temp | 0x80)
|
||||
ret += bytes([temp | 0x80])
|
||||
else:
|
||||
ret += byte_to_bytes(temp)
|
||||
ret += bytes([temp])
|
||||
|
||||
return ret
|
||||
|
||||
@@ -84,8 +83,7 @@ def _varuint_to_bytes(value):
|
||||
def _bytes_to_varuint(value):
|
||||
result = 0
|
||||
bitpos = 0
|
||||
for c in value:
|
||||
val = char_to_byte(c)
|
||||
for val in value:
|
||||
result |= (val & 0x7F) << bitpos
|
||||
bitpos += 7
|
||||
if (val & 0x80) == 0:
|
||||
@@ -191,8 +189,8 @@ class APIClient(threading.Thread):
|
||||
self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||
try:
|
||||
self._socket.connect((ip, self._port))
|
||||
except socket.error as err:
|
||||
err = APIConnectionError("Error connecting to {}: {}".format(ip, err))
|
||||
except OSError as err:
|
||||
err = APIConnectionError(f"Error connecting to {ip}: {err}")
|
||||
self._fatal_error(err)
|
||||
raise err
|
||||
self._socket.settimeout(0.1)
|
||||
@@ -200,7 +198,7 @@ class APIClient(threading.Thread):
|
||||
self._socket_open_event.set()
|
||||
|
||||
hello = pb.HelloRequest()
|
||||
hello.client_info = 'ESPHome v{}'.format(const.__version__)
|
||||
hello.client_info = f'ESPHome v{const.__version__}'
|
||||
try:
|
||||
resp = self._send_message_await_response(hello, pb.HelloResponse)
|
||||
except APIConnectionError as err:
|
||||
@@ -251,8 +249,8 @@ class APIClient(threading.Thread):
|
||||
with self._socket_write_lock:
|
||||
try:
|
||||
self._socket.sendall(data)
|
||||
except socket.error as err:
|
||||
err = APIConnectionError("Error while writing data: {}".format(err))
|
||||
except OSError as err:
|
||||
err = APIConnectionError(f"Error while writing data: {err}")
|
||||
self._fatal_error(err)
|
||||
raise err
|
||||
|
||||
@@ -265,11 +263,8 @@ class APIClient(threading.Thread):
|
||||
raise ValueError
|
||||
|
||||
encoded = msg.SerializeToString()
|
||||
_LOGGER.debug("Sending %s:\n%s", type(msg), indent(text_type(msg)))
|
||||
if IS_PY2:
|
||||
req = chr(0x00)
|
||||
else:
|
||||
req = bytes([0])
|
||||
_LOGGER.debug("Sending %s:\n%s", type(msg), indent(str(msg)))
|
||||
req = bytes([0])
|
||||
req += _varuint_to_bytes(len(encoded))
|
||||
req += _varuint_to_bytes(message_type)
|
||||
req += encoded
|
||||
@@ -355,14 +350,14 @@ class APIClient(threading.Thread):
|
||||
raise APIConnectionError("Socket was closed")
|
||||
except socket.timeout:
|
||||
continue
|
||||
except socket.error as err:
|
||||
raise APIConnectionError("Error while receiving data: {}".format(err))
|
||||
except OSError as err:
|
||||
raise APIConnectionError(f"Error while receiving data: {err}")
|
||||
ret += val
|
||||
return ret
|
||||
|
||||
def _recv_varint(self):
|
||||
raw = bytes()
|
||||
while not raw or char_to_byte(raw[-1]) & 0x80:
|
||||
while not raw or raw[-1] & 0x80:
|
||||
raw += self._recv(1)
|
||||
return _bytes_to_varuint(raw)
|
||||
|
||||
@@ -371,7 +366,7 @@ class APIClient(threading.Thread):
|
||||
return
|
||||
|
||||
# Preamble
|
||||
if char_to_byte(self._recv(1)[0]) != 0x00:
|
||||
if self._recv(1)[0] != 0x00:
|
||||
raise APIConnectionError("Invalid preamble")
|
||||
|
||||
length = self._recv_varint()
|
||||
@@ -436,7 +431,7 @@ def run_logs(config, address):
|
||||
return
|
||||
|
||||
if err:
|
||||
_LOGGER.warning(u"Disconnected from API: %s", err)
|
||||
_LOGGER.warning("Disconnected from API: %s", err)
|
||||
|
||||
while retry_timer:
|
||||
retry_timer.pop(0).cancel()
|
||||
@@ -454,18 +449,18 @@ def run_logs(config, address):
|
||||
|
||||
wait_time = int(min(1.5**min(tries, 100), 30))
|
||||
if not has_connects:
|
||||
_LOGGER.warning(u"Initial connection failed. The ESP might not be connected "
|
||||
u"to WiFi yet (%s). Re-Trying in %s seconds",
|
||||
_LOGGER.warning("Initial connection failed. The ESP might not be connected "
|
||||
"to WiFi yet (%s). Re-Trying in %s seconds",
|
||||
error, wait_time)
|
||||
else:
|
||||
_LOGGER.warning(u"Couldn't connect to API (%s). Trying to reconnect in %s seconds",
|
||||
_LOGGER.warning("Couldn't connect to API (%s). Trying to reconnect in %s seconds",
|
||||
error, wait_time)
|
||||
timer = threading.Timer(wait_time, functools.partial(try_connect, None, tries + 1))
|
||||
timer.start()
|
||||
retry_timer.append(timer)
|
||||
|
||||
def on_log(msg):
|
||||
time_ = datetime.now().time().strftime(u'[%H:%M:%S]')
|
||||
time_ = datetime.now().time().strftime('[%H:%M:%S]')
|
||||
text = msg.message
|
||||
if msg.send_failed:
|
||||
text = color('white', '(Message skipped because it was too big to fit in '
|
||||
|
@@ -7,13 +7,17 @@ from esphome.util import Registry
|
||||
|
||||
|
||||
def maybe_simple_id(*validators):
|
||||
return maybe_conf(CONF_ID, *validators)
|
||||
|
||||
|
||||
def maybe_conf(conf, *validators):
|
||||
validator = cv.All(*validators)
|
||||
|
||||
def validate(value):
|
||||
if isinstance(value, dict):
|
||||
return validator(value)
|
||||
with cv.remove_prepend_path([CONF_ID]):
|
||||
return validator({CONF_ID: value})
|
||||
with cv.remove_prepend_path([conf]):
|
||||
return validator({conf: value})
|
||||
|
||||
return validate
|
||||
|
||||
@@ -79,9 +83,9 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False):
|
||||
try:
|
||||
return cv.Schema([schema])(value)
|
||||
except cv.Invalid as err2:
|
||||
if u'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:
|
||||
raise err
|
||||
if u'Unable to find action' in str(err):
|
||||
if 'Unable to find action' in str(err):
|
||||
raise err2
|
||||
raise cv.MultipleInvalid([err, err2])
|
||||
elif isinstance(value, dict):
|
||||
|
@@ -19,7 +19,7 @@ from esphome.cpp_helpers import ( # noqa
|
||||
gpio_pin_expression, register_component, build_registry_entry,
|
||||
build_registry_list, extract_registry_entry_config, register_parented)
|
||||
from esphome.cpp_types import ( # noqa
|
||||
global_ns, void, nullptr, float_, double, bool_, std_ns, std_string,
|
||||
global_ns, void, nullptr, float_, double, bool_, int_, std_ns, std_string,
|
||||
std_vector, uint8, uint16, uint32, int32, const_char_ptr, NAN,
|
||||
esphome_ns, App, Nameable, Component, ComponentPtr,
|
||||
PollingComponent, Application, optional, arduino_json_ns, JsonObject,
|
||||
|
0
esphome/components/ac_dimmer/__init__.py
Normal file
0
esphome/components/ac_dimmer/__init__.py
Normal file
217
esphome/components/ac_dimmer/ac_dimmer.cpp
Normal file
217
esphome/components/ac_dimmer/ac_dimmer.cpp
Normal file
@@ -0,0 +1,217 @@
|
||||
#include "ac_dimmer.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
#include <core_esp8266_waveform.h>
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace ac_dimmer {
|
||||
|
||||
static const char *TAG = "ac_dimmer";
|
||||
|
||||
// Global array to store dimmer objects
|
||||
static AcDimmerDataStore *all_dimmers[32];
|
||||
|
||||
/// Time in microseconds the gate should be held high
|
||||
/// 10µs should be long enough for most triacs
|
||||
/// For reference: BT136 datasheet says 2µs nominal (page 7)
|
||||
static uint32_t GATE_ENABLE_TIME = 10;
|
||||
|
||||
/// Function called from timer interrupt
|
||||
/// Input is current time in microseconds (micros())
|
||||
/// Returns when next "event" is expected in µs, or 0 if no such event known.
|
||||
uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
|
||||
// If no ZC signal received yet.
|
||||
if (this->crossed_zero_at == 0)
|
||||
return 0;
|
||||
|
||||
uint32_t time_since_zc = now - this->crossed_zero_at;
|
||||
if (this->value == 65535 || this->value == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this->enable_time_us != 0 && time_since_zc >= this->enable_time_us) {
|
||||
this->enable_time_us = 0;
|
||||
this->gate_pin->digital_write(true);
|
||||
// Prevent too short pulses
|
||||
this->disable_time_us = max(this->disable_time_us, time_since_zc + GATE_ENABLE_TIME);
|
||||
}
|
||||
if (this->disable_time_us != 0 && time_since_zc >= this->disable_time_us) {
|
||||
this->disable_time_us = 0;
|
||||
this->gate_pin->digital_write(false);
|
||||
}
|
||||
|
||||
if (time_since_zc < this->enable_time_us)
|
||||
// Next event is enable, return time until that event
|
||||
return this->enable_time_us - time_since_zc;
|
||||
else if (time_since_zc < disable_time_us) {
|
||||
// Next event is disable, return time until that event
|
||||
return this->disable_time_us - time_since_zc;
|
||||
}
|
||||
|
||||
if (time_since_zc >= this->cycle_time_us) {
|
||||
// Already past last cycle time, schedule next call shortly
|
||||
return 100;
|
||||
}
|
||||
|
||||
return this->cycle_time_us - time_since_zc;
|
||||
}
|
||||
|
||||
/// Run timer interrupt code and return in how many µs the next event is expected
|
||||
uint32_t ICACHE_RAM_ATTR HOT timer_interrupt() {
|
||||
// run at least with 1kHz
|
||||
uint32_t min_dt_us = 1000;
|
||||
uint32_t now = micros();
|
||||
for (auto *dimmer : all_dimmers) {
|
||||
if (dimmer == nullptr)
|
||||
// no more dimmers
|
||||
break;
|
||||
uint32_t res = dimmer->timer_intr(now);
|
||||
if (res != 0 && res < min_dt_us)
|
||||
min_dt_us = res;
|
||||
}
|
||||
// return time until next timer1 interrupt in µs
|
||||
return min_dt_us;
|
||||
}
|
||||
|
||||
/// GPIO interrupt routine, called when ZC pin triggers
|
||||
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
|
||||
uint32_t prev_crossed = this->crossed_zero_at;
|
||||
|
||||
// 50Hz mains frequency should give a half cycle of 10ms a 60Hz will give 8.33ms
|
||||
// in any case the cycle last at least 5ms
|
||||
this->crossed_zero_at = micros();
|
||||
uint32_t cycle_time = this->crossed_zero_at - prev_crossed;
|
||||
if (cycle_time > 5000) {
|
||||
this->cycle_time_us = cycle_time;
|
||||
} else {
|
||||
// Otherwise this is noise and this is 2nd (or 3rd...) fall in the same pulse
|
||||
// Consider this is the right fall edge and accumulate the cycle time instead
|
||||
this->cycle_time_us += cycle_time;
|
||||
}
|
||||
|
||||
if (this->value == 65535) {
|
||||
// fully on, enable output immediately
|
||||
this->gate_pin->digital_write(true);
|
||||
} else if (this->init_cycle) {
|
||||
// send a full cycle
|
||||
this->init_cycle = false;
|
||||
this->enable_time_us = 0;
|
||||
this->disable_time_us = cycle_time_us;
|
||||
} else if (this->value == 0) {
|
||||
// fully off, disable output immediately
|
||||
this->gate_pin->digital_write(false);
|
||||
} else {
|
||||
if (this->method == DIM_METHOD_TRAILING) {
|
||||
this->enable_time_us = 1; // cannot be 0
|
||||
this->disable_time_us = max((uint32_t) 10, this->value * this->cycle_time_us / 65535);
|
||||
} else {
|
||||
// calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic
|
||||
// also take into account min_power
|
||||
auto min_us = this->cycle_time_us * this->min_power / 1000;
|
||||
this->enable_time_us = max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535);
|
||||
if (this->method == DIM_METHOD_LEADING_PULSE) {
|
||||
// Minimum pulse time should be enough for the triac to trigger when it is close to the ZC zone
|
||||
// this is for brightness near 99%
|
||||
this->disable_time_us = max(this->enable_time_us + GATE_ENABLE_TIME, (uint32_t) cycle_time_us / 10);
|
||||
} else {
|
||||
this->gate_pin->digital_write(false);
|
||||
this->disable_time_us = this->cycle_time_us;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) {
|
||||
// Attaching pin interrupts on the same pin will override the previous interupt
|
||||
// However, the user expects that multiple dimmers sharing the same ZC pin will work.
|
||||
// We solve this in a bit of a hacky way: On each pin interrupt, we check all dimmers
|
||||
// if any of them are using the same ZC pin, and also trigger the interrupt for *them*.
|
||||
for (auto *dimmer : all_dimmers) {
|
||||
if (dimmer == nullptr)
|
||||
break;
|
||||
if (dimmer->zero_cross_pin_number == store->zero_cross_pin_number) {
|
||||
dimmer->gpio_intr();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
// ESP32 implementation, uses basically the same code but needs to wrap
|
||||
// timer_interrupt() function to auto-reschedule
|
||||
static hw_timer_t *dimmer_timer = nullptr;
|
||||
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_timer_intr() { timer_interrupt(); }
|
||||
#endif
|
||||
|
||||
void AcDimmer::setup() {
|
||||
// extend all_dimmers array with our dimmer
|
||||
|
||||
// Need to be sure the zero cross pin is setup only once, ESP8266 fails and ESP32 seems to fail silently
|
||||
auto setup_zero_cross_pin = true;
|
||||
|
||||
for (auto &all_dimmer : all_dimmers) {
|
||||
if (all_dimmer == nullptr) {
|
||||
all_dimmer = &this->store_;
|
||||
break;
|
||||
}
|
||||
if (all_dimmer->zero_cross_pin_number == this->zero_cross_pin_->get_pin()) {
|
||||
setup_zero_cross_pin = false;
|
||||
}
|
||||
}
|
||||
|
||||
this->gate_pin_->setup();
|
||||
this->store_.gate_pin = this->gate_pin_->to_isr();
|
||||
this->store_.zero_cross_pin_number = this->zero_cross_pin_->get_pin();
|
||||
this->store_.min_power = static_cast<uint16_t>(this->min_power_ * 1000);
|
||||
this->min_power_ = 0;
|
||||
this->store_.method = this->method_;
|
||||
|
||||
if (setup_zero_cross_pin) {
|
||||
this->zero_cross_pin_->setup();
|
||||
this->store_.zero_cross_pin = this->zero_cross_pin_->to_isr();
|
||||
this->zero_cross_pin_->attach_interrupt(&AcDimmerDataStore::s_gpio_intr, &this->store_, FALLING);
|
||||
}
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
// Uses ESP8266 waveform (soft PWM) class
|
||||
// PWM and AcDimmer can even run at the same time this way
|
||||
setTimer1Callback(&timer_interrupt);
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
// 80 Divider -> 1 count=1µs
|
||||
dimmer_timer = timerBegin(0, 80, true);
|
||||
timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr, true);
|
||||
// For ESP32, we can't use dynamic interval calculation because the timerX functions
|
||||
// are not callable from ISR (placed in flash storage).
|
||||
// Here we just use an interrupt firing every 50 µs.
|
||||
timerAlarmWrite(dimmer_timer, 50, true);
|
||||
timerAlarmEnable(dimmer_timer);
|
||||
#endif
|
||||
}
|
||||
void AcDimmer::write_state(float state) {
|
||||
auto new_value = static_cast<uint16_t>(roundf(state * 65535));
|
||||
if (new_value != 0 && this->store_.value == 0)
|
||||
this->store_.init_cycle = this->init_with_half_cycle_;
|
||||
this->store_.value = new_value;
|
||||
}
|
||||
void AcDimmer::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "AcDimmer:");
|
||||
LOG_PIN(" Output Pin: ", this->gate_pin_);
|
||||
LOG_PIN(" Zero-Cross Pin: ", this->zero_cross_pin_);
|
||||
ESP_LOGCONFIG(TAG, " Min Power: %.1f%%", this->store_.min_power / 10.0f);
|
||||
ESP_LOGCONFIG(TAG, " Init with half cycle: %s", YESNO(this->init_with_half_cycle_));
|
||||
if (method_ == DIM_METHOD_LEADING_PULSE)
|
||||
ESP_LOGCONFIG(TAG, " Method: leading pulse");
|
||||
else if (method_ == DIM_METHOD_LEADING)
|
||||
ESP_LOGCONFIG(TAG, " Method: leading");
|
||||
else
|
||||
ESP_LOGCONFIG(TAG, " Method: trailing");
|
||||
|
||||
LOG_FLOAT_OUTPUT(this);
|
||||
ESP_LOGV(TAG, " Estimated Frequency: %.3fHz", 1e6f / this->store_.cycle_time_us / 2);
|
||||
}
|
||||
|
||||
} // namespace ac_dimmer
|
||||
} // namespace esphome
|
66
esphome/components/ac_dimmer/ac_dimmer.h
Normal file
66
esphome/components/ac_dimmer/ac_dimmer.h
Normal file
@@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/esphal.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ac_dimmer {
|
||||
|
||||
enum DimMethod { DIM_METHOD_LEADING_PULSE = 0, DIM_METHOD_LEADING, DIM_METHOD_TRAILING };
|
||||
|
||||
struct AcDimmerDataStore {
|
||||
/// Zero-cross pin
|
||||
ISRInternalGPIOPin *zero_cross_pin;
|
||||
/// Zero-cross pin number - used to share ZC pin across multiple dimmers
|
||||
uint8_t zero_cross_pin_number;
|
||||
/// Output pin to write to
|
||||
ISRInternalGPIOPin *gate_pin;
|
||||
/// Value of the dimmer - 0 to 65535.
|
||||
uint16_t value;
|
||||
/// Minimum power for activation
|
||||
uint16_t min_power;
|
||||
/// Time between the last two ZC pulses
|
||||
uint32_t cycle_time_us;
|
||||
/// Time (in micros()) of last ZC signal
|
||||
uint32_t crossed_zero_at;
|
||||
/// Time since last ZC pulse to enable gate pin. 0 means not set.
|
||||
uint32_t enable_time_us;
|
||||
/// Time since last ZC pulse to disable gate pin. 0 means no disable.
|
||||
uint32_t disable_time_us;
|
||||
/// Set to send the first half ac cycle complete
|
||||
bool init_cycle;
|
||||
/// Dimmer method
|
||||
DimMethod method;
|
||||
|
||||
uint32_t timer_intr(uint32_t now);
|
||||
|
||||
void gpio_intr();
|
||||
static void s_gpio_intr(AcDimmerDataStore *store);
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
static void s_timer_intr();
|
||||
#endif
|
||||
};
|
||||
|
||||
class AcDimmer : public output::FloatOutput, public Component {
|
||||
public:
|
||||
void setup() override;
|
||||
|
||||
void dump_config() override;
|
||||
void set_gate_pin(GPIOPin *gate_pin) { gate_pin_ = gate_pin; }
|
||||
void set_zero_cross_pin(GPIOPin *zero_cross_pin) { zero_cross_pin_ = zero_cross_pin; }
|
||||
void set_init_with_half_cycle(bool init_with_half_cycle) { init_with_half_cycle_ = init_with_half_cycle; }
|
||||
void set_method(DimMethod method) { method_ = method; }
|
||||
|
||||
protected:
|
||||
void write_state(float state) override;
|
||||
|
||||
GPIOPin *gate_pin_;
|
||||
GPIOPin *zero_cross_pin_;
|
||||
AcDimmerDataStore store_;
|
||||
bool init_with_half_cycle_;
|
||||
DimMethod method_;
|
||||
};
|
||||
|
||||
} // namespace ac_dimmer
|
||||
} // namespace esphome
|
45
esphome/components/ac_dimmer/output.py
Normal file
45
esphome/components/ac_dimmer/output.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import pins
|
||||
from esphome.components import output
|
||||
from esphome.const import CONF_ID, CONF_MIN_POWER, CONF_METHOD
|
||||
|
||||
CODEOWNERS = ['@glmnet']
|
||||
|
||||
ac_dimmer_ns = cg.esphome_ns.namespace('ac_dimmer')
|
||||
AcDimmer = ac_dimmer_ns.class_('AcDimmer', output.FloatOutput, cg.Component)
|
||||
|
||||
DimMethod = ac_dimmer_ns.enum('DimMethod')
|
||||
DIM_METHODS = {
|
||||
'LEADING_PULSE': DimMethod.DIM_METHOD_LEADING_PULSE,
|
||||
'LEADING': DimMethod.DIM_METHOD_LEADING,
|
||||
'TRAILING': DimMethod.DIM_METHOD_TRAILING,
|
||||
}
|
||||
|
||||
CONF_GATE_PIN = 'gate_pin'
|
||||
CONF_ZERO_CROSS_PIN = 'zero_cross_pin'
|
||||
CONF_INIT_WITH_HALF_CYCLE = 'init_with_half_cycle'
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend({
|
||||
cv.Required(CONF_ID): cv.declare_id(AcDimmer),
|
||||
cv.Required(CONF_GATE_PIN): pins.internal_gpio_output_pin_schema,
|
||||
cv.Required(CONF_ZERO_CROSS_PIN): pins.internal_gpio_input_pin_schema,
|
||||
cv.Optional(CONF_INIT_WITH_HALF_CYCLE, default=True): cv.boolean,
|
||||
cv.Optional(CONF_METHOD, default='leading pulse'): cv.enum(DIM_METHODS, upper=True, space='_'),
|
||||
}).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
|
||||
# override default min power to 10%
|
||||
if CONF_MIN_POWER not in config:
|
||||
config[CONF_MIN_POWER] = 0.1
|
||||
yield output.register_output(var, config)
|
||||
|
||||
pin = yield cg.gpio_pin_expression(config[CONF_GATE_PIN])
|
||||
cg.add(var.set_gate_pin(pin))
|
||||
pin = yield cg.gpio_pin_expression(config[CONF_ZERO_CROSS_PIN])
|
||||
cg.add(var.set_zero_cross_pin(pin))
|
||||
cg.add(var.set_init_with_half_cycle(config[CONF_INIT_WITH_HALF_CYCLE]))
|
||||
cg.add(var.set_method(config[CONF_METHOD]))
|
24
esphome/components/adalight/__init__.py
Normal file
24
esphome/components/adalight/__init__.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import uart
|
||||
from esphome.components.light.types import AddressableLightEffect
|
||||
from esphome.components.light.effects import register_addressable_effect
|
||||
from esphome.const import CONF_NAME, CONF_UART_ID
|
||||
|
||||
DEPENDENCIES = ['uart']
|
||||
|
||||
adalight_ns = cg.esphome_ns.namespace('adalight')
|
||||
AdalightLightEffect = adalight_ns.class_(
|
||||
'AdalightLightEffect', uart.UARTDevice, AddressableLightEffect)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema({})
|
||||
|
||||
|
||||
@register_addressable_effect('adalight', AdalightLightEffect, "Adalight", {
|
||||
cv.GenerateID(CONF_UART_ID): cv.use_id(uart.UARTComponent)
|
||||
})
|
||||
def adalight_light_effect_to_code(config, effect_id):
|
||||
effect = cg.new_Pvariable(effect_id, config[CONF_NAME])
|
||||
yield uart.register_uart_device(effect, config)
|
||||
|
||||
yield effect
|
140
esphome/components/adalight/adalight_light_effect.cpp
Normal file
140
esphome/components/adalight/adalight_light_effect.cpp
Normal file
@@ -0,0 +1,140 @@
|
||||
#include "adalight_light_effect.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace adalight {
|
||||
|
||||
static const char *TAG = "adalight_light_effect";
|
||||
|
||||
static const uint32_t ADALIGHT_ACK_INTERVAL = 1000;
|
||||
static const uint32_t ADALIGHT_RECEIVE_TIMEOUT = 1000;
|
||||
|
||||
AdalightLightEffect::AdalightLightEffect(const std::string &name) : AddressableLightEffect(name) {}
|
||||
|
||||
void AdalightLightEffect::start() {
|
||||
AddressableLightEffect::start();
|
||||
|
||||
last_ack_ = 0;
|
||||
last_byte_ = 0;
|
||||
last_reset_ = 0;
|
||||
}
|
||||
|
||||
void AdalightLightEffect::stop() {
|
||||
frame_.resize(0);
|
||||
|
||||
AddressableLightEffect::stop();
|
||||
}
|
||||
|
||||
int AdalightLightEffect::get_frame_size_(int led_count) const {
|
||||
// 3 bytes: Ada
|
||||
// 2 bytes: LED count
|
||||
// 1 byte: checksum
|
||||
// 3 bytes per LED
|
||||
return 3 + 2 + 1 + led_count * 3;
|
||||
}
|
||||
|
||||
void AdalightLightEffect::reset_frame_(light::AddressableLight &it) {
|
||||
int buffer_capacity = get_frame_size_(it.size());
|
||||
|
||||
frame_.clear();
|
||||
frame_.reserve(buffer_capacity);
|
||||
}
|
||||
|
||||
void AdalightLightEffect::blank_all_leds_(light::AddressableLight &it) {
|
||||
for (int led = it.size(); led-- > 0;) {
|
||||
it[led].set(light::ESPColor::BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
void AdalightLightEffect::apply(light::AddressableLight &it, const light::ESPColor ¤t_color) {
|
||||
const uint32_t now = millis();
|
||||
|
||||
if (now - this->last_ack_ >= ADALIGHT_ACK_INTERVAL) {
|
||||
ESP_LOGV(TAG, "Sending ACK");
|
||||
this->write_str("Ada\n");
|
||||
this->last_ack_ = now;
|
||||
}
|
||||
|
||||
if (!this->last_reset_) {
|
||||
ESP_LOGW(TAG, "Frame: Reset.");
|
||||
reset_frame_(it);
|
||||
blank_all_leds_(it);
|
||||
this->last_reset_ = now;
|
||||
}
|
||||
|
||||
if (!this->frame_.empty() && now - this->last_byte_ >= ADALIGHT_RECEIVE_TIMEOUT) {
|
||||
ESP_LOGW(TAG, "Frame: Receive timeout (size=%zu).", this->frame_.size());
|
||||
reset_frame_(it);
|
||||
blank_all_leds_(it);
|
||||
}
|
||||
|
||||
if (this->available() > 0) {
|
||||
ESP_LOGV(TAG, "Frame: Available (size=%d).", this->available());
|
||||
}
|
||||
|
||||
while (this->available() != 0) {
|
||||
uint8_t data;
|
||||
if (!this->read_byte(&data))
|
||||
break;
|
||||
this->frame_.push_back(data);
|
||||
this->last_byte_ = now;
|
||||
|
||||
switch (this->parse_frame_(it)) {
|
||||
case INVALID:
|
||||
ESP_LOGD(TAG, "Frame: Invalid (size=%zu, first=%d).", this->frame_.size(), this->frame_[0]);
|
||||
reset_frame_(it);
|
||||
break;
|
||||
|
||||
case PARTIAL:
|
||||
break;
|
||||
|
||||
case CONSUMED:
|
||||
ESP_LOGV(TAG, "Frame: Consumed (size=%zu).", this->frame_.size());
|
||||
reset_frame_(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AdalightLightEffect::Frame AdalightLightEffect::parse_frame_(light::AddressableLight &it) {
|
||||
if (frame_.empty())
|
||||
return INVALID;
|
||||
|
||||
// Check header: `Ada`
|
||||
if (frame_[0] != 'A')
|
||||
return INVALID;
|
||||
if (frame_.size() > 1 && frame_[1] != 'd')
|
||||
return INVALID;
|
||||
if (frame_.size() > 2 && frame_[2] != 'a')
|
||||
return INVALID;
|
||||
|
||||
// 3 bytes: Count Hi, Count Lo, Checksum
|
||||
if (frame_.size() < 6)
|
||||
return PARTIAL;
|
||||
|
||||
// Check checksum
|
||||
uint16_t checksum = frame_[3] ^ frame_[4] ^ 0x55;
|
||||
if (checksum != frame_[5])
|
||||
return INVALID;
|
||||
|
||||
// Check if we received the full frame
|
||||
uint16_t led_count = (frame_[3] << 8) + frame_[4] + 1;
|
||||
auto buffer_size = get_frame_size_(led_count);
|
||||
if (frame_.size() < buffer_size)
|
||||
return PARTIAL;
|
||||
|
||||
// Apply lights
|
||||
auto accepted_led_count = std::min<int>(led_count, it.size());
|
||||
uint8_t *led_data = &frame_[6];
|
||||
|
||||
for (int led = 0; led < accepted_led_count; led++, led_data += 3) {
|
||||
auto white = std::min(std::min(led_data[0], led_data[1]), led_data[2]);
|
||||
|
||||
it[led].set(light::ESPColor(led_data[0], led_data[1], led_data[2], white));
|
||||
}
|
||||
|
||||
return CONSUMED;
|
||||
}
|
||||
|
||||
} // namespace adalight
|
||||
} // namespace esphome
|
41
esphome/components/adalight/adalight_light_effect.h
Normal file
41
esphome/components/adalight/adalight_light_effect.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/light/addressable_light_effect.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
namespace adalight {
|
||||
|
||||
class AdalightLightEffect : public light::AddressableLightEffect, public uart::UARTDevice {
|
||||
public:
|
||||
AdalightLightEffect(const std::string &name);
|
||||
|
||||
public:
|
||||
void start() override;
|
||||
void stop() override;
|
||||
void apply(light::AddressableLight &it, const light::ESPColor ¤t_color) override;
|
||||
|
||||
protected:
|
||||
enum Frame {
|
||||
INVALID,
|
||||
PARTIAL,
|
||||
CONSUMED,
|
||||
};
|
||||
|
||||
int get_frame_size_(int led_count) const;
|
||||
void reset_frame_(light::AddressableLight &it);
|
||||
void blank_all_leds_(light::AddressableLight &it);
|
||||
Frame parse_frame_(light::AddressableLight &it);
|
||||
|
||||
protected:
|
||||
uint32_t last_ack_{0};
|
||||
uint32_t last_byte_{0};
|
||||
uint32_t last_reset_{0};
|
||||
std::vector<uint8_t> frame_;
|
||||
};
|
||||
|
||||
} // namespace adalight
|
||||
} // namespace esphome
|
@@ -0,0 +1 @@
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
|
@@ -58,7 +58,7 @@ void ADCSensor::update() {
|
||||
}
|
||||
float ADCSensor::sample() {
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
float value_v = analogRead(this->pin_) / 4095.0f;
|
||||
float value_v = analogRead(this->pin_) / 4095.0f; // NOLINT
|
||||
switch (this->attenuation_) {
|
||||
case ADC_0db:
|
||||
value_v *= 1.1;
|
||||
@@ -80,7 +80,7 @@ float ADCSensor::sample() {
|
||||
#ifdef USE_ADC_SENSOR_VCC
|
||||
return ESP.getVcc() / 1024.0f;
|
||||
#else
|
||||
return analogRead(this->pin_) / 1024.0f;
|
||||
return analogRead(this->pin_) / 1024.0f; // NOLINT
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@ void ADE7953::dump_config() {
|
||||
}
|
||||
|
||||
#define ADE_PUBLISH_(name, factor) \
|
||||
if (name) { \
|
||||
if (name && this->name##_sensor_) { \
|
||||
float value = *name / factor; \
|
||||
this->name##_sensor_->publish_state(value); \
|
||||
}
|
||||
|
@@ -36,4 +36,4 @@ def to_code(config):
|
||||
continue
|
||||
conf = config[key]
|
||||
sens = yield sensor.new_sensor(conf)
|
||||
cg.add(getattr(var, 'set_{}_sensor'.format(key))(sens))
|
||||
cg.add(getattr(var, f'set_{key}_sensor')(sens))
|
||||
|
@@ -2,7 +2,6 @@ import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, voltage_sampler
|
||||
from esphome.const import CONF_GAIN, CONF_MULTIPLEXER, ICON_FLASH, UNIT_VOLT, CONF_ID
|
||||
from esphome.py_compat import string_types
|
||||
from . import ads1115_ns, ADS1115Component
|
||||
|
||||
DEPENDENCIES = ['ads1115']
|
||||
@@ -32,9 +31,9 @@ GAIN = {
|
||||
|
||||
def validate_gain(value):
|
||||
if isinstance(value, float):
|
||||
value = u'{:0.03f}'.format(value)
|
||||
elif not isinstance(value, string_types):
|
||||
raise cv.Invalid('invalid gain "{}"'.format(value))
|
||||
value = f'{value:0.03f}'
|
||||
elif not isinstance(value, str):
|
||||
raise cv.Invalid(f'invalid gain "{value}"')
|
||||
|
||||
return cv.enum(GAIN)(value)
|
||||
|
||||
|
0
esphome/components/aht10/__init__.py
Normal file
0
esphome/components/aht10/__init__.py
Normal file
127
esphome/components/aht10/aht10.cpp
Normal file
127
esphome/components/aht10/aht10.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
// Implementation based on:
|
||||
// - AHT10: https://github.com/Thinary/AHT10
|
||||
// - Official Datasheet (cn):
|
||||
// http://www.aosong.com/userfiles/files/media/aht10%E8%A7%84%E6%A0%BC%E4%B9%A6v1_1%EF%BC%8820191015%EF%BC%89.pdf
|
||||
// - Unofficial Translated Datasheet (en):
|
||||
// https://wiki.liutyi.info/download/attachments/30507639/Aosong_AHT10_en_draft_0c.pdf
|
||||
//
|
||||
// When configured for humidity, the log 'Components should block for at most 20-30ms in loop().' will be generated in
|
||||
// verbose mode. This is due to technical specs of the sensor and can not be avoided.
|
||||
//
|
||||
// According to the datasheet, the component is supposed to respond in more than 75ms. In fact, it can answer almost
|
||||
// immediately for temperature. But for humidity, it takes >90ms to get a valid data. From experience, we have best
|
||||
// results making successive requests; the current implementation make 3 attemps with a delay of 30ms each time.
|
||||
|
||||
#include "aht10.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace aht10 {
|
||||
|
||||
static const char *TAG = "aht10";
|
||||
static const uint8_t AHT10_CALIBRATE_CMD[] = {0xE1};
|
||||
static const uint8_t AHT10_MEASURE_CMD[] = {0xAC, 0x33, 0x00};
|
||||
static const uint8_t AHT10_DEFAULT_DELAY = 5; // ms, for calibration and temperature measurement
|
||||
static const uint8_t AHT10_HUMIDITY_DELAY = 30; // ms
|
||||
static const uint8_t AHT10_ATTEMPS = 3; // safety margin, normally 3 attemps are enough: 3*30=90ms
|
||||
|
||||
void AHT10Component::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up AHT10...");
|
||||
|
||||
if (!this->write_bytes(0, AHT10_CALIBRATE_CMD, sizeof(AHT10_CALIBRATE_CMD))) {
|
||||
ESP_LOGE(TAG, "Communication with AHT10 failed!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
uint8_t data;
|
||||
if (!this->read_byte(0, &data, AHT10_DEFAULT_DELAY)) {
|
||||
ESP_LOGD(TAG, "Communication with AHT10 failed!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
if ((data & 0x68) != 0x08) { // Bit[6:5] = 0b00, NORMAL mode and Bit[3] = 0b1, CALIBRATED
|
||||
ESP_LOGE(TAG, "AHT10 calibration failed!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "AHT10 calibrated");
|
||||
}
|
||||
|
||||
void AHT10Component::update() {
|
||||
if (!this->write_bytes(0, AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD))) {
|
||||
ESP_LOGE(TAG, "Communication with AHT10 failed!");
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
uint8_t data[6];
|
||||
uint8_t delay = AHT10_DEFAULT_DELAY;
|
||||
if (this->humidity_sensor_ != nullptr)
|
||||
delay = AHT10_HUMIDITY_DELAY;
|
||||
for (int i = 0; i < AHT10_ATTEMPS; ++i) {
|
||||
ESP_LOGVV(TAG, "Attemps %u at %6ld", i, millis());
|
||||
if (!this->read_bytes(0, data, 6, delay)) {
|
||||
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
|
||||
} else if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy
|
||||
ESP_LOGD(TAG, "AHT10 is busy, waiting...");
|
||||
} else if (data[1] == 0x0 && data[2] == 0x0 && (data[3] >> 4) == 0x0) {
|
||||
// Unrealistic humidity (0x0)
|
||||
if (this->humidity_sensor_ == nullptr) {
|
||||
ESP_LOGVV(TAG, "ATH10 Unrealistic humidity (0x0), but humidity is not required");
|
||||
break;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "ATH10 Unrealistic humidity (0x0), retrying...");
|
||||
if (!this->write_bytes(0, AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD))) {
|
||||
ESP_LOGE(TAG, "Communication with AHT10 failed!");
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// data is valid, we can break the loop
|
||||
ESP_LOGVV(TAG, "Answer at %6ld", millis());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((data[0] & 0x80) == 0x80) {
|
||||
ESP_LOGE(TAG, "Measurements reading timed-out!");
|
||||
this->status_set_warning();
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t raw_temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
|
||||
uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4;
|
||||
|
||||
float temperature = ((200.0 * (float) raw_temperature) / 1048576.0) - 50.0;
|
||||
float humidity;
|
||||
if (raw_humidity == 0) { // unrealistic value
|
||||
humidity = NAN;
|
||||
} else {
|
||||
humidity = (float) raw_humidity * 100.0 / 1048576.0;
|
||||
}
|
||||
|
||||
if (this->temperature_sensor_ != nullptr) {
|
||||
this->temperature_sensor_->publish_state(temperature);
|
||||
}
|
||||
if (this->humidity_sensor_ != nullptr) {
|
||||
if (isnan(humidity))
|
||||
ESP_LOGW(TAG, "Invalid humidity! Sensor reported 0%% Hum");
|
||||
this->humidity_sensor_->publish_state(humidity);
|
||||
}
|
||||
this->status_clear_warning();
|
||||
}
|
||||
|
||||
float AHT10Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
void AHT10Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "AHT10:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, "Communication with AHT10 failed!");
|
||||
}
|
||||
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
||||
}
|
||||
|
||||
} // namespace aht10
|
||||
} // namespace esphome
|
26
esphome/components/aht10/aht10.h
Normal file
26
esphome/components/aht10/aht10.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace aht10 {
|
||||
|
||||
class AHT10Component : public PollingComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
void setup() override;
|
||||
void update() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override;
|
||||
|
||||
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
||||
void set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; }
|
||||
|
||||
protected:
|
||||
sensor::Sensor *temperature_sensor_;
|
||||
sensor::Sensor *humidity_sensor_;
|
||||
};
|
||||
|
||||
} // namespace aht10
|
||||
} // namespace esphome
|
30
esphome/components/aht10/sensor.py
Normal file
30
esphome/components/aht10/sensor.py
Normal file
@@ -0,0 +1,30 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c, sensor
|
||||
from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, \
|
||||
UNIT_CELSIUS, ICON_THERMOMETER, ICON_WATER_PERCENT, UNIT_PERCENT
|
||||
|
||||
DEPENDENCIES = ['i2c']
|
||||
|
||||
aht10_ns = cg.esphome_ns.namespace('aht10')
|
||||
AHT10Component = aht10_ns.class_('AHT10Component', cg.PollingComponent, i2c.I2CDevice)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema({
|
||||
cv.GenerateID(): cv.declare_id(AHT10Component),
|
||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 2),
|
||||
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 2),
|
||||
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x38))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield i2c.register_i2c_device(var, config)
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||
cg.add(var.set_temperature_sensor(sens))
|
||||
|
||||
if CONF_HUMIDITY in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_HUMIDITY])
|
||||
cg.add(var.set_humidity_sensor(sens))
|
@@ -8,6 +8,7 @@ from esphome.core import coroutine_with_priority
|
||||
|
||||
DEPENDENCIES = ['network']
|
||||
AUTO_LOAD = ['async_tcp']
|
||||
CODEOWNERS = ['@OttoWinter']
|
||||
|
||||
api_ns = cg.esphome_ns.namespace('api')
|
||||
APIServer = api_ns.class_('APIServer', cg.Component, cg.Controller)
|
||||
@@ -70,14 +71,14 @@ def to_code(config):
|
||||
cg.add_global(api_ns.using)
|
||||
|
||||
|
||||
KEY_VALUE_SCHEMA = cv.Schema({cv.string: cv.templatable(cv.string)})
|
||||
KEY_VALUE_SCHEMA = cv.Schema({cv.string: cv.templatable(cv.string_strict)})
|
||||
|
||||
HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema({
|
||||
cv.GenerateID(): cv.use_id(APIServer),
|
||||
cv.Required(CONF_SERVICE): cv.templatable(cv.string),
|
||||
cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA,
|
||||
cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA,
|
||||
cv.Optional(CONF_VARIABLES, default={}): KEY_VALUE_SCHEMA,
|
||||
cv.Optional(CONF_VARIABLES, default={}): cv.Schema({cv.string: cv.returning_lambda}),
|
||||
})
|
||||
|
||||
|
||||
@@ -102,7 +103,7 @@ def homeassistant_service_to_code(config, action_id, template_arg, args):
|
||||
|
||||
def validate_homeassistant_event(value):
|
||||
value = cv.string(value)
|
||||
if not value.startswith(u'esphome.'):
|
||||
if not value.startswith('esphome.'):
|
||||
raise cv.Invalid("ESPHome can only generate Home Assistant events that begin with "
|
||||
"esphome. For example 'esphome.xyz'")
|
||||
return value
|
||||
|
@@ -216,6 +216,9 @@ message BinarySensorStateResponse {
|
||||
|
||||
fixed32 key = 1;
|
||||
bool state = 2;
|
||||
// If the binary sensor does not have a valid state yet.
|
||||
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||
bool missing_state = 3;
|
||||
}
|
||||
|
||||
// ==================== COVER ====================
|
||||
@@ -298,12 +301,17 @@ message ListEntitiesFanResponse {
|
||||
|
||||
bool supports_oscillation = 5;
|
||||
bool supports_speed = 6;
|
||||
bool supports_direction = 7;
|
||||
}
|
||||
enum FanSpeed {
|
||||
FAN_SPEED_LOW = 0;
|
||||
FAN_SPEED_MEDIUM = 1;
|
||||
FAN_SPEED_HIGH = 2;
|
||||
}
|
||||
enum FanDirection {
|
||||
FAN_DIRECTION_FORWARD = 0;
|
||||
FAN_DIRECTION_REVERSE = 1;
|
||||
}
|
||||
message FanStateResponse {
|
||||
option (id) = 23;
|
||||
option (source) = SOURCE_SERVER;
|
||||
@@ -314,6 +322,7 @@ message FanStateResponse {
|
||||
bool state = 2;
|
||||
bool oscillating = 3;
|
||||
FanSpeed speed = 4;
|
||||
FanDirection direction = 5;
|
||||
}
|
||||
message FanCommandRequest {
|
||||
option (id) = 31;
|
||||
@@ -328,6 +337,8 @@ message FanCommandRequest {
|
||||
FanSpeed speed = 5;
|
||||
bool has_oscillating = 6;
|
||||
bool oscillating = 7;
|
||||
bool has_direction = 8;
|
||||
FanDirection direction = 9;
|
||||
}
|
||||
|
||||
// ==================== LIGHT ====================
|
||||
@@ -416,6 +427,9 @@ message SensorStateResponse {
|
||||
|
||||
fixed32 key = 1;
|
||||
float state = 2;
|
||||
// If the sensor does not have a valid state yet.
|
||||
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||
bool missing_state = 3;
|
||||
}
|
||||
|
||||
// ==================== SWITCH ====================
|
||||
@@ -472,6 +486,9 @@ message TextSensorStateResponse {
|
||||
|
||||
fixed32 key = 1;
|
||||
string state = 2;
|
||||
// If the text sensor does not have a valid state yet.
|
||||
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||
bool missing_state = 3;
|
||||
}
|
||||
|
||||
// ==================== SUBSCRIBE LOGS ====================
|
||||
@@ -644,12 +661,34 @@ enum ClimateMode {
|
||||
CLIMATE_MODE_AUTO = 1;
|
||||
CLIMATE_MODE_COOL = 2;
|
||||
CLIMATE_MODE_HEAT = 3;
|
||||
CLIMATE_MODE_FAN_ONLY = 4;
|
||||
CLIMATE_MODE_DRY = 5;
|
||||
}
|
||||
enum ClimateFanMode {
|
||||
CLIMATE_FAN_ON = 0;
|
||||
CLIMATE_FAN_OFF = 1;
|
||||
CLIMATE_FAN_AUTO = 2;
|
||||
CLIMATE_FAN_LOW = 3;
|
||||
CLIMATE_FAN_MEDIUM = 4;
|
||||
CLIMATE_FAN_HIGH = 5;
|
||||
CLIMATE_FAN_MIDDLE = 6;
|
||||
CLIMATE_FAN_FOCUS = 7;
|
||||
CLIMATE_FAN_DIFFUSE = 8;
|
||||
}
|
||||
enum ClimateSwingMode {
|
||||
CLIMATE_SWING_OFF = 0;
|
||||
CLIMATE_SWING_BOTH = 1;
|
||||
CLIMATE_SWING_VERTICAL = 2;
|
||||
CLIMATE_SWINT_HORIZONTAL = 3;
|
||||
}
|
||||
enum ClimateAction {
|
||||
CLIMATE_ACTION_OFF = 0;
|
||||
// values same as mode for readability
|
||||
CLIMATE_ACTION_COOLING = 2;
|
||||
CLIMATE_ACTION_HEATING = 3;
|
||||
CLIMATE_ACTION_IDLE = 4;
|
||||
CLIMATE_ACTION_DRYING = 5;
|
||||
CLIMATE_ACTION_FAN = 6;
|
||||
}
|
||||
message ListEntitiesClimateResponse {
|
||||
option (id) = 46;
|
||||
@@ -669,6 +708,8 @@ message ListEntitiesClimateResponse {
|
||||
float visual_temperature_step = 10;
|
||||
bool supports_away = 11;
|
||||
bool supports_action = 12;
|
||||
repeated ClimateFanMode supported_fan_modes = 13;
|
||||
repeated ClimateSwingMode supported_swing_modes = 14;
|
||||
}
|
||||
message ClimateStateResponse {
|
||||
option (id) = 47;
|
||||
@@ -684,6 +725,8 @@ message ClimateStateResponse {
|
||||
float target_temperature_high = 6;
|
||||
bool away = 7;
|
||||
ClimateAction action = 8;
|
||||
ClimateFanMode fan_mode = 9;
|
||||
ClimateSwingMode swing_mode = 10;
|
||||
}
|
||||
message ClimateCommandRequest {
|
||||
option (id) = 48;
|
||||
@@ -702,4 +745,8 @@ message ClimateCommandRequest {
|
||||
float target_temperature_high = 9;
|
||||
bool has_away = 10;
|
||||
bool away = 11;
|
||||
bool has_fan_mode = 12;
|
||||
ClimateFanMode fan_mode = 13;
|
||||
bool has_swing_mode = 14;
|
||||
ClimateSwingMode swing_mode = 15;
|
||||
}
|
||||
|
@@ -137,7 +137,6 @@ void APIConnection::loop() {
|
||||
// bool done = 3;
|
||||
bool done = this->image_reader_.available() == to_send;
|
||||
buffer.encode_bool(3, done);
|
||||
this->set_nodelay(false);
|
||||
bool success = this->send_buffer(buffer, 44);
|
||||
|
||||
if (success) {
|
||||
@@ -163,6 +162,7 @@ bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary
|
||||
BinarySensorStateResponse resp;
|
||||
resp.key = binary_sensor->get_object_id_hash();
|
||||
resp.state = state;
|
||||
resp.missing_state = !binary_sensor->has_state();
|
||||
return this->send_binary_sensor_state_response(resp);
|
||||
}
|
||||
bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor) {
|
||||
@@ -185,11 +185,12 @@ bool APIConnection::send_cover_state(cover::Cover *cover) {
|
||||
auto traits = cover->get_traits();
|
||||
CoverStateResponse resp{};
|
||||
resp.key = cover->get_object_id_hash();
|
||||
resp.legacy_state = (cover->position == cover::COVER_OPEN) ? LEGACY_COVER_STATE_OPEN : LEGACY_COVER_STATE_CLOSED;
|
||||
resp.legacy_state =
|
||||
(cover->position == cover::COVER_OPEN) ? enums::LEGACY_COVER_STATE_OPEN : enums::LEGACY_COVER_STATE_CLOSED;
|
||||
resp.position = cover->position;
|
||||
if (traits.get_supports_tilt())
|
||||
resp.tilt = cover->tilt;
|
||||
resp.current_operation = static_cast<EnumCoverOperation>(cover->current_operation);
|
||||
resp.current_operation = static_cast<enums::CoverOperation>(cover->current_operation);
|
||||
return this->send_cover_state_response(resp);
|
||||
}
|
||||
bool APIConnection::send_cover_info(cover::Cover *cover) {
|
||||
@@ -213,13 +214,13 @@ void APIConnection::cover_command(const CoverCommandRequest &msg) {
|
||||
auto call = cover->make_call();
|
||||
if (msg.has_legacy_command) {
|
||||
switch (msg.legacy_command) {
|
||||
case LEGACY_COVER_COMMAND_OPEN:
|
||||
case enums::LEGACY_COVER_COMMAND_OPEN:
|
||||
call.set_command_open();
|
||||
break;
|
||||
case LEGACY_COVER_COMMAND_CLOSE:
|
||||
case enums::LEGACY_COVER_COMMAND_CLOSE:
|
||||
call.set_command_close();
|
||||
break;
|
||||
case LEGACY_COVER_COMMAND_STOP:
|
||||
case enums::LEGACY_COVER_COMMAND_STOP:
|
||||
call.set_command_stop();
|
||||
break;
|
||||
}
|
||||
@@ -246,7 +247,9 @@ bool APIConnection::send_fan_state(fan::FanState *fan) {
|
||||
if (traits.supports_oscillation())
|
||||
resp.oscillating = fan->oscillating;
|
||||
if (traits.supports_speed())
|
||||
resp.speed = static_cast<EnumFanSpeed>(fan->speed);
|
||||
resp.speed = static_cast<enums::FanSpeed>(fan->speed);
|
||||
if (traits.supports_direction())
|
||||
resp.direction = static_cast<enums::FanDirection>(fan->direction);
|
||||
return this->send_fan_state_response(resp);
|
||||
}
|
||||
bool APIConnection::send_fan_info(fan::FanState *fan) {
|
||||
@@ -258,6 +261,7 @@ bool APIConnection::send_fan_info(fan::FanState *fan) {
|
||||
msg.unique_id = get_default_unique_id("fan", fan);
|
||||
msg.supports_oscillation = traits.supports_oscillation();
|
||||
msg.supports_speed = traits.supports_speed();
|
||||
msg.supports_direction = traits.supports_direction();
|
||||
return this->send_list_entities_fan_response(msg);
|
||||
}
|
||||
void APIConnection::fan_command(const FanCommandRequest &msg) {
|
||||
@@ -272,6 +276,8 @@ void APIConnection::fan_command(const FanCommandRequest &msg) {
|
||||
call.set_oscillating(msg.oscillating);
|
||||
if (msg.has_speed)
|
||||
call.set_speed(static_cast<fan::FanSpeed>(msg.speed));
|
||||
if (msg.has_direction)
|
||||
call.set_direction(static_cast<fan::FanDirection>(msg.direction));
|
||||
call.perform();
|
||||
}
|
||||
#endif
|
||||
@@ -361,6 +367,7 @@ bool APIConnection::send_sensor_state(sensor::Sensor *sensor, float state) {
|
||||
SensorStateResponse resp{};
|
||||
resp.key = sensor->get_object_id_hash();
|
||||
resp.state = state;
|
||||
resp.missing_state = !sensor->has_state();
|
||||
return this->send_sensor_state_response(resp);
|
||||
}
|
||||
bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
|
||||
@@ -374,6 +381,7 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
|
||||
msg.icon = sensor->get_icon();
|
||||
msg.unit_of_measurement = sensor->get_unit_of_measurement();
|
||||
msg.accuracy_decimals = sensor->get_accuracy_decimals();
|
||||
msg.force_update = sensor->get_force_update();
|
||||
return this->send_list_entities_sensor_response(msg);
|
||||
}
|
||||
#endif
|
||||
@@ -418,6 +426,7 @@ bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor,
|
||||
TextSensorStateResponse resp{};
|
||||
resp.key = text_sensor->get_object_id_hash();
|
||||
resp.state = std::move(state);
|
||||
resp.missing_state = !text_sensor->has_state();
|
||||
return this->send_text_sensor_state_response(resp);
|
||||
}
|
||||
bool APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor) {
|
||||
@@ -441,8 +450,8 @@ bool APIConnection::send_climate_state(climate::Climate *climate) {
|
||||
auto traits = climate->get_traits();
|
||||
ClimateStateResponse resp{};
|
||||
resp.key = climate->get_object_id_hash();
|
||||
resp.mode = static_cast<EnumClimateMode>(climate->mode);
|
||||
resp.action = static_cast<EnumClimateAction>(climate->action);
|
||||
resp.mode = static_cast<enums::ClimateMode>(climate->mode);
|
||||
resp.action = static_cast<enums::ClimateAction>(climate->action);
|
||||
if (traits.get_supports_current_temperature())
|
||||
resp.current_temperature = climate->current_temperature;
|
||||
if (traits.get_supports_two_point_target_temperature()) {
|
||||
@@ -453,6 +462,10 @@ bool APIConnection::send_climate_state(climate::Climate *climate) {
|
||||
}
|
||||
if (traits.get_supports_away())
|
||||
resp.away = climate->away;
|
||||
if (traits.get_supports_fan_modes())
|
||||
resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode);
|
||||
if (traits.get_supports_swing_modes())
|
||||
resp.swing_mode = static_cast<enums::ClimateSwingMode>(climate->swing_mode);
|
||||
return this->send_climate_state_response(resp);
|
||||
}
|
||||
bool APIConnection::send_climate_info(climate::Climate *climate) {
|
||||
@@ -465,15 +478,26 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
|
||||
msg.supports_current_temperature = traits.get_supports_current_temperature();
|
||||
msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
|
||||
for (auto mode : {climate::CLIMATE_MODE_AUTO, climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_COOL,
|
||||
climate::CLIMATE_MODE_HEAT}) {
|
||||
climate::CLIMATE_MODE_HEAT, climate::CLIMATE_MODE_DRY, climate::CLIMATE_MODE_FAN_ONLY}) {
|
||||
if (traits.supports_mode(mode))
|
||||
msg.supported_modes.push_back(static_cast<EnumClimateMode>(mode));
|
||||
msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
|
||||
}
|
||||
msg.visual_min_temperature = traits.get_visual_min_temperature();
|
||||
msg.visual_max_temperature = traits.get_visual_max_temperature();
|
||||
msg.visual_temperature_step = traits.get_visual_temperature_step();
|
||||
msg.supports_away = traits.get_supports_away();
|
||||
msg.supports_action = traits.get_supports_action();
|
||||
for (auto fan_mode : {climate::CLIMATE_FAN_ON, climate::CLIMATE_FAN_OFF, climate::CLIMATE_FAN_AUTO,
|
||||
climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH,
|
||||
climate::CLIMATE_FAN_MIDDLE, climate::CLIMATE_FAN_FOCUS, climate::CLIMATE_FAN_DIFFUSE}) {
|
||||
if (traits.supports_fan_mode(fan_mode))
|
||||
msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode));
|
||||
}
|
||||
for (auto swing_mode : {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_BOTH, climate::CLIMATE_SWING_VERTICAL,
|
||||
climate::CLIMATE_SWING_HORIZONTAL}) {
|
||||
if (traits.supports_swing_mode(swing_mode))
|
||||
msg.supported_swing_modes.push_back(static_cast<enums::ClimateSwingMode>(swing_mode));
|
||||
}
|
||||
return this->send_list_entities_climate_response(msg);
|
||||
}
|
||||
void APIConnection::climate_command(const ClimateCommandRequest &msg) {
|
||||
@@ -492,6 +516,10 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
|
||||
call.set_target_temperature_high(msg.target_temperature_high);
|
||||
if (msg.has_away)
|
||||
call.set_away(msg.away);
|
||||
if (msg.has_fan_mode)
|
||||
call.set_fan_mode(static_cast<climate::ClimateFanMode>(msg.fan_mode));
|
||||
if (msg.has_swing_mode)
|
||||
call.set_swing_mode(static_cast<climate::ClimateSwingMode>(msg.swing_mode));
|
||||
call.perform();
|
||||
}
|
||||
#endif
|
||||
@@ -534,8 +562,6 @@ bool APIConnection::send_log_message(int level, const char *tag, const char *lin
|
||||
if (this->log_subscription_ < level)
|
||||
return false;
|
||||
|
||||
this->set_nodelay(false);
|
||||
|
||||
// Send raw so that we don't copy too much
|
||||
auto buffer = this->create_buffer();
|
||||
// LogLevel level = 1;
|
||||
|
@@ -138,12 +138,6 @@ class APIConnection : public APIServerConnection {
|
||||
void on_timeout_(uint32_t time);
|
||||
void on_data_(uint8_t *buf, size_t len);
|
||||
void parse_recv_buffer_();
|
||||
void set_nodelay(bool nodelay) override {
|
||||
if (nodelay == this->current_nodelay_)
|
||||
return;
|
||||
this->client_->setNoDelay(nodelay);
|
||||
this->current_nodelay_ = nodelay;
|
||||
}
|
||||
|
||||
enum class ConnectionState {
|
||||
WAITING_FOR_HELLO,
|
||||
|
@@ -1,119 +1,179 @@
|
||||
// This file was automatically generated with a tool.
|
||||
// See scripts/api_protobuf/api_protobuf.py
|
||||
#include "api_pb2.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
|
||||
template<> const char *proto_enum_to_string<EnumLegacyCoverState>(EnumLegacyCoverState value) {
|
||||
template<> const char *proto_enum_to_string<enums::LegacyCoverState>(enums::LegacyCoverState value) {
|
||||
switch (value) {
|
||||
case LEGACY_COVER_STATE_OPEN:
|
||||
case enums::LEGACY_COVER_STATE_OPEN:
|
||||
return "LEGACY_COVER_STATE_OPEN";
|
||||
case LEGACY_COVER_STATE_CLOSED:
|
||||
case enums::LEGACY_COVER_STATE_CLOSED:
|
||||
return "LEGACY_COVER_STATE_CLOSED";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
template<> const char *proto_enum_to_string<EnumCoverOperation>(EnumCoverOperation value) {
|
||||
template<> const char *proto_enum_to_string<enums::CoverOperation>(enums::CoverOperation value) {
|
||||
switch (value) {
|
||||
case COVER_OPERATION_IDLE:
|
||||
case enums::COVER_OPERATION_IDLE:
|
||||
return "COVER_OPERATION_IDLE";
|
||||
case COVER_OPERATION_IS_OPENING:
|
||||
case enums::COVER_OPERATION_IS_OPENING:
|
||||
return "COVER_OPERATION_IS_OPENING";
|
||||
case COVER_OPERATION_IS_CLOSING:
|
||||
case enums::COVER_OPERATION_IS_CLOSING:
|
||||
return "COVER_OPERATION_IS_CLOSING";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
template<> const char *proto_enum_to_string<EnumLegacyCoverCommand>(EnumLegacyCoverCommand value) {
|
||||
template<> const char *proto_enum_to_string<enums::LegacyCoverCommand>(enums::LegacyCoverCommand value) {
|
||||
switch (value) {
|
||||
case LEGACY_COVER_COMMAND_OPEN:
|
||||
case enums::LEGACY_COVER_COMMAND_OPEN:
|
||||
return "LEGACY_COVER_COMMAND_OPEN";
|
||||
case LEGACY_COVER_COMMAND_CLOSE:
|
||||
case enums::LEGACY_COVER_COMMAND_CLOSE:
|
||||
return "LEGACY_COVER_COMMAND_CLOSE";
|
||||
case LEGACY_COVER_COMMAND_STOP:
|
||||
case enums::LEGACY_COVER_COMMAND_STOP:
|
||||
return "LEGACY_COVER_COMMAND_STOP";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
template<> const char *proto_enum_to_string<EnumFanSpeed>(EnumFanSpeed value) {
|
||||
template<> const char *proto_enum_to_string<enums::FanSpeed>(enums::FanSpeed value) {
|
||||
switch (value) {
|
||||
case FAN_SPEED_LOW:
|
||||
case enums::FAN_SPEED_LOW:
|
||||
return "FAN_SPEED_LOW";
|
||||
case FAN_SPEED_MEDIUM:
|
||||
case enums::FAN_SPEED_MEDIUM:
|
||||
return "FAN_SPEED_MEDIUM";
|
||||
case FAN_SPEED_HIGH:
|
||||
case enums::FAN_SPEED_HIGH:
|
||||
return "FAN_SPEED_HIGH";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
template<> const char *proto_enum_to_string<EnumLogLevel>(EnumLogLevel value) {
|
||||
template<> const char *proto_enum_to_string<enums::FanDirection>(enums::FanDirection value) {
|
||||
switch (value) {
|
||||
case LOG_LEVEL_NONE:
|
||||
case enums::FAN_DIRECTION_FORWARD:
|
||||
return "FAN_DIRECTION_FORWARD";
|
||||
case enums::FAN_DIRECTION_REVERSE:
|
||||
return "FAN_DIRECTION_REVERSE";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
template<> const char *proto_enum_to_string<enums::LogLevel>(enums::LogLevel value) {
|
||||
switch (value) {
|
||||
case enums::LOG_LEVEL_NONE:
|
||||
return "LOG_LEVEL_NONE";
|
||||
case LOG_LEVEL_ERROR:
|
||||
case enums::LOG_LEVEL_ERROR:
|
||||
return "LOG_LEVEL_ERROR";
|
||||
case LOG_LEVEL_WARN:
|
||||
case enums::LOG_LEVEL_WARN:
|
||||
return "LOG_LEVEL_WARN";
|
||||
case LOG_LEVEL_INFO:
|
||||
case enums::LOG_LEVEL_INFO:
|
||||
return "LOG_LEVEL_INFO";
|
||||
case LOG_LEVEL_DEBUG:
|
||||
case enums::LOG_LEVEL_DEBUG:
|
||||
return "LOG_LEVEL_DEBUG";
|
||||
case LOG_LEVEL_VERBOSE:
|
||||
case enums::LOG_LEVEL_VERBOSE:
|
||||
return "LOG_LEVEL_VERBOSE";
|
||||
case LOG_LEVEL_VERY_VERBOSE:
|
||||
case enums::LOG_LEVEL_VERY_VERBOSE:
|
||||
return "LOG_LEVEL_VERY_VERBOSE";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
template<> const char *proto_enum_to_string<EnumServiceArgType>(EnumServiceArgType value) {
|
||||
template<> const char *proto_enum_to_string<enums::ServiceArgType>(enums::ServiceArgType value) {
|
||||
switch (value) {
|
||||
case SERVICE_ARG_TYPE_BOOL:
|
||||
case enums::SERVICE_ARG_TYPE_BOOL:
|
||||
return "SERVICE_ARG_TYPE_BOOL";
|
||||
case SERVICE_ARG_TYPE_INT:
|
||||
case enums::SERVICE_ARG_TYPE_INT:
|
||||
return "SERVICE_ARG_TYPE_INT";
|
||||
case SERVICE_ARG_TYPE_FLOAT:
|
||||
case enums::SERVICE_ARG_TYPE_FLOAT:
|
||||
return "SERVICE_ARG_TYPE_FLOAT";
|
||||
case SERVICE_ARG_TYPE_STRING:
|
||||
case enums::SERVICE_ARG_TYPE_STRING:
|
||||
return "SERVICE_ARG_TYPE_STRING";
|
||||
case SERVICE_ARG_TYPE_BOOL_ARRAY:
|
||||
case enums::SERVICE_ARG_TYPE_BOOL_ARRAY:
|
||||
return "SERVICE_ARG_TYPE_BOOL_ARRAY";
|
||||
case SERVICE_ARG_TYPE_INT_ARRAY:
|
||||
case enums::SERVICE_ARG_TYPE_INT_ARRAY:
|
||||
return "SERVICE_ARG_TYPE_INT_ARRAY";
|
||||
case SERVICE_ARG_TYPE_FLOAT_ARRAY:
|
||||
case enums::SERVICE_ARG_TYPE_FLOAT_ARRAY:
|
||||
return "SERVICE_ARG_TYPE_FLOAT_ARRAY";
|
||||
case SERVICE_ARG_TYPE_STRING_ARRAY:
|
||||
case enums::SERVICE_ARG_TYPE_STRING_ARRAY:
|
||||
return "SERVICE_ARG_TYPE_STRING_ARRAY";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
template<> const char *proto_enum_to_string<EnumClimateMode>(EnumClimateMode value) {
|
||||
template<> const char *proto_enum_to_string<enums::ClimateMode>(enums::ClimateMode value) {
|
||||
switch (value) {
|
||||
case CLIMATE_MODE_OFF:
|
||||
case enums::CLIMATE_MODE_OFF:
|
||||
return "CLIMATE_MODE_OFF";
|
||||
case CLIMATE_MODE_AUTO:
|
||||
case enums::CLIMATE_MODE_AUTO:
|
||||
return "CLIMATE_MODE_AUTO";
|
||||
case CLIMATE_MODE_COOL:
|
||||
case enums::CLIMATE_MODE_COOL:
|
||||
return "CLIMATE_MODE_COOL";
|
||||
case CLIMATE_MODE_HEAT:
|
||||
case enums::CLIMATE_MODE_HEAT:
|
||||
return "CLIMATE_MODE_HEAT";
|
||||
case enums::CLIMATE_MODE_FAN_ONLY:
|
||||
return "CLIMATE_MODE_FAN_ONLY";
|
||||
case enums::CLIMATE_MODE_DRY:
|
||||
return "CLIMATE_MODE_DRY";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
template<> const char *proto_enum_to_string<EnumClimateAction>(EnumClimateAction value) {
|
||||
template<> const char *proto_enum_to_string<enums::ClimateFanMode>(enums::ClimateFanMode value) {
|
||||
switch (value) {
|
||||
case CLIMATE_ACTION_OFF:
|
||||
case enums::CLIMATE_FAN_ON:
|
||||
return "CLIMATE_FAN_ON";
|
||||
case enums::CLIMATE_FAN_OFF:
|
||||
return "CLIMATE_FAN_OFF";
|
||||
case enums::CLIMATE_FAN_AUTO:
|
||||
return "CLIMATE_FAN_AUTO";
|
||||
case enums::CLIMATE_FAN_LOW:
|
||||
return "CLIMATE_FAN_LOW";
|
||||
case enums::CLIMATE_FAN_MEDIUM:
|
||||
return "CLIMATE_FAN_MEDIUM";
|
||||
case enums::CLIMATE_FAN_HIGH:
|
||||
return "CLIMATE_FAN_HIGH";
|
||||
case enums::CLIMATE_FAN_MIDDLE:
|
||||
return "CLIMATE_FAN_MIDDLE";
|
||||
case enums::CLIMATE_FAN_FOCUS:
|
||||
return "CLIMATE_FAN_FOCUS";
|
||||
case enums::CLIMATE_FAN_DIFFUSE:
|
||||
return "CLIMATE_FAN_DIFFUSE";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
template<> const char *proto_enum_to_string<enums::ClimateSwingMode>(enums::ClimateSwingMode value) {
|
||||
switch (value) {
|
||||
case enums::CLIMATE_SWING_OFF:
|
||||
return "CLIMATE_SWING_OFF";
|
||||
case enums::CLIMATE_SWING_BOTH:
|
||||
return "CLIMATE_SWING_BOTH";
|
||||
case enums::CLIMATE_SWING_VERTICAL:
|
||||
return "CLIMATE_SWING_VERTICAL";
|
||||
case enums::CLIMATE_SWINT_HORIZONTAL:
|
||||
return "CLIMATE_SWINT_HORIZONTAL";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
template<> const char *proto_enum_to_string<enums::ClimateAction>(enums::ClimateAction value) {
|
||||
switch (value) {
|
||||
case enums::CLIMATE_ACTION_OFF:
|
||||
return "CLIMATE_ACTION_OFF";
|
||||
case CLIMATE_ACTION_COOLING:
|
||||
case enums::CLIMATE_ACTION_COOLING:
|
||||
return "CLIMATE_ACTION_COOLING";
|
||||
case CLIMATE_ACTION_HEATING:
|
||||
case enums::CLIMATE_ACTION_HEATING:
|
||||
return "CLIMATE_ACTION_HEATING";
|
||||
case enums::CLIMATE_ACTION_IDLE:
|
||||
return "CLIMATE_ACTION_IDLE";
|
||||
case enums::CLIMATE_ACTION_DRYING:
|
||||
return "CLIMATE_ACTION_DRYING";
|
||||
case enums::CLIMATE_ACTION_FAN:
|
||||
return "CLIMATE_ACTION_FAN";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
@@ -404,6 +464,10 @@ bool BinarySensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt val
|
||||
this->state = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 3: {
|
||||
this->missing_state = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -421,6 +485,7 @@ bool BinarySensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value
|
||||
void BinarySensorStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_bool(2, this->state);
|
||||
buffer.encode_bool(3, this->missing_state);
|
||||
}
|
||||
void BinarySensorStateResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
@@ -433,6 +498,10 @@ void BinarySensorStateResponse::dump_to(std::string &out) const {
|
||||
out.append(" state: ");
|
||||
out.append(YESNO(this->state));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" missing_state: ");
|
||||
out.append(YESNO(this->missing_state));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
bool ListEntitiesCoverResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
@@ -535,11 +604,11 @@ void ListEntitiesCoverResponse::dump_to(std::string &out) const {
|
||||
bool CoverStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
this->legacy_state = value.as_enum<EnumLegacyCoverState>();
|
||||
this->legacy_state = value.as_enum<enums::LegacyCoverState>();
|
||||
return true;
|
||||
}
|
||||
case 5: {
|
||||
this->current_operation = value.as_enum<EnumCoverOperation>();
|
||||
this->current_operation = value.as_enum<enums::CoverOperation>();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
@@ -566,10 +635,10 @@ bool CoverStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
}
|
||||
void CoverStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_enum<EnumLegacyCoverState>(2, this->legacy_state);
|
||||
buffer.encode_enum<enums::LegacyCoverState>(2, this->legacy_state);
|
||||
buffer.encode_float(3, this->position);
|
||||
buffer.encode_float(4, this->tilt);
|
||||
buffer.encode_enum<EnumCoverOperation>(5, this->current_operation);
|
||||
buffer.encode_enum<enums::CoverOperation>(5, this->current_operation);
|
||||
}
|
||||
void CoverStateResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
@@ -580,7 +649,7 @@ void CoverStateResponse::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
|
||||
out.append(" legacy_state: ");
|
||||
out.append(proto_enum_to_string<EnumLegacyCoverState>(this->legacy_state));
|
||||
out.append(proto_enum_to_string<enums::LegacyCoverState>(this->legacy_state));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" position: ");
|
||||
@@ -594,7 +663,7 @@ void CoverStateResponse::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
|
||||
out.append(" current_operation: ");
|
||||
out.append(proto_enum_to_string<EnumCoverOperation>(this->current_operation));
|
||||
out.append(proto_enum_to_string<enums::CoverOperation>(this->current_operation));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
@@ -605,7 +674,7 @@ bool CoverCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
return true;
|
||||
}
|
||||
case 3: {
|
||||
this->legacy_command = value.as_enum<EnumLegacyCoverCommand>();
|
||||
this->legacy_command = value.as_enum<enums::LegacyCoverCommand>();
|
||||
return true;
|
||||
}
|
||||
case 4: {
|
||||
@@ -645,7 +714,7 @@ bool CoverCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
void CoverCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_bool(2, this->has_legacy_command);
|
||||
buffer.encode_enum<EnumLegacyCoverCommand>(3, this->legacy_command);
|
||||
buffer.encode_enum<enums::LegacyCoverCommand>(3, this->legacy_command);
|
||||
buffer.encode_bool(4, this->has_position);
|
||||
buffer.encode_float(5, this->position);
|
||||
buffer.encode_bool(6, this->has_tilt);
|
||||
@@ -665,7 +734,7 @@ void CoverCommandRequest::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
|
||||
out.append(" legacy_command: ");
|
||||
out.append(proto_enum_to_string<EnumLegacyCoverCommand>(this->legacy_command));
|
||||
out.append(proto_enum_to_string<enums::LegacyCoverCommand>(this->legacy_command));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" has_position: ");
|
||||
@@ -701,6 +770,10 @@ bool ListEntitiesFanResponse::decode_varint(uint32_t field_id, ProtoVarInt value
|
||||
this->supports_speed = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 7: {
|
||||
this->supports_direction = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -740,6 +813,7 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(4, this->unique_id);
|
||||
buffer.encode_bool(5, this->supports_oscillation);
|
||||
buffer.encode_bool(6, this->supports_speed);
|
||||
buffer.encode_bool(7, this->supports_direction);
|
||||
}
|
||||
void ListEntitiesFanResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
@@ -768,6 +842,10 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const {
|
||||
out.append(" supports_speed: ");
|
||||
out.append(YESNO(this->supports_speed));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" supports_direction: ");
|
||||
out.append(YESNO(this->supports_direction));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
bool FanStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
@@ -781,7 +859,11 @@ bool FanStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
return true;
|
||||
}
|
||||
case 4: {
|
||||
this->speed = value.as_enum<EnumFanSpeed>();
|
||||
this->speed = value.as_enum<enums::FanSpeed>();
|
||||
return true;
|
||||
}
|
||||
case 5: {
|
||||
this->direction = value.as_enum<enums::FanDirection>();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
@@ -802,7 +884,8 @@ void FanStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_bool(2, this->state);
|
||||
buffer.encode_bool(3, this->oscillating);
|
||||
buffer.encode_enum<EnumFanSpeed>(4, this->speed);
|
||||
buffer.encode_enum<enums::FanSpeed>(4, this->speed);
|
||||
buffer.encode_enum<enums::FanDirection>(5, this->direction);
|
||||
}
|
||||
void FanStateResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
@@ -821,7 +904,11 @@ void FanStateResponse::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
|
||||
out.append(" speed: ");
|
||||
out.append(proto_enum_to_string<EnumFanSpeed>(this->speed));
|
||||
out.append(proto_enum_to_string<enums::FanSpeed>(this->speed));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" direction: ");
|
||||
out.append(proto_enum_to_string<enums::FanDirection>(this->direction));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
@@ -840,7 +927,7 @@ bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
return true;
|
||||
}
|
||||
case 5: {
|
||||
this->speed = value.as_enum<EnumFanSpeed>();
|
||||
this->speed = value.as_enum<enums::FanSpeed>();
|
||||
return true;
|
||||
}
|
||||
case 6: {
|
||||
@@ -851,6 +938,14 @@ bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
this->oscillating = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 8: {
|
||||
this->has_direction = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 9: {
|
||||
this->direction = value.as_enum<enums::FanDirection>();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -870,9 +965,11 @@ void FanCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_bool(2, this->has_state);
|
||||
buffer.encode_bool(3, this->state);
|
||||
buffer.encode_bool(4, this->has_speed);
|
||||
buffer.encode_enum<EnumFanSpeed>(5, this->speed);
|
||||
buffer.encode_enum<enums::FanSpeed>(5, this->speed);
|
||||
buffer.encode_bool(6, this->has_oscillating);
|
||||
buffer.encode_bool(7, this->oscillating);
|
||||
buffer.encode_bool(8, this->has_direction);
|
||||
buffer.encode_enum<enums::FanDirection>(9, this->direction);
|
||||
}
|
||||
void FanCommandRequest::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
@@ -895,7 +992,7 @@ void FanCommandRequest::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
|
||||
out.append(" speed: ");
|
||||
out.append(proto_enum_to_string<EnumFanSpeed>(this->speed));
|
||||
out.append(proto_enum_to_string<enums::FanSpeed>(this->speed));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" has_oscillating: ");
|
||||
@@ -905,6 +1002,14 @@ void FanCommandRequest::dump_to(std::string &out) const {
|
||||
out.append(" oscillating: ");
|
||||
out.append(YESNO(this->oscillating));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" has_direction: ");
|
||||
out.append(YESNO(this->has_direction));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" direction: ");
|
||||
out.append(proto_enum_to_string<enums::FanDirection>(this->direction));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
bool ListEntitiesLightResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
@@ -1451,6 +1556,16 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
bool SensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 3: {
|
||||
this->missing_state = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool SensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
@@ -1468,6 +1583,7 @@ bool SensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
void SensorStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_float(2, this->state);
|
||||
buffer.encode_bool(3, this->missing_state);
|
||||
}
|
||||
void SensorStateResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
@@ -1481,6 +1597,10 @@ void SensorStateResponse::dump_to(std::string &out) const {
|
||||
sprintf(buffer, "%g", this->state);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" missing_state: ");
|
||||
out.append(YESNO(this->missing_state));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
bool ListEntitiesSwitchResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
@@ -1700,6 +1820,16 @@ void ListEntitiesTextSensorResponse::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
bool TextSensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 3: {
|
||||
this->missing_state = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool TextSensorStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
@@ -1723,6 +1853,7 @@ bool TextSensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value)
|
||||
void TextSensorStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_string(2, this->state);
|
||||
buffer.encode_bool(3, this->missing_state);
|
||||
}
|
||||
void TextSensorStateResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
@@ -1735,12 +1866,16 @@ void TextSensorStateResponse::dump_to(std::string &out) const {
|
||||
out.append(" state: ");
|
||||
out.append("'").append(this->state).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" missing_state: ");
|
||||
out.append(YESNO(this->missing_state));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
bool SubscribeLogsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->level = value.as_enum<EnumLogLevel>();
|
||||
this->level = value.as_enum<enums::LogLevel>();
|
||||
return true;
|
||||
}
|
||||
case 2: {
|
||||
@@ -1752,14 +1887,14 @@ bool SubscribeLogsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
}
|
||||
}
|
||||
void SubscribeLogsRequest::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_enum<EnumLogLevel>(1, this->level);
|
||||
buffer.encode_enum<enums::LogLevel>(1, this->level);
|
||||
buffer.encode_bool(2, this->dump_config);
|
||||
}
|
||||
void SubscribeLogsRequest::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("SubscribeLogsRequest {\n");
|
||||
out.append(" level: ");
|
||||
out.append(proto_enum_to_string<EnumLogLevel>(this->level));
|
||||
out.append(proto_enum_to_string<enums::LogLevel>(this->level));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" dump_config: ");
|
||||
@@ -1770,7 +1905,7 @@ void SubscribeLogsRequest::dump_to(std::string &out) const {
|
||||
bool SubscribeLogsResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->level = value.as_enum<EnumLogLevel>();
|
||||
this->level = value.as_enum<enums::LogLevel>();
|
||||
return true;
|
||||
}
|
||||
case 4: {
|
||||
@@ -1796,7 +1931,7 @@ bool SubscribeLogsResponse::decode_length(uint32_t field_id, ProtoLengthDelimite
|
||||
}
|
||||
}
|
||||
void SubscribeLogsResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_enum<EnumLogLevel>(1, this->level);
|
||||
buffer.encode_enum<enums::LogLevel>(1, this->level);
|
||||
buffer.encode_string(2, this->tag);
|
||||
buffer.encode_string(3, this->message);
|
||||
buffer.encode_bool(4, this->send_failed);
|
||||
@@ -1805,7 +1940,7 @@ void SubscribeLogsResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("SubscribeLogsResponse {\n");
|
||||
out.append(" level: ");
|
||||
out.append(proto_enum_to_string<EnumLogLevel>(this->level));
|
||||
out.append(proto_enum_to_string<enums::LogLevel>(this->level));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" tag: ");
|
||||
@@ -2010,7 +2145,7 @@ void GetTimeResponse::dump_to(std::string &out) const {
|
||||
bool ListEntitiesServicesArgument::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
this->type = value.as_enum<EnumServiceArgType>();
|
||||
this->type = value.as_enum<enums::ServiceArgType>();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
@@ -2029,7 +2164,7 @@ bool ListEntitiesServicesArgument::decode_length(uint32_t field_id, ProtoLengthD
|
||||
}
|
||||
void ListEntitiesServicesArgument::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(1, this->name);
|
||||
buffer.encode_enum<EnumServiceArgType>(2, this->type);
|
||||
buffer.encode_enum<enums::ServiceArgType>(2, this->type);
|
||||
}
|
||||
void ListEntitiesServicesArgument::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
@@ -2039,7 +2174,7 @@ void ListEntitiesServicesArgument::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
|
||||
out.append(" type: ");
|
||||
out.append(proto_enum_to_string<EnumServiceArgType>(this->type));
|
||||
out.append(proto_enum_to_string<enums::ServiceArgType>(this->type));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
@@ -2408,7 +2543,7 @@ bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt v
|
||||
return true;
|
||||
}
|
||||
case 7: {
|
||||
this->supported_modes.push_back(value.as_enum<EnumClimateMode>());
|
||||
this->supported_modes.push_back(value.as_enum<enums::ClimateMode>());
|
||||
return true;
|
||||
}
|
||||
case 11: {
|
||||
@@ -2419,6 +2554,14 @@ bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt v
|
||||
this->supports_action = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 13: {
|
||||
this->supported_fan_modes.push_back(value.as_enum<enums::ClimateFanMode>());
|
||||
return true;
|
||||
}
|
||||
case 14: {
|
||||
this->supported_swing_modes.push_back(value.as_enum<enums::ClimateSwingMode>());
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -2471,13 +2614,19 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_bool(5, this->supports_current_temperature);
|
||||
buffer.encode_bool(6, this->supports_two_point_target_temperature);
|
||||
for (auto &it : this->supported_modes) {
|
||||
buffer.encode_enum<EnumClimateMode>(7, it, true);
|
||||
buffer.encode_enum<enums::ClimateMode>(7, it, true);
|
||||
}
|
||||
buffer.encode_float(8, this->visual_min_temperature);
|
||||
buffer.encode_float(9, this->visual_max_temperature);
|
||||
buffer.encode_float(10, this->visual_temperature_step);
|
||||
buffer.encode_bool(11, this->supports_away);
|
||||
buffer.encode_bool(12, this->supports_action);
|
||||
for (auto &it : this->supported_fan_modes) {
|
||||
buffer.encode_enum<enums::ClimateFanMode>(13, it, true);
|
||||
}
|
||||
for (auto &it : this->supported_swing_modes) {
|
||||
buffer.encode_enum<enums::ClimateSwingMode>(14, it, true);
|
||||
}
|
||||
}
|
||||
void ListEntitiesClimateResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
@@ -2509,7 +2658,7 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
|
||||
|
||||
for (const auto &it : this->supported_modes) {
|
||||
out.append(" supported_modes: ");
|
||||
out.append(proto_enum_to_string<EnumClimateMode>(it));
|
||||
out.append(proto_enum_to_string<enums::ClimateMode>(it));
|
||||
out.append("\n");
|
||||
}
|
||||
|
||||
@@ -2535,12 +2684,24 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
|
||||
out.append(" supports_action: ");
|
||||
out.append(YESNO(this->supports_action));
|
||||
out.append("\n");
|
||||
|
||||
for (const auto &it : this->supported_fan_modes) {
|
||||
out.append(" supported_fan_modes: ");
|
||||
out.append(proto_enum_to_string<enums::ClimateFanMode>(it));
|
||||
out.append("\n");
|
||||
}
|
||||
|
||||
for (const auto &it : this->supported_swing_modes) {
|
||||
out.append(" supported_swing_modes: ");
|
||||
out.append(proto_enum_to_string<enums::ClimateSwingMode>(it));
|
||||
out.append("\n");
|
||||
}
|
||||
out.append("}");
|
||||
}
|
||||
bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
this->mode = value.as_enum<EnumClimateMode>();
|
||||
this->mode = value.as_enum<enums::ClimateMode>();
|
||||
return true;
|
||||
}
|
||||
case 7: {
|
||||
@@ -2548,7 +2709,15 @@ bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
return true;
|
||||
}
|
||||
case 8: {
|
||||
this->action = value.as_enum<EnumClimateAction>();
|
||||
this->action = value.as_enum<enums::ClimateAction>();
|
||||
return true;
|
||||
}
|
||||
case 9: {
|
||||
this->fan_mode = value.as_enum<enums::ClimateFanMode>();
|
||||
return true;
|
||||
}
|
||||
case 10: {
|
||||
this->swing_mode = value.as_enum<enums::ClimateSwingMode>();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
@@ -2583,13 +2752,15 @@ bool ClimateStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
}
|
||||
void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_enum<EnumClimateMode>(2, this->mode);
|
||||
buffer.encode_enum<enums::ClimateMode>(2, this->mode);
|
||||
buffer.encode_float(3, this->current_temperature);
|
||||
buffer.encode_float(4, this->target_temperature);
|
||||
buffer.encode_float(5, this->target_temperature_low);
|
||||
buffer.encode_float(6, this->target_temperature_high);
|
||||
buffer.encode_bool(7, this->away);
|
||||
buffer.encode_enum<EnumClimateAction>(8, this->action);
|
||||
buffer.encode_enum<enums::ClimateAction>(8, this->action);
|
||||
buffer.encode_enum<enums::ClimateFanMode>(9, this->fan_mode);
|
||||
buffer.encode_enum<enums::ClimateSwingMode>(10, this->swing_mode);
|
||||
}
|
||||
void ClimateStateResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
@@ -2600,7 +2771,7 @@ void ClimateStateResponse::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
|
||||
out.append(" mode: ");
|
||||
out.append(proto_enum_to_string<EnumClimateMode>(this->mode));
|
||||
out.append(proto_enum_to_string<enums::ClimateMode>(this->mode));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" current_temperature: ");
|
||||
@@ -2628,7 +2799,15 @@ void ClimateStateResponse::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
|
||||
out.append(" action: ");
|
||||
out.append(proto_enum_to_string<EnumClimateAction>(this->action));
|
||||
out.append(proto_enum_to_string<enums::ClimateAction>(this->action));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" fan_mode: ");
|
||||
out.append(proto_enum_to_string<enums::ClimateFanMode>(this->fan_mode));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" swing_mode: ");
|
||||
out.append(proto_enum_to_string<enums::ClimateSwingMode>(this->swing_mode));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
@@ -2639,7 +2818,7 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
|
||||
return true;
|
||||
}
|
||||
case 3: {
|
||||
this->mode = value.as_enum<EnumClimateMode>();
|
||||
this->mode = value.as_enum<enums::ClimateMode>();
|
||||
return true;
|
||||
}
|
||||
case 4: {
|
||||
@@ -2662,6 +2841,22 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
|
||||
this->away = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 12: {
|
||||
this->has_fan_mode = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 13: {
|
||||
this->fan_mode = value.as_enum<enums::ClimateFanMode>();
|
||||
return true;
|
||||
}
|
||||
case 14: {
|
||||
this->has_swing_mode = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 15: {
|
||||
this->swing_mode = value.as_enum<enums::ClimateSwingMode>();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -2691,7 +2886,7 @@ bool ClimateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_bool(2, this->has_mode);
|
||||
buffer.encode_enum<EnumClimateMode>(3, this->mode);
|
||||
buffer.encode_enum<enums::ClimateMode>(3, this->mode);
|
||||
buffer.encode_bool(4, this->has_target_temperature);
|
||||
buffer.encode_float(5, this->target_temperature);
|
||||
buffer.encode_bool(6, this->has_target_temperature_low);
|
||||
@@ -2700,6 +2895,10 @@ void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_float(9, this->target_temperature_high);
|
||||
buffer.encode_bool(10, this->has_away);
|
||||
buffer.encode_bool(11, this->away);
|
||||
buffer.encode_bool(12, this->has_fan_mode);
|
||||
buffer.encode_enum<enums::ClimateFanMode>(13, this->fan_mode);
|
||||
buffer.encode_bool(14, this->has_swing_mode);
|
||||
buffer.encode_enum<enums::ClimateSwingMode>(15, this->swing_mode);
|
||||
}
|
||||
void ClimateCommandRequest::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
@@ -2714,7 +2913,7 @@ void ClimateCommandRequest::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
|
||||
out.append(" mode: ");
|
||||
out.append(proto_enum_to_string<EnumClimateMode>(this->mode));
|
||||
out.append(proto_enum_to_string<enums::ClimateMode>(this->mode));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" has_target_temperature: ");
|
||||
@@ -2751,6 +2950,22 @@ void ClimateCommandRequest::dump_to(std::string &out) const {
|
||||
out.append(" away: ");
|
||||
out.append(YESNO(this->away));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" has_fan_mode: ");
|
||||
out.append(YESNO(this->has_fan_mode));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" fan_mode: ");
|
||||
out.append(proto_enum_to_string<enums::ClimateFanMode>(this->fan_mode));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" has_swing_mode: ");
|
||||
out.append(YESNO(this->has_swing_mode));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" swing_mode: ");
|
||||
out.append(proto_enum_to_string<enums::ClimateSwingMode>(this->swing_mode));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
// This file was automatically generated with a tool.
|
||||
// See scripts/api_protobuf/api_protobuf.py
|
||||
#pragma once
|
||||
|
||||
#include "proto.h"
|
||||
@@ -5,26 +7,32 @@
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
|
||||
enum EnumLegacyCoverState : uint32_t {
|
||||
namespace enums {
|
||||
|
||||
enum LegacyCoverState : uint32_t {
|
||||
LEGACY_COVER_STATE_OPEN = 0,
|
||||
LEGACY_COVER_STATE_CLOSED = 1,
|
||||
};
|
||||
enum EnumCoverOperation : uint32_t {
|
||||
enum CoverOperation : uint32_t {
|
||||
COVER_OPERATION_IDLE = 0,
|
||||
COVER_OPERATION_IS_OPENING = 1,
|
||||
COVER_OPERATION_IS_CLOSING = 2,
|
||||
};
|
||||
enum EnumLegacyCoverCommand : uint32_t {
|
||||
enum LegacyCoverCommand : uint32_t {
|
||||
LEGACY_COVER_COMMAND_OPEN = 0,
|
||||
LEGACY_COVER_COMMAND_CLOSE = 1,
|
||||
LEGACY_COVER_COMMAND_STOP = 2,
|
||||
};
|
||||
enum EnumFanSpeed : uint32_t {
|
||||
enum FanSpeed : uint32_t {
|
||||
FAN_SPEED_LOW = 0,
|
||||
FAN_SPEED_MEDIUM = 1,
|
||||
FAN_SPEED_HIGH = 2,
|
||||
};
|
||||
enum EnumLogLevel : uint32_t {
|
||||
enum FanDirection : uint32_t {
|
||||
FAN_DIRECTION_FORWARD = 0,
|
||||
FAN_DIRECTION_REVERSE = 1,
|
||||
};
|
||||
enum LogLevel : uint32_t {
|
||||
LOG_LEVEL_NONE = 0,
|
||||
LOG_LEVEL_ERROR = 1,
|
||||
LOG_LEVEL_WARN = 2,
|
||||
@@ -33,7 +41,7 @@ enum EnumLogLevel : uint32_t {
|
||||
LOG_LEVEL_VERBOSE = 5,
|
||||
LOG_LEVEL_VERY_VERBOSE = 6,
|
||||
};
|
||||
enum EnumServiceArgType : uint32_t {
|
||||
enum ServiceArgType : uint32_t {
|
||||
SERVICE_ARG_TYPE_BOOL = 0,
|
||||
SERVICE_ARG_TYPE_INT = 1,
|
||||
SERVICE_ARG_TYPE_FLOAT = 2,
|
||||
@@ -43,17 +51,42 @@ enum EnumServiceArgType : uint32_t {
|
||||
SERVICE_ARG_TYPE_FLOAT_ARRAY = 6,
|
||||
SERVICE_ARG_TYPE_STRING_ARRAY = 7,
|
||||
};
|
||||
enum EnumClimateMode : uint32_t {
|
||||
enum ClimateMode : uint32_t {
|
||||
CLIMATE_MODE_OFF = 0,
|
||||
CLIMATE_MODE_AUTO = 1,
|
||||
CLIMATE_MODE_COOL = 2,
|
||||
CLIMATE_MODE_HEAT = 3,
|
||||
CLIMATE_MODE_FAN_ONLY = 4,
|
||||
CLIMATE_MODE_DRY = 5,
|
||||
};
|
||||
enum EnumClimateAction : uint32_t {
|
||||
enum ClimateFanMode : uint32_t {
|
||||
CLIMATE_FAN_ON = 0,
|
||||
CLIMATE_FAN_OFF = 1,
|
||||
CLIMATE_FAN_AUTO = 2,
|
||||
CLIMATE_FAN_LOW = 3,
|
||||
CLIMATE_FAN_MEDIUM = 4,
|
||||
CLIMATE_FAN_HIGH = 5,
|
||||
CLIMATE_FAN_MIDDLE = 6,
|
||||
CLIMATE_FAN_FOCUS = 7,
|
||||
CLIMATE_FAN_DIFFUSE = 8,
|
||||
};
|
||||
enum ClimateSwingMode : uint32_t {
|
||||
CLIMATE_SWING_OFF = 0,
|
||||
CLIMATE_SWING_BOTH = 1,
|
||||
CLIMATE_SWING_VERTICAL = 2,
|
||||
CLIMATE_SWINT_HORIZONTAL = 3,
|
||||
};
|
||||
enum ClimateAction : uint32_t {
|
||||
CLIMATE_ACTION_OFF = 0,
|
||||
CLIMATE_ACTION_COOLING = 2,
|
||||
CLIMATE_ACTION_HEATING = 3,
|
||||
CLIMATE_ACTION_IDLE = 4,
|
||||
CLIMATE_ACTION_DRYING = 5,
|
||||
CLIMATE_ACTION_FAN = 6,
|
||||
};
|
||||
|
||||
} // namespace enums
|
||||
|
||||
class HelloRequest : public ProtoMessage {
|
||||
public:
|
||||
std::string client_info{}; // NOLINT
|
||||
@@ -183,8 +216,9 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage {
|
||||
};
|
||||
class BinarySensorStateResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
bool state{false}; // NOLINT
|
||||
uint32_t key{0}; // NOLINT
|
||||
bool state{false}; // NOLINT
|
||||
bool missing_state{false}; // NOLINT
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void dump_to(std::string &out) const override;
|
||||
|
||||
@@ -212,11 +246,11 @@ class ListEntitiesCoverResponse : public ProtoMessage {
|
||||
};
|
||||
class CoverStateResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
EnumLegacyCoverState legacy_state{}; // NOLINT
|
||||
float position{0.0f}; // NOLINT
|
||||
float tilt{0.0f}; // NOLINT
|
||||
EnumCoverOperation current_operation{}; // NOLINT
|
||||
uint32_t key{0}; // NOLINT
|
||||
enums::LegacyCoverState legacy_state{}; // NOLINT
|
||||
float position{0.0f}; // NOLINT
|
||||
float tilt{0.0f}; // NOLINT
|
||||
enums::CoverOperation current_operation{}; // NOLINT
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void dump_to(std::string &out) const override;
|
||||
|
||||
@@ -226,14 +260,14 @@ class CoverStateResponse : public ProtoMessage {
|
||||
};
|
||||
class CoverCommandRequest : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
bool has_legacy_command{false}; // NOLINT
|
||||
EnumLegacyCoverCommand legacy_command{}; // NOLINT
|
||||
bool has_position{false}; // NOLINT
|
||||
float position{0.0f}; // NOLINT
|
||||
bool has_tilt{false}; // NOLINT
|
||||
float tilt{0.0f}; // NOLINT
|
||||
bool stop{false}; // NOLINT
|
||||
uint32_t key{0}; // NOLINT
|
||||
bool has_legacy_command{false}; // NOLINT
|
||||
enums::LegacyCoverCommand legacy_command{}; // NOLINT
|
||||
bool has_position{false}; // NOLINT
|
||||
float position{0.0f}; // NOLINT
|
||||
bool has_tilt{false}; // NOLINT
|
||||
float tilt{0.0f}; // NOLINT
|
||||
bool stop{false}; // NOLINT
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void dump_to(std::string &out) const override;
|
||||
|
||||
@@ -249,6 +283,7 @@ class ListEntitiesFanResponse : public ProtoMessage {
|
||||
std::string unique_id{}; // NOLINT
|
||||
bool supports_oscillation{false}; // NOLINT
|
||||
bool supports_speed{false}; // NOLINT
|
||||
bool supports_direction{false}; // NOLINT
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void dump_to(std::string &out) const override;
|
||||
|
||||
@@ -259,10 +294,11 @@ class ListEntitiesFanResponse : public ProtoMessage {
|
||||
};
|
||||
class FanStateResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
bool state{false}; // NOLINT
|
||||
bool oscillating{false}; // NOLINT
|
||||
EnumFanSpeed speed{}; // NOLINT
|
||||
uint32_t key{0}; // NOLINT
|
||||
bool state{false}; // NOLINT
|
||||
bool oscillating{false}; // NOLINT
|
||||
enums::FanSpeed speed{}; // NOLINT
|
||||
enums::FanDirection direction{}; // NOLINT
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void dump_to(std::string &out) const override;
|
||||
|
||||
@@ -272,13 +308,15 @@ class FanStateResponse : public ProtoMessage {
|
||||
};
|
||||
class FanCommandRequest : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
bool has_state{false}; // NOLINT
|
||||
bool state{false}; // NOLINT
|
||||
bool has_speed{false}; // NOLINT
|
||||
EnumFanSpeed speed{}; // NOLINT
|
||||
bool has_oscillating{false}; // NOLINT
|
||||
bool oscillating{false}; // NOLINT
|
||||
uint32_t key{0}; // NOLINT
|
||||
bool has_state{false}; // NOLINT
|
||||
bool state{false}; // NOLINT
|
||||
bool has_speed{false}; // NOLINT
|
||||
enums::FanSpeed speed{}; // NOLINT
|
||||
bool has_oscillating{false}; // NOLINT
|
||||
bool oscillating{false}; // NOLINT
|
||||
bool has_direction{false}; // NOLINT
|
||||
enums::FanDirection direction{}; // NOLINT
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void dump_to(std::string &out) const override;
|
||||
|
||||
@@ -375,13 +413,15 @@ class ListEntitiesSensorResponse : public ProtoMessage {
|
||||
};
|
||||
class SensorStateResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
float state{0.0f}; // NOLINT
|
||||
uint32_t key{0}; // NOLINT
|
||||
float state{0.0f}; // NOLINT
|
||||
bool missing_state{false}; // NOLINT
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void dump_to(std::string &out) const override;
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class ListEntitiesSwitchResponse : public ProtoMessage {
|
||||
public:
|
||||
@@ -437,18 +477,20 @@ class ListEntitiesTextSensorResponse : public ProtoMessage {
|
||||
};
|
||||
class TextSensorStateResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
std::string state{}; // NOLINT
|
||||
uint32_t key{0}; // NOLINT
|
||||
std::string state{}; // NOLINT
|
||||
bool missing_state{false}; // NOLINT
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void dump_to(std::string &out) const override;
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class SubscribeLogsRequest : public ProtoMessage {
|
||||
public:
|
||||
EnumLogLevel level{}; // NOLINT
|
||||
enums::LogLevel level{}; // NOLINT
|
||||
bool dump_config{false}; // NOLINT
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void dump_to(std::string &out) const override;
|
||||
@@ -458,7 +500,7 @@ class SubscribeLogsRequest : public ProtoMessage {
|
||||
};
|
||||
class SubscribeLogsResponse : public ProtoMessage {
|
||||
public:
|
||||
EnumLogLevel level{}; // NOLINT
|
||||
enums::LogLevel level{}; // NOLINT
|
||||
std::string tag{}; // NOLINT
|
||||
std::string message{}; // NOLINT
|
||||
bool send_failed{false}; // NOLINT
|
||||
@@ -544,8 +586,8 @@ class GetTimeResponse : public ProtoMessage {
|
||||
};
|
||||
class ListEntitiesServicesArgument : public ProtoMessage {
|
||||
public:
|
||||
std::string name{}; // NOLINT
|
||||
EnumServiceArgType type{}; // NOLINT
|
||||
std::string name{}; // NOLINT
|
||||
enums::ServiceArgType type{}; // NOLINT
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void dump_to(std::string &out) const override;
|
||||
|
||||
@@ -633,18 +675,20 @@ class CameraImageRequest : public ProtoMessage {
|
||||
};
|
||||
class ListEntitiesClimateResponse : public ProtoMessage {
|
||||
public:
|
||||
std::string object_id{}; // NOLINT
|
||||
uint32_t key{0}; // NOLINT
|
||||
std::string name{}; // NOLINT
|
||||
std::string unique_id{}; // NOLINT
|
||||
bool supports_current_temperature{false}; // NOLINT
|
||||
bool supports_two_point_target_temperature{false}; // NOLINT
|
||||
std::vector<EnumClimateMode> supported_modes{}; // NOLINT
|
||||
float visual_min_temperature{0.0f}; // NOLINT
|
||||
float visual_max_temperature{0.0f}; // NOLINT
|
||||
float visual_temperature_step{0.0f}; // NOLINT
|
||||
bool supports_away{false}; // NOLINT
|
||||
bool supports_action{false}; // NOLINT
|
||||
std::string object_id{}; // NOLINT
|
||||
uint32_t key{0}; // NOLINT
|
||||
std::string name{}; // NOLINT
|
||||
std::string unique_id{}; // NOLINT
|
||||
bool supports_current_temperature{false}; // NOLINT
|
||||
bool supports_two_point_target_temperature{false}; // NOLINT
|
||||
std::vector<enums::ClimateMode> supported_modes{}; // NOLINT
|
||||
float visual_min_temperature{0.0f}; // NOLINT
|
||||
float visual_max_temperature{0.0f}; // NOLINT
|
||||
float visual_temperature_step{0.0f}; // NOLINT
|
||||
bool supports_away{false}; // NOLINT
|
||||
bool supports_action{false}; // NOLINT
|
||||
std::vector<enums::ClimateFanMode> supported_fan_modes{}; // NOLINT
|
||||
std::vector<enums::ClimateSwingMode> supported_swing_modes{}; // NOLINT
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void dump_to(std::string &out) const override;
|
||||
|
||||
@@ -655,14 +699,16 @@ class ListEntitiesClimateResponse : public ProtoMessage {
|
||||
};
|
||||
class ClimateStateResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
EnumClimateMode mode{}; // NOLINT
|
||||
float current_temperature{0.0f}; // NOLINT
|
||||
float target_temperature{0.0f}; // NOLINT
|
||||
float target_temperature_low{0.0f}; // NOLINT
|
||||
float target_temperature_high{0.0f}; // NOLINT
|
||||
bool away{false}; // NOLINT
|
||||
EnumClimateAction action{}; // NOLINT
|
||||
uint32_t key{0}; // NOLINT
|
||||
enums::ClimateMode mode{}; // NOLINT
|
||||
float current_temperature{0.0f}; // NOLINT
|
||||
float target_temperature{0.0f}; // NOLINT
|
||||
float target_temperature_low{0.0f}; // NOLINT
|
||||
float target_temperature_high{0.0f}; // NOLINT
|
||||
bool away{false}; // NOLINT
|
||||
enums::ClimateAction action{}; // NOLINT
|
||||
enums::ClimateFanMode fan_mode{}; // NOLINT
|
||||
enums::ClimateSwingMode swing_mode{}; // NOLINT
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void dump_to(std::string &out) const override;
|
||||
|
||||
@@ -674,7 +720,7 @@ class ClimateCommandRequest : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
bool has_mode{false}; // NOLINT
|
||||
EnumClimateMode mode{}; // NOLINT
|
||||
enums::ClimateMode mode{}; // NOLINT
|
||||
bool has_target_temperature{false}; // NOLINT
|
||||
float target_temperature{0.0f}; // NOLINT
|
||||
bool has_target_temperature_low{false}; // NOLINT
|
||||
@@ -683,6 +729,10 @@ class ClimateCommandRequest : public ProtoMessage {
|
||||
float target_temperature_high{0.0f}; // NOLINT
|
||||
bool has_away{false}; // NOLINT
|
||||
bool away{false}; // NOLINT
|
||||
bool has_fan_mode{false}; // NOLINT
|
||||
enums::ClimateFanMode fan_mode{}; // NOLINT
|
||||
bool has_swing_mode{false}; // NOLINT
|
||||
enums::ClimateSwingMode swing_mode{}; // NOLINT
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void dump_to(std::string &out) const override;
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
// This file was automatically generated with a tool.
|
||||
// See scripts/api_protobuf/api_protobuf.py
|
||||
#include "api_pb2_service.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
@@ -8,69 +10,57 @@ static const char *TAG = "api.service";
|
||||
|
||||
bool APIServerConnectionBase::send_hello_response(const HelloResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_hello_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(true);
|
||||
return this->send_message_<HelloResponse>(msg, 2);
|
||||
}
|
||||
bool APIServerConnectionBase::send_connect_response(const ConnectResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_connect_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(true);
|
||||
return this->send_message_<ConnectResponse>(msg, 4);
|
||||
}
|
||||
bool APIServerConnectionBase::send_disconnect_request(const DisconnectRequest &msg) {
|
||||
ESP_LOGVV(TAG, "send_disconnect_request: %s", msg.dump().c_str());
|
||||
this->set_nodelay(true);
|
||||
return this->send_message_<DisconnectRequest>(msg, 5);
|
||||
}
|
||||
bool APIServerConnectionBase::send_disconnect_response(const DisconnectResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_disconnect_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(true);
|
||||
return this->send_message_<DisconnectResponse>(msg, 6);
|
||||
}
|
||||
bool APIServerConnectionBase::send_ping_request(const PingRequest &msg) {
|
||||
ESP_LOGVV(TAG, "send_ping_request: %s", msg.dump().c_str());
|
||||
this->set_nodelay(false);
|
||||
return this->send_message_<PingRequest>(msg, 7);
|
||||
}
|
||||
bool APIServerConnectionBase::send_ping_response(const PingResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_ping_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(false);
|
||||
return this->send_message_<PingResponse>(msg, 8);
|
||||
}
|
||||
bool APIServerConnectionBase::send_device_info_response(const DeviceInfoResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_device_info_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(false);
|
||||
return this->send_message_<DeviceInfoResponse>(msg, 10);
|
||||
}
|
||||
bool APIServerConnectionBase::send_list_entities_done_response(const ListEntitiesDoneResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_list_entities_done_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(true);
|
||||
return this->send_message_<ListEntitiesDoneResponse>(msg, 19);
|
||||
}
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
bool APIServerConnectionBase::send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_list_entities_binary_sensor_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(false);
|
||||
return this->send_message_<ListEntitiesBinarySensorResponse>(msg, 12);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
bool APIServerConnectionBase::send_binary_sensor_state_response(const BinarySensorStateResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_binary_sensor_state_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(true);
|
||||
return this->send_message_<BinarySensorStateResponse>(msg, 21);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
bool APIServerConnectionBase::send_list_entities_cover_response(const ListEntitiesCoverResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_list_entities_cover_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(false);
|
||||
return this->send_message_<ListEntitiesCoverResponse>(msg, 13);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_cover_state_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(true);
|
||||
return this->send_message_<CoverStateResponse>(msg, 22);
|
||||
}
|
||||
#endif
|
||||
@@ -79,14 +69,12 @@ bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse
|
||||
#ifdef USE_FAN
|
||||
bool APIServerConnectionBase::send_list_entities_fan_response(const ListEntitiesFanResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_list_entities_fan_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(false);
|
||||
return this->send_message_<ListEntitiesFanResponse>(msg, 14);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_fan_state_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(true);
|
||||
return this->send_message_<FanStateResponse>(msg, 23);
|
||||
}
|
||||
#endif
|
||||
@@ -95,14 +83,12 @@ bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &ms
|
||||
#ifdef USE_LIGHT
|
||||
bool APIServerConnectionBase::send_list_entities_light_response(const ListEntitiesLightResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_list_entities_light_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(false);
|
||||
return this->send_message_<ListEntitiesLightResponse>(msg, 15);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
bool APIServerConnectionBase::send_light_state_response(const LightStateResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_light_state_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(true);
|
||||
return this->send_message_<LightStateResponse>(msg, 24);
|
||||
}
|
||||
#endif
|
||||
@@ -111,28 +97,24 @@ bool APIServerConnectionBase::send_light_state_response(const LightStateResponse
|
||||
#ifdef USE_SENSOR
|
||||
bool APIServerConnectionBase::send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_list_entities_sensor_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(false);
|
||||
return this->send_message_<ListEntitiesSensorResponse>(msg, 16);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
bool APIServerConnectionBase::send_sensor_state_response(const SensorStateResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_sensor_state_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(true);
|
||||
return this->send_message_<SensorStateResponse>(msg, 25);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
bool APIServerConnectionBase::send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_list_entities_switch_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(false);
|
||||
return this->send_message_<ListEntitiesSwitchResponse>(msg, 17);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
bool APIServerConnectionBase::send_switch_state_response(const SwitchStateResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_switch_state_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(true);
|
||||
return this->send_message_<SwitchStateResponse>(msg, 26);
|
||||
}
|
||||
#endif
|
||||
@@ -141,58 +123,48 @@ bool APIServerConnectionBase::send_switch_state_response(const SwitchStateRespon
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
bool APIServerConnectionBase::send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_list_entities_text_sensor_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(false);
|
||||
return this->send_message_<ListEntitiesTextSensorResponse>(msg, 18);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
bool APIServerConnectionBase::send_text_sensor_state_response(const TextSensorStateResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_text_sensor_state_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(true);
|
||||
return this->send_message_<TextSensorStateResponse>(msg, 27);
|
||||
}
|
||||
#endif
|
||||
bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsResponse &msg) {
|
||||
this->set_nodelay(false);
|
||||
return this->send_message_<SubscribeLogsResponse>(msg, 29);
|
||||
}
|
||||
bool APIServerConnectionBase::send_homeassistant_service_response(const HomeassistantServiceResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_homeassistant_service_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(true);
|
||||
return this->send_message_<HomeassistantServiceResponse>(msg, 35);
|
||||
}
|
||||
bool APIServerConnectionBase::send_subscribe_home_assistant_state_response(
|
||||
const SubscribeHomeAssistantStateResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_subscribe_home_assistant_state_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(false);
|
||||
return this->send_message_<SubscribeHomeAssistantStateResponse>(msg, 39);
|
||||
}
|
||||
bool APIServerConnectionBase::send_get_time_request(const GetTimeRequest &msg) {
|
||||
ESP_LOGVV(TAG, "send_get_time_request: %s", msg.dump().c_str());
|
||||
this->set_nodelay(false);
|
||||
return this->send_message_<GetTimeRequest>(msg, 36);
|
||||
}
|
||||
bool APIServerConnectionBase::send_get_time_response(const GetTimeResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_get_time_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(true);
|
||||
return this->send_message_<GetTimeResponse>(msg, 37);
|
||||
}
|
||||
bool APIServerConnectionBase::send_list_entities_services_response(const ListEntitiesServicesResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_list_entities_services_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(false);
|
||||
return this->send_message_<ListEntitiesServicesResponse>(msg, 41);
|
||||
}
|
||||
#ifdef USE_ESP32_CAMERA
|
||||
bool APIServerConnectionBase::send_list_entities_camera_response(const ListEntitiesCameraResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_list_entities_camera_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(false);
|
||||
return this->send_message_<ListEntitiesCameraResponse>(msg, 43);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_ESP32_CAMERA
|
||||
bool APIServerConnectionBase::send_camera_image_response(const CameraImageResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_camera_image_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(false);
|
||||
return this->send_message_<CameraImageResponse>(msg, 44);
|
||||
}
|
||||
#endif
|
||||
@@ -201,14 +173,12 @@ bool APIServerConnectionBase::send_camera_image_response(const CameraImageRespon
|
||||
#ifdef USE_CLIMATE
|
||||
bool APIServerConnectionBase::send_list_entities_climate_response(const ListEntitiesClimateResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_list_entities_climate_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(false);
|
||||
return this->send_message_<ListEntitiesClimateResponse>(msg, 46);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
bool APIServerConnectionBase::send_climate_state_response(const ClimateStateResponse &msg) {
|
||||
ESP_LOGVV(TAG, "send_climate_state_response: %s", msg.dump().c_str());
|
||||
this->set_nodelay(true);
|
||||
return this->send_message_<ClimateStateResponse>(msg, 47);
|
||||
}
|
||||
#endif
|
||||
|
@@ -1,3 +1,5 @@
|
||||
// This file was automatically generated with a tool.
|
||||
// See scripts/api_protobuf/api_protobuf.py
|
||||
#pragma once
|
||||
|
||||
#include "api_pb2.h"
|
||||
|
@@ -29,6 +29,7 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
|
||||
template<typename T> void add_variable(std::string key, T value) {
|
||||
this->variables_.push_back(TemplatableKeyValuePair<Ts...>(key, value));
|
||||
}
|
||||
|
||||
void play(Ts... x) override {
|
||||
HomeassistantServiceResponse resp;
|
||||
resp.service = this->service_.value(x...);
|
||||
|
@@ -266,7 +266,6 @@ class ProtoService {
|
||||
virtual ProtoWriteBuffer create_buffer() = 0;
|
||||
virtual bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) = 0;
|
||||
virtual bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) = 0;
|
||||
virtual void set_nodelay(bool nodelay) = 0;
|
||||
|
||||
template<class C> bool send_message_(const C &msg, uint32_t message_type) {
|
||||
auto buffer = this->create_buffer();
|
||||
|
@@ -7,9 +7,6 @@ namespace api {
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
bool InitialStateIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) {
|
||||
if (!binary_sensor->has_state())
|
||||
return true;
|
||||
|
||||
return this->client_->send_binary_sensor_state(binary_sensor, binary_sensor->state);
|
||||
}
|
||||
#endif
|
||||
@@ -24,9 +21,6 @@ bool InitialStateIterator::on_light(light::LightState *light) { return this->cli
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) {
|
||||
if (!sensor->has_state())
|
||||
return true;
|
||||
|
||||
return this->client_->send_sensor_state(sensor, sensor->state);
|
||||
}
|
||||
#endif
|
||||
@@ -37,9 +31,6 @@ bool InitialStateIterator::on_switch(switch_::Switch *a_switch) {
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
|
||||
if (!text_sensor->has_state())
|
||||
return true;
|
||||
|
||||
return this->client_->send_text_sensor_state(text_sensor, text_sensor->state);
|
||||
}
|
||||
#endif
|
||||
|
@@ -25,14 +25,18 @@ template<> std::vector<std::string> get_execute_arg_value<std::vector<std::strin
|
||||
return arg.string_array;
|
||||
}
|
||||
|
||||
template<> EnumServiceArgType to_service_arg_type<bool>() { return SERVICE_ARG_TYPE_BOOL; }
|
||||
template<> EnumServiceArgType to_service_arg_type<int>() { return SERVICE_ARG_TYPE_INT; }
|
||||
template<> EnumServiceArgType to_service_arg_type<float>() { return SERVICE_ARG_TYPE_FLOAT; }
|
||||
template<> EnumServiceArgType to_service_arg_type<std::string>() { return SERVICE_ARG_TYPE_STRING; }
|
||||
template<> EnumServiceArgType to_service_arg_type<std::vector<bool>>() { return SERVICE_ARG_TYPE_BOOL_ARRAY; }
|
||||
template<> EnumServiceArgType to_service_arg_type<std::vector<int>>() { return SERVICE_ARG_TYPE_INT_ARRAY; }
|
||||
template<> EnumServiceArgType to_service_arg_type<std::vector<float>>() { return SERVICE_ARG_TYPE_FLOAT_ARRAY; }
|
||||
template<> EnumServiceArgType to_service_arg_type<std::vector<std::string>>() { return SERVICE_ARG_TYPE_STRING_ARRAY; }
|
||||
template<> enums::ServiceArgType to_service_arg_type<bool>() { return enums::SERVICE_ARG_TYPE_BOOL; }
|
||||
template<> enums::ServiceArgType to_service_arg_type<int>() { return enums::SERVICE_ARG_TYPE_INT; }
|
||||
template<> enums::ServiceArgType to_service_arg_type<float>() { return enums::SERVICE_ARG_TYPE_FLOAT; }
|
||||
template<> enums::ServiceArgType to_service_arg_type<std::string>() { return enums::SERVICE_ARG_TYPE_STRING; }
|
||||
template<> enums::ServiceArgType to_service_arg_type<std::vector<bool>>() { return enums::SERVICE_ARG_TYPE_BOOL_ARRAY; }
|
||||
template<> enums::ServiceArgType to_service_arg_type<std::vector<int>>() { return enums::SERVICE_ARG_TYPE_INT_ARRAY; }
|
||||
template<> enums::ServiceArgType to_service_arg_type<std::vector<float>>() {
|
||||
return enums::SERVICE_ARG_TYPE_FLOAT_ARRAY;
|
||||
}
|
||||
template<> enums::ServiceArgType to_service_arg_type<std::vector<std::string>>() {
|
||||
return enums::SERVICE_ARG_TYPE_STRING_ARRAY;
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
|
@@ -16,7 +16,7 @@ class UserServiceDescriptor {
|
||||
|
||||
template<typename T> T get_execute_arg_value(const ExecuteServiceArgument &arg);
|
||||
|
||||
template<typename T> EnumServiceArgType to_service_arg_type();
|
||||
template<typename T> enums::ServiceArgType to_service_arg_type();
|
||||
|
||||
template<typename... Ts> class UserServiceBase : public UserServiceDescriptor {
|
||||
public:
|
||||
@@ -29,7 +29,7 @@ template<typename... Ts> class UserServiceBase : public UserServiceDescriptor {
|
||||
ListEntitiesServicesResponse msg;
|
||||
msg.name = this->name_;
|
||||
msg.key = this->key_;
|
||||
std::array<EnumServiceArgType, sizeof...(Ts)> arg_types = {to_service_arg_type<Ts>()...};
|
||||
std::array<enums::ServiceArgType, sizeof...(Ts)> arg_types = {to_service_arg_type<Ts>()...};
|
||||
for (int i = 0; i < sizeof...(Ts); i++) {
|
||||
ListEntitiesServicesArgument arg;
|
||||
arg.type = arg_types[i];
|
||||
|
@@ -10,8 +10,8 @@ as3935_spi_ns = cg.esphome_ns.namespace('as3935_spi')
|
||||
SPIAS3935 = as3935_spi_ns.class_('SPIAS3935Component', as3935.AS3935, spi.SPIDevice)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(as3935.AS3935_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(SPIAS3935)
|
||||
}).extend(cv.COMPONENT_SCHEMA).extend(spi.SPI_DEVICE_SCHEMA))
|
||||
cv.GenerateID(): cv.declare_id(SPIAS3935),
|
||||
}).extend(cv.COMPONENT_SCHEMA).extend(spi.spi_device_schema(cs_pin_required=True)))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
|
@@ -1,23 +1,15 @@
|
||||
# Dummy integration to allow relying on AsyncTCP
|
||||
import esphome.codegen as cg
|
||||
from esphome.const import ARDUINO_VERSION_ESP32_1_0_0, ARDUINO_VERSION_ESP32_1_0_1, \
|
||||
ARDUINO_VERSION_ESP32_1_0_2
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
|
||||
CODEOWNERS = ['@OttoWinter']
|
||||
|
||||
|
||||
@coroutine_with_priority(200.0)
|
||||
def to_code(config):
|
||||
if CORE.is_esp32:
|
||||
# https://github.com/me-no-dev/AsyncTCP/blob/master/library.json
|
||||
versions_requiring_older_asynctcp = [
|
||||
ARDUINO_VERSION_ESP32_1_0_0,
|
||||
ARDUINO_VERSION_ESP32_1_0_1,
|
||||
ARDUINO_VERSION_ESP32_1_0_2,
|
||||
]
|
||||
if CORE.arduino_version in versions_requiring_older_asynctcp:
|
||||
cg.add_library('AsyncTCP', '1.0.3')
|
||||
else:
|
||||
cg.add_library('AsyncTCP', '1.1.1')
|
||||
# https://github.com/OttoWinter/AsyncTCP/blob/master/library.json
|
||||
cg.add_library('AsyncTCP-esphome', '1.1.1')
|
||||
elif CORE.is_esp8266:
|
||||
# https://github.com/OttoWinter/ESPAsyncTCP
|
||||
cg.add_library('ESPAsyncTCP-esphome', '1.2.2')
|
||||
|
@@ -40,19 +40,45 @@ void ATM90E32Component::update() {
|
||||
if (this->phase_[2].power_sensor_ != nullptr) {
|
||||
this->phase_[2].power_sensor_->publish_state(this->get_active_power_c_());
|
||||
}
|
||||
if (this->phase_[0].reactive_power_sensor_ != nullptr) {
|
||||
this->phase_[0].reactive_power_sensor_->publish_state(this->get_reactive_power_a_());
|
||||
}
|
||||
if (this->phase_[1].reactive_power_sensor_ != nullptr) {
|
||||
this->phase_[1].reactive_power_sensor_->publish_state(this->get_reactive_power_b_());
|
||||
}
|
||||
if (this->phase_[2].reactive_power_sensor_ != nullptr) {
|
||||
this->phase_[2].reactive_power_sensor_->publish_state(this->get_reactive_power_c_());
|
||||
}
|
||||
if (this->phase_[0].power_factor_sensor_ != nullptr) {
|
||||
this->phase_[0].power_factor_sensor_->publish_state(this->get_power_factor_a_());
|
||||
}
|
||||
if (this->phase_[1].power_factor_sensor_ != nullptr) {
|
||||
this->phase_[1].power_factor_sensor_->publish_state(this->get_power_factor_b_());
|
||||
}
|
||||
if (this->phase_[2].power_factor_sensor_ != nullptr) {
|
||||
this->phase_[2].power_factor_sensor_->publish_state(this->get_power_factor_c_());
|
||||
}
|
||||
if (this->freq_sensor_ != nullptr) {
|
||||
this->freq_sensor_->publish_state(this->get_frequency_());
|
||||
}
|
||||
if (this->chip_temperature_sensor_ != nullptr) {
|
||||
this->chip_temperature_sensor_->publish_state(this->get_chip_temperature_());
|
||||
}
|
||||
this->status_clear_warning();
|
||||
}
|
||||
|
||||
void ATM90E32Component::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up ATM90E32Component...");
|
||||
ESP_LOGCONFIG(TAG, "Setting up ATM90E32 Component...");
|
||||
this->spi_setup();
|
||||
|
||||
uint16_t mmode0 = 0x185;
|
||||
uint16_t mmode0 = 0x87; // 3P4W 50Hz
|
||||
if (line_freq_ == 60) {
|
||||
mmode0 |= 1 << 12;
|
||||
mmode0 |= 1 << 12; // sets 12th bit to 1, 60Hz
|
||||
}
|
||||
|
||||
if (current_phases_ == 2) {
|
||||
mmode0 |= 1 << 8; // sets 8th bit to 1, 3P3W
|
||||
mmode0 |= 0 << 1; // sets 1st bit to 0, phase b is not counted into the all-phase sum energy/power (P/Q/S)
|
||||
}
|
||||
|
||||
this->write16_(ATM90E32_REGISTER_SOFTRESET, 0x789A); // Perform soft reset
|
||||
@@ -63,13 +89,15 @@ void ATM90E32Component::setup() {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
this->write16_(ATM90E32_REGISTER_ZXCONFIG, 0x0A55); // ZX2, ZX1, ZX0 pin config
|
||||
this->write16_(ATM90E32_REGISTER_MMODE0, mmode0); // Mode Config (frequency set in main program)
|
||||
this->write16_(ATM90E32_REGISTER_MMODE1, pga_gain_); // PGA Gain Configuration for Current Channels
|
||||
this->write16_(ATM90E32_REGISTER_PSTARTTH, 0x0AFC); // Active Startup Power Threshold = 50%
|
||||
this->write16_(ATM90E32_REGISTER_QSTARTTH, 0x0AEC); // Reactive Startup Power Threshold = 50%
|
||||
this->write16_(ATM90E32_REGISTER_PPHASETH, 0x00BC); // Active Phase Threshold = 10%
|
||||
this->write16_(ATM90E32_REGISTER_PLCONSTH, 0x0861); // PL Constant MSB (default) = 140625000
|
||||
this->write16_(ATM90E32_REGISTER_PLCONSTL, 0xC468); // PL Constant LSB (default)
|
||||
this->write16_(ATM90E32_REGISTER_ZXCONFIG, 0xD654); // ZX2, ZX1, ZX0 pin config
|
||||
this->write16_(ATM90E32_REGISTER_MMODE0, mmode0); // Mode Config (frequency set in main program)
|
||||
this->write16_(ATM90E32_REGISTER_MMODE1, pga_gain_); // PGA Gain Configuration for Current Channels
|
||||
this->write16_(ATM90E32_REGISTER_PSTARTTH, 0x1D4C); // All Active Startup Power Threshold - 0.02A/0.00032 = 7500
|
||||
this->write16_(ATM90E32_REGISTER_QSTARTTH, 0x1D4C); // All Reactive Startup Power Threshold - 50%
|
||||
this->write16_(ATM90E32_REGISTER_PPHASETH, 0x02EE); // Each Phase Active Phase Threshold - 0.002A/0.00032 = 750
|
||||
this->write16_(ATM90E32_REGISTER_QPHASETH, 0x02EE); // Each phase Reactive Phase Threshold - 10%
|
||||
this->write16_(ATM90E32_REGISTER_UGAINA, this->phase_[0].volt_gain_); // A Voltage rms gain
|
||||
this->write16_(ATM90E32_REGISTER_IGAINA, this->phase_[0].ct_gain_); // A line current gain
|
||||
this->write16_(ATM90E32_REGISTER_UGAINB, this->phase_[1].volt_gain_); // B Voltage rms gain
|
||||
@@ -89,13 +117,20 @@ void ATM90E32Component::dump_config() {
|
||||
LOG_SENSOR(" ", "Voltage A", this->phase_[0].voltage_sensor_);
|
||||
LOG_SENSOR(" ", "Current A", this->phase_[0].current_sensor_);
|
||||
LOG_SENSOR(" ", "Power A", this->phase_[0].power_sensor_);
|
||||
LOG_SENSOR(" ", "Reactive Power A", this->phase_[0].reactive_power_sensor_);
|
||||
LOG_SENSOR(" ", "PF A", this->phase_[0].power_factor_sensor_);
|
||||
LOG_SENSOR(" ", "Voltage B", this->phase_[1].voltage_sensor_);
|
||||
LOG_SENSOR(" ", "Current B", this->phase_[1].current_sensor_);
|
||||
LOG_SENSOR(" ", "Power B", this->phase_[1].power_sensor_);
|
||||
LOG_SENSOR(" ", "Reactive Power B", this->phase_[1].reactive_power_sensor_);
|
||||
LOG_SENSOR(" ", "PF B", this->phase_[1].power_factor_sensor_);
|
||||
LOG_SENSOR(" ", "Voltage C", this->phase_[2].voltage_sensor_);
|
||||
LOG_SENSOR(" ", "Current C", this->phase_[2].current_sensor_);
|
||||
LOG_SENSOR(" ", "Power C", this->phase_[2].power_sensor_);
|
||||
LOG_SENSOR(" ", "Frequency", this->freq_sensor_)
|
||||
LOG_SENSOR(" ", "Reactive Power C", this->phase_[2].reactive_power_sensor_);
|
||||
LOG_SENSOR(" ", "PF C", this->phase_[2].power_factor_sensor_);
|
||||
LOG_SENSOR(" ", "Frequency", this->freq_sensor_);
|
||||
LOG_SENSOR(" ", "Chip Temp", this->chip_temperature_sensor_);
|
||||
}
|
||||
float ATM90E32Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
@@ -180,9 +215,37 @@ float ATM90E32Component::get_active_power_c_() {
|
||||
int val = this->read32_(ATM90E32_REGISTER_PMEANC, ATM90E32_REGISTER_PMEANCLSB);
|
||||
return val * 0.00032f;
|
||||
}
|
||||
float ATM90E32Component::get_reactive_power_a_() {
|
||||
int val = this->read32_(ATM90E32_REGISTER_QMEANA, ATM90E32_REGISTER_QMEANALSB);
|
||||
return val * 0.00032f;
|
||||
}
|
||||
float ATM90E32Component::get_reactive_power_b_() {
|
||||
int val = this->read32_(ATM90E32_REGISTER_QMEANB, ATM90E32_REGISTER_QMEANBLSB);
|
||||
return val * 0.00032f;
|
||||
}
|
||||
float ATM90E32Component::get_reactive_power_c_() {
|
||||
int val = this->read32_(ATM90E32_REGISTER_QMEANC, ATM90E32_REGISTER_QMEANCLSB);
|
||||
return val * 0.00032f;
|
||||
}
|
||||
float ATM90E32Component::get_power_factor_a_() {
|
||||
int16_t pf = this->read16_(ATM90E32_REGISTER_PFMEANA);
|
||||
return (float) pf / 1000;
|
||||
}
|
||||
float ATM90E32Component::get_power_factor_b_() {
|
||||
int16_t pf = this->read16_(ATM90E32_REGISTER_PFMEANB);
|
||||
return (float) pf / 1000;
|
||||
}
|
||||
float ATM90E32Component::get_power_factor_c_() {
|
||||
int16_t pf = this->read16_(ATM90E32_REGISTER_PFMEANC);
|
||||
return (float) pf / 1000;
|
||||
}
|
||||
float ATM90E32Component::get_frequency_() {
|
||||
uint16_t freq = this->read16_(ATM90E32_REGISTER_FREQ);
|
||||
return (float) freq / 100;
|
||||
}
|
||||
float ATM90E32Component::get_chip_temperature_() {
|
||||
uint16_t ctemp = this->read16_(ATM90E32_REGISTER_TEMP);
|
||||
return (float) ctemp;
|
||||
}
|
||||
} // namespace atm90e32
|
||||
} // namespace esphome
|
||||
|
@@ -19,11 +19,17 @@ class ATM90E32Component : public PollingComponent,
|
||||
void set_voltage_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].voltage_sensor_ = obj; }
|
||||
void set_current_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].current_sensor_ = obj; }
|
||||
void set_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_sensor_ = obj; }
|
||||
void set_reactive_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].reactive_power_sensor_ = obj; }
|
||||
void set_power_factor_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_factor_sensor_ = obj; }
|
||||
void set_volt_gain(int phase, uint16_t gain) { this->phase_[phase].volt_gain_ = gain; }
|
||||
void set_ct_gain(int phase, uint16_t gain) { this->phase_[phase].ct_gain_ = gain; }
|
||||
|
||||
void set_freq_sensor(sensor::Sensor *freq_sensor) { freq_sensor_ = freq_sensor; }
|
||||
void set_chip_temperature_sensor(sensor::Sensor *chip_temperature_sensor) {
|
||||
chip_temperature_sensor_ = chip_temperature_sensor;
|
||||
}
|
||||
void set_line_freq(int freq) { line_freq_ = freq; }
|
||||
void set_current_phases(int phases) { current_phases_ = phases; }
|
||||
void set_pga_gain(uint16_t gain) { pga_gain_ = gain; }
|
||||
|
||||
protected:
|
||||
@@ -40,18 +46,29 @@ class ATM90E32Component : public PollingComponent,
|
||||
float get_active_power_a_();
|
||||
float get_active_power_b_();
|
||||
float get_active_power_c_();
|
||||
float get_reactive_power_a_();
|
||||
float get_reactive_power_b_();
|
||||
float get_reactive_power_c_();
|
||||
float get_power_factor_a_();
|
||||
float get_power_factor_b_();
|
||||
float get_power_factor_c_();
|
||||
float get_frequency_();
|
||||
float get_chip_temperature_();
|
||||
|
||||
struct ATM90E32Phase {
|
||||
uint16_t volt_gain_{41820};
|
||||
uint16_t ct_gain_{25498};
|
||||
uint16_t volt_gain_{7305};
|
||||
uint16_t ct_gain_{27961};
|
||||
sensor::Sensor *voltage_sensor_{nullptr};
|
||||
sensor::Sensor *current_sensor_{nullptr};
|
||||
sensor::Sensor *power_sensor_{nullptr};
|
||||
sensor::Sensor *reactive_power_sensor_{nullptr};
|
||||
sensor::Sensor *power_factor_sensor_{nullptr};
|
||||
} phase_[3];
|
||||
sensor::Sensor *freq_sensor_{nullptr};
|
||||
sensor::Sensor *chip_temperature_sensor_{nullptr};
|
||||
uint16_t pga_gain_{0x15};
|
||||
int line_freq_{60};
|
||||
int current_phases_{3};
|
||||
};
|
||||
|
||||
} // namespace atm90e32
|
||||
|
@@ -234,12 +234,12 @@ static const uint16_t ATM90E32_REGISTER_IRMSBLSB = 0xEE; // Lower Word (B RMS
|
||||
static const uint16_t ATM90E32_REGISTER_IRMSCLSB = 0xEF; // Lower Word (C RMS Current)
|
||||
|
||||
/* THD, FREQUENCY, ANGLE & TEMPTEMP REGISTERS*/
|
||||
static const uint16_t ATM90E32_REGISTER_THDNUA = 0xF1; // A Voltage THD+N
|
||||
static const uint16_t ATM90E32_REGISTER_THDNUB = 0xF2; // B Voltage THD+N
|
||||
static const uint16_t ATM90E32_REGISTER_THDNUC = 0xF3; // C Voltage THD+N
|
||||
static const uint16_t ATM90E32_REGISTER_THDNIA = 0xF5; // A Current THD+N
|
||||
static const uint16_t ATM90E32_REGISTER_THDNIB = 0xF6; // B Current THD+N
|
||||
static const uint16_t ATM90E32_REGISTER_THDNIC = 0xF7; // C Current THD+N
|
||||
static const uint16_t ATM90E32_REGISTER_UPEAKA = 0xF1; // A Voltage Peak
|
||||
static const uint16_t ATM90E32_REGISTER_UPEAKB = 0xF2; // B Voltage Peak
|
||||
static const uint16_t ATM90E32_REGISTER_UPEAKC = 0xF3; // C Voltage Peak
|
||||
static const uint16_t ATM90E32_REGISTER_IPEAKA = 0xF5; // A Current Peak
|
||||
static const uint16_t ATM90E32_REGISTER_IPEAKB = 0xF6; // B Current Peak
|
||||
static const uint16_t ATM90E32_REGISTER_IPEAKC = 0xF7; // C Current Peak
|
||||
static const uint16_t ATM90E32_REGISTER_FREQ = 0xF8; // Frequency
|
||||
static const uint16_t ATM90E32_REGISTER_PANGLEA = 0xF9; // A Mean Phase Angle
|
||||
static const uint16_t ATM90E32_REGISTER_PANGLEB = 0xFA; // B Mean Phase Angle
|
||||
|
@@ -2,21 +2,29 @@ import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, spi
|
||||
from esphome.const import \
|
||||
CONF_ID, CONF_VOLTAGE, CONF_CURRENT, CONF_POWER, CONF_FREQUENCY, \
|
||||
ICON_FLASH, UNIT_HZ, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT
|
||||
CONF_ID, CONF_VOLTAGE, CONF_CURRENT, CONF_POWER, CONF_POWER_FACTOR, CONF_FREQUENCY, \
|
||||
ICON_FLASH, ICON_LIGHTBULB, ICON_CURRENT_AC, ICON_THERMOMETER, \
|
||||
UNIT_HERTZ, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT, UNIT_EMPTY, UNIT_CELSIUS, UNIT_VOLT_AMPS_REACTIVE
|
||||
|
||||
CONF_PHASE_A = 'phase_a'
|
||||
CONF_PHASE_B = 'phase_b'
|
||||
CONF_PHASE_C = 'phase_c'
|
||||
|
||||
CONF_REACTIVE_POWER = 'reactive_power'
|
||||
CONF_LINE_FREQUENCY = 'line_frequency'
|
||||
CONF_CHIP_TEMPERATURE = 'chip_temperature'
|
||||
CONF_GAIN_PGA = 'gain_pga'
|
||||
CONF_CURRENT_PHASES = 'current_phases'
|
||||
CONF_GAIN_VOLTAGE = 'gain_voltage'
|
||||
CONF_GAIN_CT = 'gain_ct'
|
||||
LINE_FREQS = {
|
||||
'50HZ': 50,
|
||||
'60HZ': 60,
|
||||
}
|
||||
CURRENT_PHASES = {
|
||||
'2': 2,
|
||||
'3': 3,
|
||||
}
|
||||
PGA_GAINS = {
|
||||
'1X': 0x0,
|
||||
'2X': 0x15,
|
||||
@@ -28,10 +36,13 @@ ATM90E32Component = atm90e32_ns.class_('ATM90E32Component', cg.PollingComponent,
|
||||
|
||||
ATM90E32_PHASE_SCHEMA = cv.Schema({
|
||||
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2),
|
||||
cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2),
|
||||
cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_CURRENT_AC, 2),
|
||||
cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 2),
|
||||
cv.Optional(CONF_GAIN_VOLTAGE, default=41820): cv.uint16_t,
|
||||
cv.Optional(CONF_GAIN_CT, default=25498): cv.uint16_t,
|
||||
cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema(UNIT_VOLT_AMPS_REACTIVE,
|
||||
ICON_LIGHTBULB, 2),
|
||||
cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(UNIT_EMPTY, ICON_FLASH, 2),
|
||||
cv.Optional(CONF_GAIN_VOLTAGE, default=7305): cv.uint16_t,
|
||||
cv.Optional(CONF_GAIN_CT, default=27961): cv.uint16_t,
|
||||
})
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema({
|
||||
@@ -39,10 +50,12 @@ CONFIG_SCHEMA = cv.Schema({
|
||||
cv.Optional(CONF_PHASE_A): ATM90E32_PHASE_SCHEMA,
|
||||
cv.Optional(CONF_PHASE_B): ATM90E32_PHASE_SCHEMA,
|
||||
cv.Optional(CONF_PHASE_C): ATM90E32_PHASE_SCHEMA,
|
||||
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(UNIT_HZ, ICON_FLASH, 1),
|
||||
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(UNIT_HERTZ, ICON_CURRENT_AC, 1),
|
||||
cv.Optional(CONF_CHIP_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1),
|
||||
cv.Required(CONF_LINE_FREQUENCY): cv.enum(LINE_FREQS, upper=True),
|
||||
cv.Optional(CONF_CURRENT_PHASES, default='3'): cv.enum(CURRENT_PHASES, upper=True),
|
||||
cv.Optional(CONF_GAIN_PGA, default='2X'): cv.enum(PGA_GAINS, upper=True),
|
||||
}).extend(cv.polling_component_schema('60s')).extend(spi.SPI_DEVICE_SCHEMA)
|
||||
}).extend(cv.polling_component_schema('60s')).extend(spi.spi_device_schema())
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -65,8 +78,18 @@ def to_code(config):
|
||||
if CONF_POWER in conf:
|
||||
sens = yield sensor.new_sensor(conf[CONF_POWER])
|
||||
cg.add(var.set_power_sensor(i, sens))
|
||||
if CONF_REACTIVE_POWER in conf:
|
||||
sens = yield sensor.new_sensor(conf[CONF_REACTIVE_POWER])
|
||||
cg.add(var.set_reactive_power_sensor(i, sens))
|
||||
if CONF_POWER_FACTOR in conf:
|
||||
sens = yield sensor.new_sensor(conf[CONF_POWER_FACTOR])
|
||||
cg.add(var.set_power_factor_sensor(i, sens))
|
||||
if CONF_FREQUENCY in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_FREQUENCY])
|
||||
cg.add(var.set_freq_sensor(sens))
|
||||
if CONF_CHIP_TEMPERATURE in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_CHIP_TEMPERATURE])
|
||||
cg.add(var.set_chip_temperature_sensor(sens))
|
||||
cg.add(var.set_line_freq(config[CONF_LINE_FREQUENCY]))
|
||||
cg.add(var.set_current_phases(config[CONF_CURRENT_PHASES]))
|
||||
cg.add(var.set_pga_gain(config[CONF_GAIN_PGA]))
|
||||
|
@@ -0,0 +1 @@
|
||||
CODEOWNERS = ['@OttoWinter']
|
||||
|
@@ -51,12 +51,15 @@ climate::ClimateTraits BangBangClimate::traits() {
|
||||
}
|
||||
void BangBangClimate::compute_state_() {
|
||||
if (this->mode != climate::CLIMATE_MODE_AUTO) {
|
||||
// in non-auto mode
|
||||
// in non-auto mode, switch directly to appropriate action
|
||||
// - HEAT mode -> HEATING action
|
||||
// - COOL mode -> COOLING action
|
||||
// - OFF mode -> OFF action (not IDLE!)
|
||||
this->switch_to_action_(static_cast<climate::ClimateAction>(this->mode));
|
||||
return;
|
||||
}
|
||||
if (isnan(this->current_temperature) || isnan(this->target_temperature_low) || isnan(this->target_temperature_high)) {
|
||||
// if any control values are nan, go to OFF (idle) mode
|
||||
// if any control parameters are nan, go to OFF action (not IDLE!)
|
||||
this->switch_to_action_(climate::CLIMATE_ACTION_OFF);
|
||||
return;
|
||||
}
|
||||
@@ -69,18 +72,18 @@ void BangBangClimate::compute_state_() {
|
||||
if (this->supports_heat_)
|
||||
target_action = climate::CLIMATE_ACTION_HEATING;
|
||||
else
|
||||
target_action = climate::CLIMATE_ACTION_OFF;
|
||||
target_action = climate::CLIMATE_ACTION_IDLE;
|
||||
} else if (too_hot) {
|
||||
// too hot -> enable cooling if possible, else idle
|
||||
if (this->supports_cool_)
|
||||
target_action = climate::CLIMATE_ACTION_COOLING;
|
||||
else
|
||||
target_action = climate::CLIMATE_ACTION_OFF;
|
||||
target_action = climate::CLIMATE_ACTION_IDLE;
|
||||
} else {
|
||||
// neither too hot nor too cold -> in range
|
||||
if (this->supports_cool_ && this->supports_heat_) {
|
||||
// if supports both ends, go to idle mode
|
||||
target_action = climate::CLIMATE_ACTION_OFF;
|
||||
// if supports both ends, go to idle action
|
||||
target_action = climate::CLIMATE_ACTION_IDLE;
|
||||
} else {
|
||||
// else use current mode and don't change (hysteresis)
|
||||
target_action = this->action;
|
||||
@@ -94,13 +97,24 @@ void BangBangClimate::switch_to_action_(climate::ClimateAction action) {
|
||||
// already in target mode
|
||||
return;
|
||||
|
||||
if ((action == climate::CLIMATE_ACTION_OFF && this->action == climate::CLIMATE_ACTION_IDLE) ||
|
||||
(action == climate::CLIMATE_ACTION_IDLE && this->action == climate::CLIMATE_ACTION_OFF)) {
|
||||
// switching from OFF to IDLE or vice-versa
|
||||
// these only have visual difference. OFF means user manually disabled,
|
||||
// IDLE means it's in auto mode but value is in target range.
|
||||
this->action = action;
|
||||
this->publish_state();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->prev_trigger_ != nullptr) {
|
||||
this->prev_trigger_->stop();
|
||||
this->prev_trigger_->stop_action();
|
||||
this->prev_trigger_ = nullptr;
|
||||
}
|
||||
Trigger<> *trig;
|
||||
switch (action) {
|
||||
case climate::CLIMATE_ACTION_OFF:
|
||||
case climate::CLIMATE_ACTION_IDLE:
|
||||
trig = this->idle_trigger_;
|
||||
break;
|
||||
case climate::CLIMATE_ACTION_COOLING:
|
||||
@@ -112,13 +126,11 @@ void BangBangClimate::switch_to_action_(climate::ClimateAction action) {
|
||||
default:
|
||||
trig = nullptr;
|
||||
}
|
||||
if (trig != nullptr) {
|
||||
// trig should never be null, but still check so that we don't crash
|
||||
trig->trigger();
|
||||
this->action = action;
|
||||
this->prev_trigger_ = trig;
|
||||
this->publish_state();
|
||||
}
|
||||
assert(trig != nullptr);
|
||||
trig->trigger();
|
||||
this->action = action;
|
||||
this->prev_trigger_ = trig;
|
||||
this->publish_state();
|
||||
}
|
||||
void BangBangClimate::change_away_(bool away) {
|
||||
if (!away) {
|
||||
|
@@ -7,6 +7,8 @@ namespace bh1750 {
|
||||
static const char *TAG = "bh1750.sensor";
|
||||
|
||||
static const uint8_t BH1750_COMMAND_POWER_ON = 0b00000001;
|
||||
static const uint8_t BH1750_COMMAND_MT_REG_HI = 0b01000000; // last 3 bits
|
||||
static const uint8_t BH1750_COMMAND_MT_REG_LO = 0b01100000; // last 5 bits
|
||||
|
||||
void BH1750Sensor::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up BH1750 '%s'...", this->name_.c_str());
|
||||
@@ -14,7 +16,13 @@ void BH1750Sensor::setup() {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t mtreg_hi = (this->measurement_time_ >> 5) & 0b111;
|
||||
uint8_t mtreg_lo = (this->measurement_time_ >> 0) & 0b11111;
|
||||
this->write_bytes(BH1750_COMMAND_MT_REG_HI | mtreg_hi, nullptr, 0);
|
||||
this->write_bytes(BH1750_COMMAND_MT_REG_LO | mtreg_lo, nullptr, 0);
|
||||
}
|
||||
|
||||
void BH1750Sensor::dump_config() {
|
||||
LOG_SENSOR("", "BH1750", this);
|
||||
LOG_I2C_DEVICE(this);
|
||||
@@ -59,6 +67,7 @@ void BH1750Sensor::update() {
|
||||
|
||||
this->set_timeout("illuminance", wait, [this]() { this->read_data_(); });
|
||||
}
|
||||
|
||||
float BH1750Sensor::get_setup_priority() const { return setup_priority::DATA; }
|
||||
void BH1750Sensor::read_data_() {
|
||||
uint16_t raw_value;
|
||||
@@ -68,10 +77,12 @@ void BH1750Sensor::read_data_() {
|
||||
}
|
||||
|
||||
float lx = float(raw_value) / 1.2f;
|
||||
lx *= 69.0f / this->measurement_time_;
|
||||
ESP_LOGD(TAG, "'%s': Got illuminance=%.1flx", this->get_name().c_str(), lx);
|
||||
this->publish_state(lx);
|
||||
this->status_clear_warning();
|
||||
}
|
||||
|
||||
void BH1750Sensor::set_resolution(BH1750Resolution resolution) { this->resolution_ = resolution; }
|
||||
|
||||
} // namespace bh1750
|
||||
|
@@ -28,6 +28,7 @@ class BH1750Sensor : public sensor::Sensor, public PollingComponent, public i2c:
|
||||
* @param resolution The new resolution of the sensor.
|
||||
*/
|
||||
void set_resolution(BH1750Resolution resolution);
|
||||
void set_measurement_time(uint8_t measurement_time) { measurement_time_ = measurement_time; }
|
||||
|
||||
// ========== INTERNAL METHODS ==========
|
||||
// (In most use cases you won't need these)
|
||||
@@ -40,6 +41,7 @@ class BH1750Sensor : public sensor::Sensor, public PollingComponent, public i2c:
|
||||
void read_data_();
|
||||
|
||||
BH1750Resolution resolution_{BH1750_RESOLUTION_0P5_LX};
|
||||
uint8_t measurement_time_;
|
||||
};
|
||||
|
||||
} // namespace bh1750
|
||||
|
@@ -15,9 +15,11 @@ BH1750_RESOLUTIONS = {
|
||||
|
||||
BH1750Sensor = bh1750_ns.class_('BH1750Sensor', sensor.Sensor, cg.PollingComponent, i2c.I2CDevice)
|
||||
|
||||
CONF_MEASUREMENT_TIME = 'measurement_time'
|
||||
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_LUX, ICON_BRIGHTNESS_5, 1).extend({
|
||||
cv.GenerateID(): cv.declare_id(BH1750Sensor),
|
||||
cv.Optional(CONF_RESOLUTION, default=0.5): cv.enum(BH1750_RESOLUTIONS, float=True),
|
||||
cv.Optional(CONF_MEASUREMENT_TIME, default=69): cv.int_range(min=31, max=254),
|
||||
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x23))
|
||||
|
||||
|
||||
@@ -28,3 +30,4 @@ def to_code(config):
|
||||
yield i2c.register_i2c_device(var, config)
|
||||
|
||||
cg.add(var.set_resolution(config[CONF_RESOLUTION]))
|
||||
cg.add(var.set_measurement_time(config[CONF_MEASUREMENT_TIME]))
|
||||
|
@@ -1,7 +1,8 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import fan, output
|
||||
from esphome.const import CONF_OSCILLATION_OUTPUT, CONF_OUTPUT, CONF_OUTPUT_ID
|
||||
from esphome.const import CONF_DIRECTION_OUTPUT, CONF_OSCILLATION_OUTPUT, \
|
||||
CONF_OUTPUT, CONF_OUTPUT_ID
|
||||
from .. import binary_ns
|
||||
|
||||
BinaryFan = binary_ns.class_('BinaryFan', cg.Component)
|
||||
@@ -9,6 +10,7 @@ BinaryFan = binary_ns.class_('BinaryFan', cg.Component)
|
||||
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(BinaryFan),
|
||||
cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput),
|
||||
cv.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput),
|
||||
cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput),
|
||||
}).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
@@ -25,3 +27,7 @@ def to_code(config):
|
||||
if CONF_OSCILLATION_OUTPUT in config:
|
||||
oscillation_output = yield cg.get_variable(config[CONF_OSCILLATION_OUTPUT])
|
||||
cg.add(var.set_oscillating(oscillation_output))
|
||||
|
||||
if CONF_DIRECTION_OUTPUT in config:
|
||||
direction_output = yield cg.get_variable(config[CONF_DIRECTION_OUTPUT])
|
||||
cg.add(var.set_direction(direction_output))
|
||||
|
@@ -11,9 +11,12 @@ void binary::BinaryFan::dump_config() {
|
||||
if (this->fan_->get_traits().supports_oscillation()) {
|
||||
ESP_LOGCONFIG(TAG, " Oscillation: YES");
|
||||
}
|
||||
if (this->fan_->get_traits().supports_direction()) {
|
||||
ESP_LOGCONFIG(TAG, " Direction: YES");
|
||||
}
|
||||
}
|
||||
void BinaryFan::setup() {
|
||||
auto traits = fan::FanTraits(this->oscillating_ != nullptr, false);
|
||||
auto traits = fan::FanTraits(this->oscillating_ != nullptr, false, this->direction_ != nullptr);
|
||||
this->fan_->set_traits(traits);
|
||||
this->fan_->add_on_state_callback([this]() { this->next_update_ = true; });
|
||||
}
|
||||
@@ -41,6 +44,16 @@ void BinaryFan::loop() {
|
||||
}
|
||||
ESP_LOGD(TAG, "Setting oscillation: %s", ONOFF(enable));
|
||||
}
|
||||
|
||||
if (this->direction_ != nullptr) {
|
||||
bool enable = this->fan_->direction == fan::FAN_DIRECTION_REVERSE;
|
||||
if (enable) {
|
||||
this->direction_->turn_on();
|
||||
} else {
|
||||
this->direction_->turn_off();
|
||||
}
|
||||
ESP_LOGD(TAG, "Setting reverse direction: %s", ONOFF(enable));
|
||||
}
|
||||
}
|
||||
float BinaryFan::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
|
@@ -16,11 +16,13 @@ class BinaryFan : public Component {
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override;
|
||||
void set_oscillating(output::BinaryOutput *oscillating) { this->oscillating_ = oscillating; }
|
||||
void set_direction(output::BinaryOutput *direction) { this->direction_ = direction; }
|
||||
|
||||
protected:
|
||||
fan::FanState *fan_;
|
||||
output::BinaryOutput *output_;
|
||||
output::BinaryOutput *oscillating_{nullptr};
|
||||
output::BinaryOutput *direction_{nullptr};
|
||||
bool next_update_{true};
|
||||
};
|
||||
|
||||
|
@@ -9,9 +9,9 @@ from esphome.const import CONF_DEVICE_CLASS, CONF_FILTERS, \
|
||||
CONF_ON_DOUBLE_CLICK, CONF_ON_MULTI_CLICK, CONF_ON_PRESS, CONF_ON_RELEASE, CONF_ON_STATE, \
|
||||
CONF_STATE, CONF_TIMING, CONF_TRIGGER_ID, CONF_FOR, CONF_NAME, CONF_MQTT_ID
|
||||
from esphome.core import CORE, coroutine, coroutine_with_priority
|
||||
from esphome.py_compat import string_types
|
||||
from esphome.util import Registry
|
||||
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
DEVICE_CLASSES = [
|
||||
'', 'battery', 'cold', 'connectivity', 'door', 'garage_door', 'gas',
|
||||
'heat', 'light', 'lock', 'moisture', 'motion', 'moving', 'occupancy',
|
||||
@@ -94,7 +94,7 @@ MULTI_CLICK_TIMING_SCHEMA = cv.Schema({
|
||||
|
||||
|
||||
def parse_multi_click_timing_str(value):
|
||||
if not isinstance(value, string_types):
|
||||
if not isinstance(value, str):
|
||||
return value
|
||||
|
||||
parts = value.lower().split(' ')
|
||||
@@ -104,10 +104,10 @@ def parse_multi_click_timing_str(value):
|
||||
try:
|
||||
state = cv.boolean(parts[0])
|
||||
except cv.Invalid:
|
||||
raise cv.Invalid(u"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':
|
||||
raise cv.Invalid(u"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[3] == 'least':
|
||||
@@ -115,12 +115,12 @@ def parse_multi_click_timing_str(value):
|
||||
elif parts[3] == 'most':
|
||||
key = CONF_MAX_LENGTH
|
||||
else:
|
||||
raise cv.Invalid(u"Third word after at must either be 'least' or 'most', got {}"
|
||||
u"".format(parts[3]))
|
||||
raise cv.Invalid("Third word after at must either be 'least' or 'most', got {}"
|
||||
"".format(parts[3]))
|
||||
try:
|
||||
length = cv.positive_time_period_milliseconds(parts[4])
|
||||
except cv.Invalid as err:
|
||||
raise cv.Invalid(u"Multi Click Grammar Parsing length failed: {}".format(err))
|
||||
raise cv.Invalid(f"Multi Click Grammar Parsing length failed: {err}")
|
||||
return {
|
||||
CONF_STATE: state,
|
||||
key: str(length)
|
||||
@@ -132,12 +132,12 @@ def parse_multi_click_timing_str(value):
|
||||
try:
|
||||
min_length = cv.positive_time_period_milliseconds(parts[2])
|
||||
except cv.Invalid as err:
|
||||
raise cv.Invalid(u"Multi Click Grammar Parsing minimum length failed: {}".format(err))
|
||||
raise cv.Invalid(f"Multi Click Grammar Parsing minimum length failed: {err}")
|
||||
|
||||
try:
|
||||
max_length = cv.positive_time_period_milliseconds(parts[4])
|
||||
except cv.Invalid as err:
|
||||
raise cv.Invalid(u"Multi Click Grammar Parsing minimum length failed: {}".format(err))
|
||||
raise cv.Invalid(f"Multi Click Grammar Parsing minimum length failed: {err}")
|
||||
|
||||
return {
|
||||
CONF_STATE: state,
|
||||
@@ -225,7 +225,7 @@ BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
|
||||
def setup_binary_sensor_core_(var, config):
|
||||
cg.add(var.set_name(config[CONF_NAME]))
|
||||
if CONF_INTERNAL in config:
|
||||
cg.add(var.set_internal(CONF_INTERNAL))
|
||||
cg.add(var.set_internal(config[CONF_INTERNAL]))
|
||||
if CONF_DEVICE_CLASS in config:
|
||||
cg.add(var.set_device_class(config[CONF_DEVICE_CLASS]))
|
||||
if CONF_INVERTED in config:
|
||||
|
@@ -137,6 +137,7 @@ template<typename... Ts> class BinarySensorPublishAction : public Action<Ts...>
|
||||
public:
|
||||
explicit BinarySensorPublishAction(BinarySensor *sensor) : sensor_(sensor) {}
|
||||
TEMPLATABLE_VALUE(bool, state)
|
||||
|
||||
void play(Ts... x) override {
|
||||
auto val = this->state_.value(x...);
|
||||
this->sensor_->publish_state(val);
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import binary_sensor, esp32_ble_tracker
|
||||
from esphome.const import CONF_MAC_ADDRESS, CONF_ID
|
||||
from esphome.const import CONF_MAC_ADDRESS, CONF_SERVICE_UUID, CONF_ID
|
||||
|
||||
DEPENDENCIES = ['esp32_ble_tracker']
|
||||
|
||||
@@ -9,10 +9,12 @@ ble_presence_ns = cg.esphome_ns.namespace('ble_presence')
|
||||
BLEPresenceDevice = ble_presence_ns.class_('BLEPresenceDevice', binary_sensor.BinarySensor,
|
||||
cg.Component, esp32_ble_tracker.ESPBTDeviceListener)
|
||||
|
||||
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({
|
||||
CONFIG_SCHEMA = cv.All(binary_sensor.BINARY_SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(BLEPresenceDevice),
|
||||
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
|
||||
}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA)
|
||||
cv.Optional(CONF_MAC_ADDRESS): cv.mac_address,
|
||||
cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
|
||||
}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(
|
||||
cv.COMPONENT_SCHEMA), cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -21,4 +23,14 @@ def to_code(config):
|
||||
yield esp32_ble_tracker.register_ble_device(var, config)
|
||||
yield binary_sensor.register_binary_sensor(var, config)
|
||||
|
||||
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
||||
if CONF_MAC_ADDRESS in config:
|
||||
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
||||
|
||||
if CONF_SERVICE_UUID in config:
|
||||
if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
|
||||
cg.add(var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID])))
|
||||
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])))
|
||||
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
|
||||
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_SERVICE_UUID])
|
||||
cg.add(var.set_service_uuid128(uuid128))
|
||||
|
@@ -13,17 +13,42 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
|
||||
public esp32_ble_tracker::ESPBTDeviceListener,
|
||||
public Component {
|
||||
public:
|
||||
void set_address(uint64_t address) { address_ = address; }
|
||||
void set_address(uint64_t address) {
|
||||
this->by_address_ = true;
|
||||
this->address_ = address;
|
||||
}
|
||||
void set_service_uuid16(uint16_t uuid) {
|
||||
this->by_address_ = false;
|
||||
this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_uint16(uuid);
|
||||
}
|
||||
void set_service_uuid32(uint32_t uuid) {
|
||||
this->by_address_ = false;
|
||||
this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_uint32(uuid);
|
||||
}
|
||||
void set_service_uuid128(uint8_t *uuid) {
|
||||
this->by_address_ = false;
|
||||
this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw(uuid);
|
||||
}
|
||||
void on_scan_end() override {
|
||||
if (!this->found_)
|
||||
this->publish_state(false);
|
||||
this->found_ = false;
|
||||
}
|
||||
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override {
|
||||
if (device.address_uint64() == this->address_) {
|
||||
this->publish_state(true);
|
||||
this->found_ = true;
|
||||
return true;
|
||||
if (this->by_address_) {
|
||||
if (device.address_uint64() == this->address_) {
|
||||
this->publish_state(true);
|
||||
this->found_ = true;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
for (auto uuid : device.get_service_uuids()) {
|
||||
if (this->uuid_ == uuid) {
|
||||
this->publish_state(device.get_rssi());
|
||||
this->found_ = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -32,7 +57,9 @@ class BLEPresenceDevice : public binary_sensor::BinarySensorInitiallyOff,
|
||||
|
||||
protected:
|
||||
bool found_{false};
|
||||
bool by_address_{false};
|
||||
uint64_t address_;
|
||||
esp32_ble_tracker::ESPBTUUID uuid_;
|
||||
};
|
||||
|
||||
} // namespace ble_presence
|
||||
|
@@ -11,17 +11,42 @@ namespace ble_rssi {
|
||||
|
||||
class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDeviceListener, public Component {
|
||||
public:
|
||||
void set_address(uint64_t address) { address_ = address; }
|
||||
void set_address(uint64_t address) {
|
||||
this->by_address_ = true;
|
||||
this->address_ = address;
|
||||
}
|
||||
void set_service_uuid16(uint16_t uuid) {
|
||||
this->by_address_ = false;
|
||||
this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_uint16(uuid);
|
||||
}
|
||||
void set_service_uuid32(uint32_t uuid) {
|
||||
this->by_address_ = false;
|
||||
this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_uint32(uuid);
|
||||
}
|
||||
void set_service_uuid128(uint8_t *uuid) {
|
||||
this->by_address_ = false;
|
||||
this->uuid_ = esp32_ble_tracker::ESPBTUUID::from_raw(uuid);
|
||||
}
|
||||
void on_scan_end() override {
|
||||
if (!this->found_)
|
||||
this->publish_state(NAN);
|
||||
this->found_ = false;
|
||||
}
|
||||
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override {
|
||||
if (device.address_uint64() == this->address_) {
|
||||
this->publish_state(device.get_rssi());
|
||||
this->found_ = true;
|
||||
return true;
|
||||
if (this->by_address_) {
|
||||
if (device.address_uint64() == this->address_) {
|
||||
this->publish_state(device.get_rssi());
|
||||
this->found_ = true;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
for (auto uuid : device.get_service_uuids()) {
|
||||
if (this->uuid_ == uuid) {
|
||||
this->publish_state(device.get_rssi());
|
||||
this->found_ = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -30,7 +55,9 @@ class BLERSSISensor : public sensor::Sensor, public esp32_ble_tracker::ESPBTDevi
|
||||
|
||||
protected:
|
||||
bool found_{false};
|
||||
bool by_address_{false};
|
||||
uint64_t address_;
|
||||
esp32_ble_tracker::ESPBTUUID uuid_;
|
||||
};
|
||||
|
||||
} // namespace ble_rssi
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, esp32_ble_tracker
|
||||
from esphome.const import CONF_MAC_ADDRESS, CONF_ID, UNIT_DECIBEL, ICON_SIGNAL
|
||||
from esphome.const import CONF_SERVICE_UUID, CONF_MAC_ADDRESS, CONF_ID, UNIT_DECIBEL, ICON_SIGNAL
|
||||
|
||||
DEPENDENCIES = ['esp32_ble_tracker']
|
||||
|
||||
@@ -9,10 +9,12 @@ ble_rssi_ns = cg.esphome_ns.namespace('ble_rssi')
|
||||
BLERSSISensor = ble_rssi_ns.class_('BLERSSISensor', sensor.Sensor, cg.Component,
|
||||
esp32_ble_tracker.ESPBTDeviceListener)
|
||||
|
||||
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_DECIBEL, ICON_SIGNAL, 0).extend({
|
||||
CONFIG_SCHEMA = cv.All(sensor.sensor_schema(UNIT_DECIBEL, ICON_SIGNAL, 0).extend({
|
||||
cv.GenerateID(): cv.declare_id(BLERSSISensor),
|
||||
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
|
||||
}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(cv.COMPONENT_SCHEMA)
|
||||
cv.Optional(CONF_MAC_ADDRESS): cv.mac_address,
|
||||
cv.Optional(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
|
||||
}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(
|
||||
cv.COMPONENT_SCHEMA), cv.has_exactly_one_key(CONF_MAC_ADDRESS, CONF_SERVICE_UUID))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
@@ -21,4 +23,14 @@ def to_code(config):
|
||||
yield esp32_ble_tracker.register_ble_device(var, config)
|
||||
yield sensor.register_sensor(var, config)
|
||||
|
||||
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
||||
if CONF_MAC_ADDRESS in config:
|
||||
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
||||
|
||||
if CONF_SERVICE_UUID in config:
|
||||
if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
|
||||
cg.add(var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID])))
|
||||
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])))
|
||||
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
|
||||
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_SERVICE_UUID])
|
||||
cg.add(var.set_service_uuid128(uuid128))
|
||||
|
0
esphome/components/ble_scanner/__init__.py
Normal file
0
esphome/components/ble_scanner/__init__.py
Normal file
16
esphome/components/ble_scanner/ble_scanner.cpp
Normal file
16
esphome/components/ble_scanner/ble_scanner.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "ble_scanner.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_scanner {
|
||||
|
||||
static const char *TAG = "ble_scanner";
|
||||
|
||||
void BLEScanner::dump_config() { LOG_TEXT_SENSOR("", "BLE Scanner", this); }
|
||||
|
||||
} // namespace ble_scanner
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
38
esphome/components/ble_scanner/ble_scanner.h
Normal file
38
esphome/components/ble_scanner/ble_scanner.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_scanner {
|
||||
|
||||
class BLEScanner : public text_sensor::TextSensor, public esp32_ble_tracker::ESPBTDeviceListener, public Component {
|
||||
public:
|
||||
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override {
|
||||
this->publish_state("{\"timestamp\":" + to_string(::time(NULL)) +
|
||||
","
|
||||
"\"address\":\"" +
|
||||
device.address_str() +
|
||||
"\","
|
||||
"\"rssi\":" +
|
||||
to_string(device.get_rssi()) +
|
||||
","
|
||||
"\"name\":\"" +
|
||||
device.get_name() + "\"}");
|
||||
|
||||
return true;
|
||||
}
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
};
|
||||
|
||||
} // namespace ble_scanner
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
22
esphome/components/ble_scanner/text_sensor.py
Normal file
22
esphome/components/ble_scanner/text_sensor.py
Normal file
@@ -0,0 +1,22 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import text_sensor, esp32_ble_tracker
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
DEPENDENCIES = ['esp32_ble_tracker']
|
||||
|
||||
ble_scanner_ns = cg.esphome_ns.namespace('ble_scanner')
|
||||
BLEScanner = ble_scanner_ns.class_('BLEScanner', text_sensor.TextSensor, cg.Component,
|
||||
esp32_ble_tracker.ESPBTDeviceListener)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(text_sensor.TEXT_SENSOR_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(BLEScanner),
|
||||
}).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA).extend(
|
||||
cv.COMPONENT_SCHEMA))
|
||||
|
||||
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield esp32_ble_tracker.register_ble_device(var, config)
|
||||
yield text_sensor.register_text_sensor(var, config)
|
@@ -146,7 +146,7 @@ void BME280Component::dump_config() {
|
||||
ESP_LOGE(TAG, "Communication with BME280 failed!");
|
||||
break;
|
||||
case WRONG_CHIP_ID:
|
||||
ESP_LOGE(TAG, "BMP280 has wrong chip ID! Is it a BMP280?");
|
||||
ESP_LOGE(TAG, "BME280 has wrong chip ID! Is it a BME280?");
|
||||
break;
|
||||
case NONE:
|
||||
default:
|
||||
@@ -172,7 +172,7 @@ void BME280Component::update() {
|
||||
uint8_t meas_register = 0;
|
||||
meas_register |= (this->temperature_oversampling_ & 0b111) << 5;
|
||||
meas_register |= (this->pressure_oversampling_ & 0b111) << 2;
|
||||
meas_register |= 0b01; // Forced mode
|
||||
meas_register |= BME280_MODE_FORCED;
|
||||
if (!this->write_byte(BME280_REGISTER_CONTROL, meas_register)) {
|
||||
this->status_set_warning();
|
||||
return;
|
||||
|
@@ -7,6 +7,7 @@ from esphome.core import coroutine_with_priority
|
||||
|
||||
AUTO_LOAD = ['web_server_base']
|
||||
DEPENDENCIES = ['wifi']
|
||||
CODEOWNERS = ['@OttoWinter']
|
||||
|
||||
captive_portal_ns = cg.esphome_ns.namespace('captive_portal')
|
||||
CaptivePortal = captive_portal_ns.class_('CaptivePortal', cg.Component)
|
||||
|
@@ -5,11 +5,12 @@ from esphome.components import mqtt
|
||||
from esphome.const import CONF_AWAY, CONF_ID, CONF_INTERNAL, CONF_MAX_TEMPERATURE, \
|
||||
CONF_MIN_TEMPERATURE, CONF_MODE, CONF_TARGET_TEMPERATURE, \
|
||||
CONF_TARGET_TEMPERATURE_HIGH, CONF_TARGET_TEMPERATURE_LOW, CONF_TEMPERATURE_STEP, CONF_VISUAL, \
|
||||
CONF_MQTT_ID, CONF_NAME
|
||||
CONF_MQTT_ID, CONF_NAME, CONF_FAN_MODE, CONF_SWING_MODE
|
||||
from esphome.core import CORE, coroutine, coroutine_with_priority
|
||||
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
climate_ns = cg.esphome_ns.namespace('climate')
|
||||
|
||||
Climate = climate_ns.class_('Climate', cg.Nameable)
|
||||
@@ -22,9 +23,35 @@ CLIMATE_MODES = {
|
||||
'AUTO': ClimateMode.CLIMATE_MODE_AUTO,
|
||||
'COOL': ClimateMode.CLIMATE_MODE_COOL,
|
||||
'HEAT': ClimateMode.CLIMATE_MODE_HEAT,
|
||||
'DRY': ClimateMode.CLIMATE_MODE_DRY,
|
||||
'FAN_ONLY': ClimateMode.CLIMATE_MODE_FAN_ONLY,
|
||||
}
|
||||
validate_climate_mode = cv.enum(CLIMATE_MODES, upper=True)
|
||||
|
||||
ClimateFanMode = climate_ns.enum('ClimateFanMode')
|
||||
CLIMATE_FAN_MODES = {
|
||||
'ON': ClimateFanMode.CLIMATE_FAN_ON,
|
||||
'OFF': ClimateFanMode.CLIMATE_FAN_OFF,
|
||||
'AUTO': ClimateFanMode.CLIMATE_FAN_AUTO,
|
||||
'LOW': ClimateFanMode.CLIMATE_FAN_LOW,
|
||||
'MEDIUM': ClimateFanMode.CLIMATE_FAN_MEDIUM,
|
||||
'HIGH': ClimateFanMode.CLIMATE_FAN_HIGH,
|
||||
'MIDDLE': ClimateFanMode.CLIMATE_FAN_MIDDLE,
|
||||
'FOCUS': ClimateFanMode.CLIMATE_FAN_FOCUS,
|
||||
'DIFFUSE': ClimateFanMode.CLIMATE_FAN_DIFFUSE,
|
||||
}
|
||||
|
||||
validate_climate_mode = cv.enum(CLIMATE_MODES, upper=True)
|
||||
validate_climate_fan_mode = cv.enum(CLIMATE_FAN_MODES, upper=True)
|
||||
|
||||
ClimateSwingMode = climate_ns.enum('ClimateSwingMode')
|
||||
CLIMATE_SWING_MODES = {
|
||||
'OFF': ClimateSwingMode.CLIMATE_SWING_OFF,
|
||||
'BOTH': ClimateSwingMode.CLIMATE_SWING_BOTH,
|
||||
'VERTICAL': ClimateSwingMode.CLIMATE_SWING_VERTICAL,
|
||||
'HORIZONTAL': ClimateSwingMode.CLIMATE_SWING_HORIZONTAL,
|
||||
}
|
||||
|
||||
validate_climate_swing_mode = cv.enum(CLIMATE_SWING_MODES, upper=True)
|
||||
|
||||
# Actions
|
||||
ControlAction = climate_ns.class_('ControlAction', automation.Action)
|
||||
@@ -74,6 +101,8 @@ CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema({
|
||||
cv.Optional(CONF_TARGET_TEMPERATURE_LOW): cv.templatable(cv.temperature),
|
||||
cv.Optional(CONF_TARGET_TEMPERATURE_HIGH): cv.templatable(cv.temperature),
|
||||
cv.Optional(CONF_AWAY): cv.templatable(cv.boolean),
|
||||
cv.Optional(CONF_FAN_MODE): cv.templatable(validate_climate_fan_mode),
|
||||
cv.Optional(CONF_SWING_MODE): cv.templatable(validate_climate_swing_mode),
|
||||
})
|
||||
|
||||
|
||||
@@ -96,6 +125,12 @@ def climate_control_to_code(config, action_id, template_arg, args):
|
||||
if CONF_AWAY in config:
|
||||
template_ = yield cg.templatable(config[CONF_AWAY], args, bool)
|
||||
cg.add(var.set_away(template_))
|
||||
if CONF_FAN_MODE in config:
|
||||
template_ = yield cg.templatable(config[CONF_FAN_MODE], args, ClimateFanMode)
|
||||
cg.add(var.set_fan_mode(template_))
|
||||
if CONF_SWING_MODE in config:
|
||||
template_ = yield cg.templatable(config[CONF_SWING_MODE], args, ClimateSwingMode)
|
||||
cg.add(var.set_swing_mode(template_))
|
||||
yield var
|
||||
|
||||
|
||||
|
@@ -15,6 +15,8 @@ template<typename... Ts> class ControlAction : public Action<Ts...> {
|
||||
TEMPLATABLE_VALUE(float, target_temperature_low)
|
||||
TEMPLATABLE_VALUE(float, target_temperature_high)
|
||||
TEMPLATABLE_VALUE(bool, away)
|
||||
TEMPLATABLE_VALUE(ClimateFanMode, fan_mode)
|
||||
TEMPLATABLE_VALUE(ClimateSwingMode, swing_mode)
|
||||
|
||||
void play(Ts... x) override {
|
||||
auto call = this->climate_->make_call();
|
||||
@@ -23,6 +25,8 @@ template<typename... Ts> class ControlAction : public Action<Ts...> {
|
||||
call.set_target_temperature_low(this->target_temperature_low_.optional_value(x...));
|
||||
call.set_target_temperature_high(this->target_temperature_high_.optional_value(x...));
|
||||
call.set_away(this->away_.optional_value(x...));
|
||||
call.set_fan_mode(this->fan_mode_.optional_value(x...));
|
||||
call.set_swing_mode(this->swing_mode_.optional_value(x...));
|
||||
call.perform();
|
||||
}
|
||||
|
||||
|
@@ -13,6 +13,14 @@ void ClimateCall::perform() {
|
||||
const char *mode_s = climate_mode_to_string(*this->mode_);
|
||||
ESP_LOGD(TAG, " Mode: %s", mode_s);
|
||||
}
|
||||
if (this->fan_mode_.has_value()) {
|
||||
const char *fan_mode_s = climate_fan_mode_to_string(*this->fan_mode_);
|
||||
ESP_LOGD(TAG, " Fan: %s", fan_mode_s);
|
||||
}
|
||||
if (this->swing_mode_.has_value()) {
|
||||
const char *swing_mode_s = climate_swing_mode_to_string(*this->swing_mode_);
|
||||
ESP_LOGD(TAG, " Swing: %s", swing_mode_s);
|
||||
}
|
||||
if (this->target_temperature_.has_value()) {
|
||||
ESP_LOGD(TAG, " Target Temperature: %.2f", *this->target_temperature_);
|
||||
}
|
||||
@@ -36,6 +44,20 @@ void ClimateCall::validate_() {
|
||||
this->mode_.reset();
|
||||
}
|
||||
}
|
||||
if (this->fan_mode_.has_value()) {
|
||||
auto fan_mode = *this->fan_mode_;
|
||||
if (!traits.supports_fan_mode(fan_mode)) {
|
||||
ESP_LOGW(TAG, " Fan Mode %s is not supported by this device!", climate_fan_mode_to_string(fan_mode));
|
||||
this->fan_mode_.reset();
|
||||
}
|
||||
}
|
||||
if (this->swing_mode_.has_value()) {
|
||||
auto swing_mode = *this->swing_mode_;
|
||||
if (!traits.supports_swing_mode(swing_mode)) {
|
||||
ESP_LOGW(TAG, " Swing Mode %s is not supported by this device!", climate_swing_mode_to_string(swing_mode));
|
||||
this->swing_mode_.reset();
|
||||
}
|
||||
}
|
||||
if (this->target_temperature_.has_value()) {
|
||||
auto target = *this->target_temperature_;
|
||||
if (traits.get_supports_two_point_target_temperature()) {
|
||||
@@ -91,11 +113,63 @@ ClimateCall &ClimateCall::set_mode(const std::string &mode) {
|
||||
this->set_mode(CLIMATE_MODE_COOL);
|
||||
} else if (str_equals_case_insensitive(mode, "HEAT")) {
|
||||
this->set_mode(CLIMATE_MODE_HEAT);
|
||||
} else if (str_equals_case_insensitive(mode, "FAN_ONLY")) {
|
||||
this->set_mode(CLIMATE_MODE_FAN_ONLY);
|
||||
} else if (str_equals_case_insensitive(mode, "DRY")) {
|
||||
this->set_mode(CLIMATE_MODE_DRY);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "'%s' - Unrecognized mode %s", this->parent_->get_name().c_str(), mode.c_str());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
ClimateCall &ClimateCall::set_fan_mode(ClimateFanMode fan_mode) {
|
||||
this->fan_mode_ = fan_mode;
|
||||
return *this;
|
||||
}
|
||||
ClimateCall &ClimateCall::set_fan_mode(const std::string &fan_mode) {
|
||||
if (str_equals_case_insensitive(fan_mode, "ON")) {
|
||||
this->set_fan_mode(CLIMATE_FAN_ON);
|
||||
} else if (str_equals_case_insensitive(fan_mode, "OFF")) {
|
||||
this->set_fan_mode(CLIMATE_FAN_OFF);
|
||||
} else if (str_equals_case_insensitive(fan_mode, "AUTO")) {
|
||||
this->set_fan_mode(CLIMATE_FAN_AUTO);
|
||||
} else if (str_equals_case_insensitive(fan_mode, "LOW")) {
|
||||
this->set_fan_mode(CLIMATE_FAN_LOW);
|
||||
} else if (str_equals_case_insensitive(fan_mode, "MEDIUM")) {
|
||||
this->set_fan_mode(CLIMATE_FAN_MEDIUM);
|
||||
} else if (str_equals_case_insensitive(fan_mode, "HIGH")) {
|
||||
this->set_fan_mode(CLIMATE_FAN_HIGH);
|
||||
} else if (str_equals_case_insensitive(fan_mode, "MIDDLE")) {
|
||||
this->set_fan_mode(CLIMATE_FAN_MIDDLE);
|
||||
} else if (str_equals_case_insensitive(fan_mode, "FOCUS")) {
|
||||
this->set_fan_mode(CLIMATE_FAN_FOCUS);
|
||||
} else if (str_equals_case_insensitive(fan_mode, "DIFFUSE")) {
|
||||
this->set_fan_mode(CLIMATE_FAN_DIFFUSE);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "'%s' - Unrecognized fan mode %s", this->parent_->get_name().c_str(), fan_mode.c_str());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ClimateCall &ClimateCall::set_swing_mode(ClimateSwingMode swing_mode) {
|
||||
this->swing_mode_ = swing_mode;
|
||||
return *this;
|
||||
}
|
||||
ClimateCall &ClimateCall::set_swing_mode(const std::string &swing_mode) {
|
||||
if (str_equals_case_insensitive(swing_mode, "OFF")) {
|
||||
this->set_swing_mode(CLIMATE_SWING_OFF);
|
||||
} else if (str_equals_case_insensitive(swing_mode, "BOTH")) {
|
||||
this->set_swing_mode(CLIMATE_SWING_BOTH);
|
||||
} else if (str_equals_case_insensitive(swing_mode, "VERTICAL")) {
|
||||
this->set_swing_mode(CLIMATE_SWING_VERTICAL);
|
||||
} else if (str_equals_case_insensitive(swing_mode, "HORIZONTAL")) {
|
||||
this->set_swing_mode(CLIMATE_SWING_HORIZONTAL);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "'%s' - Unrecognized swing mode %s", this->parent_->get_name().c_str(), swing_mode.c_str());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ClimateCall &ClimateCall::set_target_temperature(float target_temperature) {
|
||||
this->target_temperature_ = target_temperature;
|
||||
return *this;
|
||||
@@ -113,6 +187,8 @@ const optional<float> &ClimateCall::get_target_temperature() const { return this
|
||||
const optional<float> &ClimateCall::get_target_temperature_low() const { return this->target_temperature_low_; }
|
||||
const optional<float> &ClimateCall::get_target_temperature_high() const { return this->target_temperature_high_; }
|
||||
const optional<bool> &ClimateCall::get_away() const { return this->away_; }
|
||||
const optional<ClimateFanMode> &ClimateCall::get_fan_mode() const { return this->fan_mode_; }
|
||||
const optional<ClimateSwingMode> &ClimateCall::get_swing_mode() const { return this->swing_mode_; }
|
||||
ClimateCall &ClimateCall::set_away(bool away) {
|
||||
this->away_ = away;
|
||||
return *this;
|
||||
@@ -137,6 +213,14 @@ ClimateCall &ClimateCall::set_mode(optional<ClimateMode> mode) {
|
||||
this->mode_ = mode;
|
||||
return *this;
|
||||
}
|
||||
ClimateCall &ClimateCall::set_fan_mode(optional<ClimateFanMode> fan_mode) {
|
||||
this->fan_mode_ = fan_mode;
|
||||
return *this;
|
||||
}
|
||||
ClimateCall &ClimateCall::set_swing_mode(optional<ClimateSwingMode> swing_mode) {
|
||||
this->swing_mode_ = swing_mode;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Climate::add_on_state_callback(std::function<void()> &&callback) {
|
||||
this->state_callback_.add(std::move(callback));
|
||||
@@ -165,6 +249,12 @@ void Climate::save_state_() {
|
||||
if (traits.get_supports_away()) {
|
||||
state.away = this->away;
|
||||
}
|
||||
if (traits.get_supports_fan_modes()) {
|
||||
state.fan_mode = this->fan_mode;
|
||||
}
|
||||
if (traits.get_supports_swing_modes()) {
|
||||
state.swing_mode = this->swing_mode;
|
||||
}
|
||||
|
||||
this->rtc_.save(&state);
|
||||
}
|
||||
@@ -176,6 +266,12 @@ void Climate::publish_state() {
|
||||
if (traits.get_supports_action()) {
|
||||
ESP_LOGD(TAG, " Action: %s", climate_action_to_string(this->action));
|
||||
}
|
||||
if (traits.get_supports_fan_modes()) {
|
||||
ESP_LOGD(TAG, " Fan Mode: %s", climate_fan_mode_to_string(this->fan_mode));
|
||||
}
|
||||
if (traits.get_supports_swing_modes()) {
|
||||
ESP_LOGD(TAG, " Swing Mode: %s", climate_swing_mode_to_string(this->swing_mode));
|
||||
}
|
||||
if (traits.get_supports_current_temperature()) {
|
||||
ESP_LOGD(TAG, " Current Temperature: %.2f°C", this->current_temperature);
|
||||
}
|
||||
@@ -236,6 +332,12 @@ ClimateCall ClimateDeviceRestoreState::to_call(Climate *climate) {
|
||||
if (traits.get_supports_away()) {
|
||||
call.set_away(this->away);
|
||||
}
|
||||
if (traits.get_supports_fan_modes()) {
|
||||
call.set_fan_mode(this->fan_mode);
|
||||
}
|
||||
if (traits.get_supports_swing_modes()) {
|
||||
call.set_swing_mode(this->swing_mode);
|
||||
}
|
||||
return call;
|
||||
}
|
||||
void ClimateDeviceRestoreState::apply(Climate *climate) {
|
||||
@@ -250,6 +352,12 @@ void ClimateDeviceRestoreState::apply(Climate *climate) {
|
||||
if (traits.get_supports_away()) {
|
||||
climate->away = this->away;
|
||||
}
|
||||
if (traits.get_supports_fan_modes()) {
|
||||
climate->fan_mode = this->fan_mode;
|
||||
}
|
||||
if (traits.get_supports_swing_modes()) {
|
||||
climate->swing_mode = this->swing_mode;
|
||||
}
|
||||
climate->publish_state();
|
||||
}
|
||||
|
||||
|
@@ -64,6 +64,18 @@ class ClimateCall {
|
||||
ClimateCall &set_target_temperature_high(optional<float> target_temperature_high);
|
||||
ClimateCall &set_away(bool away);
|
||||
ClimateCall &set_away(optional<bool> away);
|
||||
/// Set the fan mode of the climate device.
|
||||
ClimateCall &set_fan_mode(ClimateFanMode fan_mode);
|
||||
/// Set the fan mode of the climate device.
|
||||
ClimateCall &set_fan_mode(optional<ClimateFanMode> fan_mode);
|
||||
/// Set the fan mode of the climate device based on a string.
|
||||
ClimateCall &set_fan_mode(const std::string &fan_mode);
|
||||
/// Set the swing mode of the climate device.
|
||||
ClimateCall &set_swing_mode(ClimateSwingMode swing_mode);
|
||||
/// Set the swing mode of the climate device.
|
||||
ClimateCall &set_swing_mode(optional<ClimateSwingMode> swing_mode);
|
||||
/// Set the swing mode of the climate device based on a string.
|
||||
ClimateCall &set_swing_mode(const std::string &swing_mode);
|
||||
|
||||
void perform();
|
||||
|
||||
@@ -72,6 +84,8 @@ class ClimateCall {
|
||||
const optional<float> &get_target_temperature_low() const;
|
||||
const optional<float> &get_target_temperature_high() const;
|
||||
const optional<bool> &get_away() const;
|
||||
const optional<ClimateFanMode> &get_fan_mode() const;
|
||||
const optional<ClimateSwingMode> &get_swing_mode() const;
|
||||
|
||||
protected:
|
||||
void validate_();
|
||||
@@ -82,12 +96,16 @@ class ClimateCall {
|
||||
optional<float> target_temperature_low_;
|
||||
optional<float> target_temperature_high_;
|
||||
optional<bool> away_;
|
||||
optional<ClimateFanMode> fan_mode_;
|
||||
optional<ClimateSwingMode> swing_mode_;
|
||||
};
|
||||
|
||||
/// Struct used to save the state of the climate device in restore memory.
|
||||
struct ClimateDeviceRestoreState {
|
||||
ClimateMode mode;
|
||||
bool away;
|
||||
ClimateFanMode fan_mode;
|
||||
ClimateSwingMode swing_mode;
|
||||
union {
|
||||
float target_temperature;
|
||||
struct {
|
||||
@@ -149,6 +167,12 @@ class Climate : public Nameable {
|
||||
*/
|
||||
bool away{false};
|
||||
|
||||
/// The active fan mode of the climate device.
|
||||
ClimateFanMode fan_mode;
|
||||
|
||||
/// The active swing mode of the climate device.
|
||||
ClimateSwingMode swing_mode;
|
||||
|
||||
/** Add a callback for the climate device state, each time the state of the climate device is updated
|
||||
* (using publish_state), this callback will be called.
|
||||
*
|
||||
|
@@ -13,6 +13,10 @@ const char *climate_mode_to_string(ClimateMode mode) {
|
||||
return "COOL";
|
||||
case CLIMATE_MODE_HEAT:
|
||||
return "HEAT";
|
||||
case CLIMATE_MODE_FAN_ONLY:
|
||||
return "FAN_ONLY";
|
||||
case CLIMATE_MODE_DRY:
|
||||
return "DRY";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
@@ -25,6 +29,52 @@ const char *climate_action_to_string(ClimateAction action) {
|
||||
return "COOLING";
|
||||
case CLIMATE_ACTION_HEATING:
|
||||
return "HEATING";
|
||||
case CLIMATE_ACTION_IDLE:
|
||||
return "IDLE";
|
||||
case CLIMATE_ACTION_DRYING:
|
||||
return "DRYING";
|
||||
case CLIMATE_ACTION_FAN:
|
||||
return "FAN";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char *climate_fan_mode_to_string(ClimateFanMode fan_mode) {
|
||||
switch (fan_mode) {
|
||||
case climate::CLIMATE_FAN_ON:
|
||||
return "ON";
|
||||
case climate::CLIMATE_FAN_OFF:
|
||||
return "OFF";
|
||||
case climate::CLIMATE_FAN_AUTO:
|
||||
return "AUTO";
|
||||
case climate::CLIMATE_FAN_LOW:
|
||||
return "LOW";
|
||||
case climate::CLIMATE_FAN_MEDIUM:
|
||||
return "MEDIUM";
|
||||
case climate::CLIMATE_FAN_HIGH:
|
||||
return "HIGH";
|
||||
case climate::CLIMATE_FAN_MIDDLE:
|
||||
return "MIDDLE";
|
||||
case climate::CLIMATE_FAN_FOCUS:
|
||||
return "FOCUS";
|
||||
case climate::CLIMATE_FAN_DIFFUSE:
|
||||
return "DIFFUSE";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char *climate_swing_mode_to_string(ClimateSwingMode swing_mode) {
|
||||
switch (swing_mode) {
|
||||
case climate::CLIMATE_SWING_OFF:
|
||||
return "OFF";
|
||||
case climate::CLIMATE_SWING_BOTH:
|
||||
return "BOTH";
|
||||
case climate::CLIMATE_SWING_VERTICAL:
|
||||
return "VERTICAL";
|
||||
case climate::CLIMATE_SWING_HORIZONTAL:
|
||||
return "HORIZONTAL";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
@@ -15,6 +15,10 @@ enum ClimateMode : uint8_t {
|
||||
CLIMATE_MODE_COOL = 2,
|
||||
/// The climate device is manually set to heat mode (not in auto mode!)
|
||||
CLIMATE_MODE_HEAT = 3,
|
||||
/// The climate device is manually set to fan only mode
|
||||
CLIMATE_MODE_FAN_ONLY = 4,
|
||||
/// The climate device is manually set to dry mode
|
||||
CLIMATE_MODE_DRY = 5,
|
||||
};
|
||||
|
||||
/// Enum for the current action of the climate device. Values match those of ClimateMode.
|
||||
@@ -25,11 +29,59 @@ enum ClimateAction : uint8_t {
|
||||
CLIMATE_ACTION_COOLING = 2,
|
||||
/// The climate device is actively heating (usually in heat or auto mode)
|
||||
CLIMATE_ACTION_HEATING = 3,
|
||||
/// The climate device is idle (monitoring climate but no action needed)
|
||||
CLIMATE_ACTION_IDLE = 4,
|
||||
/// The climate device is drying (either mode DRY or AUTO)
|
||||
CLIMATE_ACTION_DRYING = 5,
|
||||
/// The climate device is in fan only mode (either mode FAN_ONLY or AUTO)
|
||||
CLIMATE_ACTION_FAN = 6,
|
||||
};
|
||||
|
||||
/// Enum for all modes a climate fan can be in
|
||||
enum ClimateFanMode : uint8_t {
|
||||
/// The fan mode is set to On
|
||||
CLIMATE_FAN_ON = 0,
|
||||
/// The fan mode is set to Off
|
||||
CLIMATE_FAN_OFF = 1,
|
||||
/// The fan mode is set to Auto
|
||||
CLIMATE_FAN_AUTO = 2,
|
||||
/// The fan mode is set to Low
|
||||
CLIMATE_FAN_LOW = 3,
|
||||
/// The fan mode is set to Medium
|
||||
CLIMATE_FAN_MEDIUM = 4,
|
||||
/// The fan mode is set to High
|
||||
CLIMATE_FAN_HIGH = 5,
|
||||
/// The fan mode is set to Middle
|
||||
CLIMATE_FAN_MIDDLE = 6,
|
||||
/// The fan mode is set to Focus
|
||||
CLIMATE_FAN_FOCUS = 7,
|
||||
/// The fan mode is set to Diffuse
|
||||
CLIMATE_FAN_DIFFUSE = 8,
|
||||
};
|
||||
|
||||
/// Enum for all modes a climate swing can be in
|
||||
enum ClimateSwingMode : uint8_t {
|
||||
/// The sing mode is set to Off
|
||||
CLIMATE_SWING_OFF = 0,
|
||||
/// The fan mode is set to Both
|
||||
CLIMATE_SWING_BOTH = 1,
|
||||
/// The fan mode is set to Vertical
|
||||
CLIMATE_SWING_VERTICAL = 2,
|
||||
/// The fan mode is set to Horizontal
|
||||
CLIMATE_SWING_HORIZONTAL = 3,
|
||||
};
|
||||
|
||||
/// Convert the given ClimateMode to a human-readable string.
|
||||
const char *climate_mode_to_string(ClimateMode mode);
|
||||
|
||||
/// Convert the given ClimateAction to a human-readable string.
|
||||
const char *climate_action_to_string(ClimateAction action);
|
||||
|
||||
/// Convert the given ClimateFanMode to a human-readable string.
|
||||
const char *climate_fan_mode_to_string(ClimateFanMode mode);
|
||||
|
||||
/// Convert the given ClimateSwingMode to a human-readable string.
|
||||
const char *climate_swing_mode_to_string(ClimateSwingMode mode);
|
||||
|
||||
} // namespace climate
|
||||
} // namespace esphome
|
||||
|
@@ -14,6 +14,10 @@ bool ClimateTraits::supports_mode(ClimateMode mode) const {
|
||||
return this->supports_cool_mode_;
|
||||
case CLIMATE_MODE_HEAT:
|
||||
return this->supports_heat_mode_;
|
||||
case CLIMATE_MODE_FAN_ONLY:
|
||||
return this->supports_fan_only_mode_;
|
||||
case CLIMATE_MODE_DRY:
|
||||
return this->supports_dry_mode_;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -29,6 +33,10 @@ void ClimateTraits::set_supports_two_point_target_temperature(bool supports_two_
|
||||
void ClimateTraits::set_supports_auto_mode(bool supports_auto_mode) { supports_auto_mode_ = supports_auto_mode; }
|
||||
void ClimateTraits::set_supports_cool_mode(bool supports_cool_mode) { supports_cool_mode_ = supports_cool_mode; }
|
||||
void ClimateTraits::set_supports_heat_mode(bool supports_heat_mode) { supports_heat_mode_ = supports_heat_mode; }
|
||||
void ClimateTraits::set_supports_fan_only_mode(bool supports_fan_only_mode) {
|
||||
supports_fan_only_mode_ = supports_fan_only_mode;
|
||||
}
|
||||
void ClimateTraits::set_supports_dry_mode(bool supports_dry_mode) { supports_dry_mode_ = supports_dry_mode; }
|
||||
void ClimateTraits::set_supports_away(bool supports_away) { supports_away_ = supports_away; }
|
||||
void ClimateTraits::set_supports_action(bool supports_action) { supports_action_ = supports_action; }
|
||||
float ClimateTraits::get_visual_min_temperature() const { return visual_min_temperature_; }
|
||||
@@ -55,5 +63,91 @@ void ClimateTraits::set_visual_temperature_step(float temperature_step) { visual
|
||||
bool ClimateTraits::get_supports_away() const { return supports_away_; }
|
||||
bool ClimateTraits::get_supports_action() const { return supports_action_; }
|
||||
|
||||
void ClimateTraits::set_supports_fan_mode_on(bool supports_fan_mode_on) {
|
||||
this->supports_fan_mode_on_ = supports_fan_mode_on;
|
||||
}
|
||||
void ClimateTraits::set_supports_fan_mode_off(bool supports_fan_mode_off) {
|
||||
this->supports_fan_mode_off_ = supports_fan_mode_off;
|
||||
}
|
||||
void ClimateTraits::set_supports_fan_mode_auto(bool supports_fan_mode_auto) {
|
||||
this->supports_fan_mode_auto_ = supports_fan_mode_auto;
|
||||
}
|
||||
void ClimateTraits::set_supports_fan_mode_low(bool supports_fan_mode_low) {
|
||||
this->supports_fan_mode_low_ = supports_fan_mode_low;
|
||||
}
|
||||
void ClimateTraits::set_supports_fan_mode_medium(bool supports_fan_mode_medium) {
|
||||
this->supports_fan_mode_medium_ = supports_fan_mode_medium;
|
||||
}
|
||||
void ClimateTraits::set_supports_fan_mode_high(bool supports_fan_mode_high) {
|
||||
this->supports_fan_mode_high_ = supports_fan_mode_high;
|
||||
}
|
||||
void ClimateTraits::set_supports_fan_mode_middle(bool supports_fan_mode_middle) {
|
||||
this->supports_fan_mode_middle_ = supports_fan_mode_middle;
|
||||
}
|
||||
void ClimateTraits::set_supports_fan_mode_focus(bool supports_fan_mode_focus) {
|
||||
this->supports_fan_mode_focus_ = supports_fan_mode_focus;
|
||||
}
|
||||
void ClimateTraits::set_supports_fan_mode_diffuse(bool supports_fan_mode_diffuse) {
|
||||
this->supports_fan_mode_diffuse_ = supports_fan_mode_diffuse;
|
||||
}
|
||||
bool ClimateTraits::supports_fan_mode(ClimateFanMode fan_mode) const {
|
||||
switch (fan_mode) {
|
||||
case climate::CLIMATE_FAN_ON:
|
||||
return this->supports_fan_mode_on_;
|
||||
case climate::CLIMATE_FAN_OFF:
|
||||
return this->supports_fan_mode_off_;
|
||||
case climate::CLIMATE_FAN_AUTO:
|
||||
return this->supports_fan_mode_auto_;
|
||||
case climate::CLIMATE_FAN_LOW:
|
||||
return this->supports_fan_mode_low_;
|
||||
case climate::CLIMATE_FAN_MEDIUM:
|
||||
return this->supports_fan_mode_medium_;
|
||||
case climate::CLIMATE_FAN_HIGH:
|
||||
return this->supports_fan_mode_high_;
|
||||
case climate::CLIMATE_FAN_MIDDLE:
|
||||
return this->supports_fan_mode_middle_;
|
||||
case climate::CLIMATE_FAN_FOCUS:
|
||||
return this->supports_fan_mode_focus_;
|
||||
case climate::CLIMATE_FAN_DIFFUSE:
|
||||
return this->supports_fan_mode_diffuse_;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ClimateTraits::get_supports_fan_modes() const {
|
||||
return this->supports_fan_mode_on_ || this->supports_fan_mode_off_ || this->supports_fan_mode_auto_ ||
|
||||
this->supports_fan_mode_low_ || this->supports_fan_mode_medium_ || this->supports_fan_mode_high_ ||
|
||||
this->supports_fan_mode_middle_ || this->supports_fan_mode_focus_ || this->supports_fan_mode_diffuse_;
|
||||
}
|
||||
void ClimateTraits::set_supports_swing_mode_off(bool supports_swing_mode_off) {
|
||||
this->supports_swing_mode_off_ = supports_swing_mode_off;
|
||||
}
|
||||
void ClimateTraits::set_supports_swing_mode_both(bool supports_swing_mode_both) {
|
||||
this->supports_swing_mode_both_ = supports_swing_mode_both;
|
||||
}
|
||||
void ClimateTraits::set_supports_swing_mode_vertical(bool supports_swing_mode_vertical) {
|
||||
this->supports_swing_mode_vertical_ = supports_swing_mode_vertical;
|
||||
}
|
||||
void ClimateTraits::set_supports_swing_mode_horizontal(bool supports_swing_mode_horizontal) {
|
||||
this->supports_swing_mode_horizontal_ = supports_swing_mode_horizontal;
|
||||
}
|
||||
bool ClimateTraits::supports_swing_mode(ClimateSwingMode swing_mode) const {
|
||||
switch (swing_mode) {
|
||||
case climate::CLIMATE_SWING_OFF:
|
||||
return this->supports_swing_mode_off_;
|
||||
case climate::CLIMATE_SWING_BOTH:
|
||||
return this->supports_swing_mode_both_;
|
||||
case climate::CLIMATE_SWING_VERTICAL:
|
||||
return this->supports_swing_mode_vertical_;
|
||||
case climate::CLIMATE_SWING_HORIZONTAL:
|
||||
return this->supports_swing_mode_horizontal_;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ClimateTraits::get_supports_swing_modes() const {
|
||||
return this->supports_swing_mode_off_ || this->supports_swing_mode_both_ || supports_swing_mode_vertical_ ||
|
||||
supports_swing_mode_horizontal_;
|
||||
}
|
||||
} // namespace climate
|
||||
} // namespace esphome
|
||||
|
@@ -21,10 +21,16 @@ namespace climate {
|
||||
* - auto mode (automatic control)
|
||||
* - cool mode (lowers current temperature)
|
||||
* - heat mode (increases current temperature)
|
||||
* - dry mode (removes humidity from air)
|
||||
* - fan mode (only turns on fan)
|
||||
* - supports away - away mode means that the climate device supports two different
|
||||
* target temperature settings: one target temp setting for "away" mode and one for non-away mode.
|
||||
* - supports action - if the climate device supports reporting the active
|
||||
* current action of the device with the action property.
|
||||
* - supports fan modes - optionally, if it has a fan which can be configured in different ways:
|
||||
* - on, off, auto, high, medium, low, middle, focus, diffuse
|
||||
* - supports swing modes - optionally, if it has a swing which can be configured in different ways:
|
||||
* - off, both, vertical, horizontal
|
||||
*
|
||||
* This class also contains static data for the climate device display:
|
||||
* - visual min/max temperature - tells the frontend what range of temperatures the climate device
|
||||
@@ -41,11 +47,30 @@ class ClimateTraits {
|
||||
void set_supports_auto_mode(bool supports_auto_mode);
|
||||
void set_supports_cool_mode(bool supports_cool_mode);
|
||||
void set_supports_heat_mode(bool supports_heat_mode);
|
||||
void set_supports_fan_only_mode(bool supports_fan_only_mode);
|
||||
void set_supports_dry_mode(bool supports_dry_mode);
|
||||
void set_supports_away(bool supports_away);
|
||||
bool get_supports_away() const;
|
||||
void set_supports_action(bool supports_action);
|
||||
bool get_supports_action() const;
|
||||
bool supports_mode(ClimateMode mode) const;
|
||||
void set_supports_fan_mode_on(bool supports_fan_mode_on);
|
||||
void set_supports_fan_mode_off(bool supports_fan_mode_off);
|
||||
void set_supports_fan_mode_auto(bool supports_fan_mode_auto);
|
||||
void set_supports_fan_mode_low(bool supports_fan_mode_low);
|
||||
void set_supports_fan_mode_medium(bool supports_fan_mode_medium);
|
||||
void set_supports_fan_mode_high(bool supports_fan_mode_high);
|
||||
void set_supports_fan_mode_middle(bool supports_fan_mode_middle);
|
||||
void set_supports_fan_mode_focus(bool supports_fan_mode_focus);
|
||||
void set_supports_fan_mode_diffuse(bool supports_fan_mode_diffuse);
|
||||
bool supports_fan_mode(ClimateFanMode fan_mode) const;
|
||||
bool get_supports_fan_modes() const;
|
||||
void set_supports_swing_mode_off(bool supports_swing_mode_off);
|
||||
void set_supports_swing_mode_both(bool supports_swing_mode_both);
|
||||
void set_supports_swing_mode_vertical(bool supports_swing_mode_vertical);
|
||||
void set_supports_swing_mode_horizontal(bool supports_swing_mode_horizontal);
|
||||
bool supports_swing_mode(ClimateSwingMode swing_mode) const;
|
||||
bool get_supports_swing_modes() const;
|
||||
|
||||
float get_visual_min_temperature() const;
|
||||
void set_visual_min_temperature(float visual_min_temperature);
|
||||
@@ -61,8 +86,23 @@ class ClimateTraits {
|
||||
bool supports_auto_mode_{false};
|
||||
bool supports_cool_mode_{false};
|
||||
bool supports_heat_mode_{false};
|
||||
bool supports_fan_only_mode_{false};
|
||||
bool supports_dry_mode_{false};
|
||||
bool supports_away_{false};
|
||||
bool supports_action_{false};
|
||||
bool supports_fan_mode_on_{false};
|
||||
bool supports_fan_mode_off_{false};
|
||||
bool supports_fan_mode_auto_{false};
|
||||
bool supports_fan_mode_low_{false};
|
||||
bool supports_fan_mode_medium_{false};
|
||||
bool supports_fan_mode_high_{false};
|
||||
bool supports_fan_mode_middle_{false};
|
||||
bool supports_fan_mode_focus_{false};
|
||||
bool supports_fan_mode_diffuse_{false};
|
||||
bool supports_swing_mode_off_{false};
|
||||
bool supports_swing_mode_both_{false};
|
||||
bool supports_swing_mode_vertical_{false};
|
||||
bool supports_swing_mode_horizontal_{false};
|
||||
|
||||
float visual_min_temperature_{10};
|
||||
float visual_max_temperature_{30};
|
||||
|
@@ -6,6 +6,7 @@ from esphome.const import CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT, CONF_SENSOR
|
||||
from esphome.core import coroutine
|
||||
|
||||
AUTO_LOAD = ['sensor', 'remote_base']
|
||||
CODEOWNERS = ['@glmnet']
|
||||
|
||||
climate_ir_ns = cg.esphome_ns.namespace('climate_ir')
|
||||
ClimateIR = climate_ir_ns.class_('ClimateIR', climate.Climate, cg.Component,
|
||||
|
@@ -12,11 +12,60 @@ climate::ClimateTraits ClimateIR::traits() {
|
||||
traits.set_supports_auto_mode(true);
|
||||
traits.set_supports_cool_mode(this->supports_cool_);
|
||||
traits.set_supports_heat_mode(this->supports_heat_);
|
||||
traits.set_supports_dry_mode(this->supports_dry_);
|
||||
traits.set_supports_fan_only_mode(this->supports_fan_only_);
|
||||
traits.set_supports_two_point_target_temperature(false);
|
||||
traits.set_supports_away(false);
|
||||
traits.set_visual_min_temperature(this->minimum_temperature_);
|
||||
traits.set_visual_max_temperature(this->maximum_temperature_);
|
||||
traits.set_visual_temperature_step(this->temperature_step_);
|
||||
for (auto fan_mode : this->fan_modes_) {
|
||||
switch (fan_mode) {
|
||||
case climate::CLIMATE_FAN_AUTO:
|
||||
traits.set_supports_fan_mode_auto(true);
|
||||
break;
|
||||
case climate::CLIMATE_FAN_DIFFUSE:
|
||||
traits.set_supports_fan_mode_diffuse(true);
|
||||
break;
|
||||
case climate::CLIMATE_FAN_FOCUS:
|
||||
traits.set_supports_fan_mode_focus(true);
|
||||
break;
|
||||
case climate::CLIMATE_FAN_HIGH:
|
||||
traits.set_supports_fan_mode_high(true);
|
||||
break;
|
||||
case climate::CLIMATE_FAN_LOW:
|
||||
traits.set_supports_fan_mode_low(true);
|
||||
break;
|
||||
case climate::CLIMATE_FAN_MEDIUM:
|
||||
traits.set_supports_fan_mode_medium(true);
|
||||
break;
|
||||
case climate::CLIMATE_FAN_MIDDLE:
|
||||
traits.set_supports_fan_mode_middle(true);
|
||||
break;
|
||||
case climate::CLIMATE_FAN_OFF:
|
||||
traits.set_supports_fan_mode_off(true);
|
||||
break;
|
||||
case climate::CLIMATE_FAN_ON:
|
||||
traits.set_supports_fan_mode_on(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (auto swing_mode : this->swing_modes_) {
|
||||
switch (swing_mode) {
|
||||
case climate::CLIMATE_SWING_OFF:
|
||||
traits.set_supports_swing_mode_off(true);
|
||||
break;
|
||||
case climate::CLIMATE_SWING_BOTH:
|
||||
traits.set_supports_swing_mode_both(true);
|
||||
break;
|
||||
case climate::CLIMATE_SWING_VERTICAL:
|
||||
traits.set_supports_swing_mode_vertical(true);
|
||||
break;
|
||||
case climate::CLIMATE_SWING_HORIZONTAL:
|
||||
traits.set_supports_swing_mode_horizontal(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return traits;
|
||||
}
|
||||
|
||||
@@ -40,6 +89,8 @@ void ClimateIR::setup() {
|
||||
// initialize target temperature to some value so that it's not NAN
|
||||
this->target_temperature =
|
||||
roundf(clamp(this->current_temperature, this->minimum_temperature_, this->maximum_temperature_));
|
||||
this->fan_mode = climate::CLIMATE_FAN_AUTO;
|
||||
this->swing_mode = climate::CLIMATE_SWING_OFF;
|
||||
}
|
||||
// Never send nan to HA
|
||||
if (isnan(this->target_temperature))
|
||||
@@ -51,7 +102,10 @@ void ClimateIR::control(const climate::ClimateCall &call) {
|
||||
this->mode = *call.get_mode();
|
||||
if (call.get_target_temperature().has_value())
|
||||
this->target_temperature = *call.get_target_temperature();
|
||||
|
||||
if (call.get_fan_mode().has_value())
|
||||
this->fan_mode = *call.get_fan_mode();
|
||||
if (call.get_swing_mode().has_value())
|
||||
this->swing_mode = *call.get_swing_mode();
|
||||
this->transmit_state();
|
||||
this->publish_state();
|
||||
}
|
||||
|
@@ -18,10 +18,17 @@ namespace climate_ir {
|
||||
*/
|
||||
class ClimateIR : public climate::Climate, public Component, public remote_base::RemoteReceiverListener {
|
||||
public:
|
||||
ClimateIR(float minimum_temperature, float maximum_temperature, float temperature_step = 1.0f) {
|
||||
ClimateIR(float minimum_temperature, float maximum_temperature, float temperature_step = 1.0f,
|
||||
bool supports_dry = false, bool supports_fan_only = false,
|
||||
std::vector<climate::ClimateFanMode> fan_modes = {},
|
||||
std::vector<climate::ClimateSwingMode> swing_modes = {}) {
|
||||
this->minimum_temperature_ = minimum_temperature;
|
||||
this->maximum_temperature_ = maximum_temperature;
|
||||
this->temperature_step_ = temperature_step;
|
||||
this->supports_dry_ = supports_dry;
|
||||
this->supports_fan_only_ = supports_fan_only;
|
||||
this->fan_modes_ = fan_modes;
|
||||
this->swing_modes_ = swing_modes;
|
||||
}
|
||||
|
||||
void setup() override;
|
||||
@@ -44,8 +51,15 @@ class ClimateIR : public climate::Climate, public Component, public remote_base:
|
||||
/// Transmit via IR the state of this climate controller.
|
||||
virtual void transmit_state() = 0;
|
||||
|
||||
// Dummy implement on_receive so implementation is optional for inheritors
|
||||
bool on_receive(remote_base::RemoteReceiveData data) override { return false; };
|
||||
|
||||
bool supports_cool_{true};
|
||||
bool supports_heat_{true};
|
||||
bool supports_dry_{false};
|
||||
bool supports_fan_only_{false};
|
||||
std::vector<climate::ClimateFanMode> fan_modes_ = {};
|
||||
std::vector<climate::ClimateSwingMode> swing_modes_ = {};
|
||||
|
||||
remote_transmitter::RemoteTransmitterComponent *transmitter_;
|
||||
sensor::Sensor *sensor_{nullptr};
|
||||
|
0
esphome/components/climate_ir_lg/__init__.py
Normal file
0
esphome/components/climate_ir_lg/__init__.py
Normal file
18
esphome/components/climate_ir_lg/climate.py
Normal file
18
esphome/components/climate_ir_lg/climate.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import climate_ir
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
AUTO_LOAD = ['climate_ir']
|
||||
|
||||
climate_ir_lg_ns = cg.esphome_ns.namespace('climate_ir_lg')
|
||||
LgIrClimate = climate_ir_lg_ns.class_('LgIrClimate', climate_ir.ClimateIR)
|
||||
|
||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(LgIrClimate),
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield climate_ir.register_climate_ir(var, config)
|
204
esphome/components/climate_ir_lg/climate_ir_lg.cpp
Normal file
204
esphome/components/climate_ir_lg/climate_ir_lg.cpp
Normal file
@@ -0,0 +1,204 @@
|
||||
#include "climate_ir_lg.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace climate_ir_lg {
|
||||
|
||||
static const char *TAG = "climate.climate_ir_lg";
|
||||
|
||||
const uint32_t COMMAND_ON = 0x00000;
|
||||
const uint32_t COMMAND_ON_AI = 0x03000;
|
||||
const uint32_t COMMAND_COOL = 0x08000;
|
||||
const uint32_t COMMAND_OFF = 0xC0000;
|
||||
const uint32_t COMMAND_SWING = 0x10000;
|
||||
// On, 25C, Mode: Auto, Fan: Auto, Zone Follow: Off, Sensor Temp: Ignore.
|
||||
const uint32_t COMMAND_AUTO = 0x0B000;
|
||||
const uint32_t COMMAND_DRY_FAN = 0x09000;
|
||||
|
||||
const uint32_t COMMAND_MASK = 0xFF000;
|
||||
|
||||
const uint32_t FAN_MASK = 0xF0;
|
||||
const uint32_t FAN_AUTO = 0x50;
|
||||
const uint32_t FAN_MIN = 0x00;
|
||||
const uint32_t FAN_MED = 0x20;
|
||||
const uint32_t FAN_MAX = 0x40;
|
||||
|
||||
// Temperature
|
||||
const uint8_t TEMP_RANGE = TEMP_MAX - TEMP_MIN + 1;
|
||||
const uint32_t TEMP_MASK = 0XF00;
|
||||
const uint32_t TEMP_SHIFT = 8;
|
||||
|
||||
// Constants
|
||||
static const uint32_t HEADER_HIGH_US = 8000;
|
||||
static const uint32_t HEADER_LOW_US = 4000;
|
||||
static const uint32_t BIT_HIGH_US = 600;
|
||||
static const uint32_t BIT_ONE_LOW_US = 1600;
|
||||
static const uint32_t BIT_ZERO_LOW_US = 550;
|
||||
|
||||
const uint16_t BITS = 28;
|
||||
|
||||
void LgIrClimate::transmit_state() {
|
||||
uint32_t remote_state = 0x8800000;
|
||||
|
||||
// ESP_LOGD(TAG, "climate_lg_ir mode_before_ code: 0x%02X", modeBefore_);
|
||||
if (send_swing_cmd_) {
|
||||
send_swing_cmd_ = false;
|
||||
remote_state |= COMMAND_SWING;
|
||||
} else {
|
||||
if (mode_before_ == climate::CLIMATE_MODE_OFF && this->mode == climate::CLIMATE_MODE_AUTO) {
|
||||
remote_state |= COMMAND_ON_AI;
|
||||
} else if (mode_before_ == climate::CLIMATE_MODE_OFF && this->mode != climate::CLIMATE_MODE_OFF) {
|
||||
remote_state |= COMMAND_ON;
|
||||
this->mode = climate::CLIMATE_MODE_COOL;
|
||||
} else {
|
||||
switch (this->mode) {
|
||||
case climate::CLIMATE_MODE_COOL:
|
||||
remote_state |= COMMAND_COOL;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_AUTO:
|
||||
remote_state |= COMMAND_AUTO;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_DRY:
|
||||
remote_state |= COMMAND_DRY_FAN;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_OFF:
|
||||
default:
|
||||
remote_state |= COMMAND_OFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mode_before_ = this->mode;
|
||||
|
||||
ESP_LOGD(TAG, "climate_lg_ir mode code: 0x%02X", this->mode);
|
||||
|
||||
if (this->mode == climate::CLIMATE_MODE_OFF) {
|
||||
remote_state |= FAN_AUTO;
|
||||
} else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_DRY) {
|
||||
switch (this->fan_mode) {
|
||||
case climate::CLIMATE_FAN_HIGH:
|
||||
remote_state |= FAN_MAX;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_MEDIUM:
|
||||
remote_state |= FAN_MED;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_LOW:
|
||||
remote_state |= FAN_MIN;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_AUTO:
|
||||
default:
|
||||
remote_state |= FAN_AUTO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->mode == climate::CLIMATE_MODE_AUTO) {
|
||||
this->fan_mode = climate::CLIMATE_FAN_AUTO;
|
||||
// remote_state |= FAN_MODE_AUTO_DRY;
|
||||
}
|
||||
if (this->mode == climate::CLIMATE_MODE_COOL) {
|
||||
auto temp = (uint8_t) roundf(clamp(this->target_temperature, TEMP_MIN, TEMP_MAX));
|
||||
remote_state |= ((temp - 15) << TEMP_SHIFT);
|
||||
}
|
||||
}
|
||||
transmit_(remote_state);
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
bool LgIrClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
uint8_t nbits = 0;
|
||||
uint32_t remote_state = 0;
|
||||
|
||||
if (!data.expect_item(HEADER_HIGH_US, HEADER_LOW_US))
|
||||
return false;
|
||||
|
||||
for (nbits = 0; nbits < 32; nbits++) {
|
||||
if (data.expect_item(BIT_HIGH_US, BIT_ONE_LOW_US)) {
|
||||
remote_state = (remote_state << 1) | 1;
|
||||
} else if (data.expect_item(BIT_HIGH_US, BIT_ZERO_LOW_US)) {
|
||||
remote_state = (remote_state << 1) | 0;
|
||||
} else if (nbits == BITS) {
|
||||
break;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Decoded 0x%02X", remote_state);
|
||||
if ((remote_state & 0xFF00000) != 0x8800000)
|
||||
return false;
|
||||
|
||||
if ((remote_state & COMMAND_MASK) == COMMAND_ON) {
|
||||
this->mode = climate::CLIMATE_MODE_COOL;
|
||||
} else if ((remote_state & COMMAND_MASK) == COMMAND_ON_AI) {
|
||||
this->mode = climate::CLIMATE_MODE_AUTO;
|
||||
}
|
||||
|
||||
if ((remote_state & COMMAND_MASK) == COMMAND_OFF) {
|
||||
this->mode = climate::CLIMATE_MODE_OFF;
|
||||
} else if ((remote_state & COMMAND_MASK) == COMMAND_SWING) {
|
||||
this->swing_mode =
|
||||
this->swing_mode == climate::CLIMATE_SWING_OFF ? climate::CLIMATE_SWING_VERTICAL : climate::CLIMATE_SWING_OFF;
|
||||
} else {
|
||||
if ((remote_state & COMMAND_MASK) == COMMAND_AUTO)
|
||||
this->mode = climate::CLIMATE_MODE_AUTO;
|
||||
else if ((remote_state & COMMAND_MASK) == COMMAND_DRY_FAN) {
|
||||
this->mode = climate::CLIMATE_MODE_DRY;
|
||||
} else {
|
||||
this->mode = climate::CLIMATE_MODE_COOL;
|
||||
}
|
||||
}
|
||||
|
||||
// Temperature
|
||||
if (this->mode == climate::CLIMATE_MODE_COOL)
|
||||
this->target_temperature = ((remote_state & TEMP_MASK) >> TEMP_SHIFT) + 15;
|
||||
|
||||
// Fan Speed
|
||||
if (this->mode == climate::CLIMATE_MODE_AUTO) {
|
||||
this->fan_mode = climate::CLIMATE_FAN_AUTO;
|
||||
} else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_DRY) {
|
||||
if ((remote_state & FAN_MASK) == FAN_AUTO)
|
||||
this->fan_mode = climate::CLIMATE_FAN_AUTO;
|
||||
else if ((remote_state & FAN_MASK) == FAN_MIN)
|
||||
this->fan_mode = climate::CLIMATE_FAN_LOW;
|
||||
else if ((remote_state & FAN_MASK) == FAN_MED)
|
||||
this->fan_mode = climate::CLIMATE_FAN_MEDIUM;
|
||||
else if ((remote_state & FAN_MASK) == FAN_MAX)
|
||||
this->fan_mode = climate::CLIMATE_FAN_HIGH;
|
||||
}
|
||||
this->publish_state();
|
||||
|
||||
return true;
|
||||
}
|
||||
void LgIrClimate::transmit_(uint32_t value) {
|
||||
calc_checksum_(value);
|
||||
ESP_LOGD(TAG, "Sending climate_lg_ir code: 0x%02X", value);
|
||||
|
||||
auto transmit = this->transmitter_->transmit();
|
||||
auto data = transmit.get_data();
|
||||
|
||||
data->set_carrier_frequency(38000);
|
||||
data->reserve(2 + BITS * 2u);
|
||||
|
||||
data->item(HEADER_HIGH_US, HEADER_LOW_US);
|
||||
|
||||
for (uint32_t mask = 1UL << (BITS - 1); mask != 0; mask >>= 1) {
|
||||
if (value & mask)
|
||||
data->item(BIT_HIGH_US, BIT_ONE_LOW_US);
|
||||
else
|
||||
data->item(BIT_HIGH_US, BIT_ZERO_LOW_US);
|
||||
}
|
||||
data->mark(BIT_HIGH_US);
|
||||
transmit.perform();
|
||||
}
|
||||
void LgIrClimate::calc_checksum_(uint32_t &value) {
|
||||
uint32_t mask = 0xF;
|
||||
uint32_t sum = 0;
|
||||
for (uint8_t i = 1; i < 8; i++) {
|
||||
sum += (value & (mask << (i * 4))) >> (i * 4);
|
||||
}
|
||||
|
||||
value |= (sum & mask);
|
||||
}
|
||||
|
||||
} // namespace climate_ir_lg
|
||||
} // namespace esphome
|
44
esphome/components/climate_ir_lg/climate_ir_lg.h
Normal file
44
esphome/components/climate_ir_lg/climate_ir_lg.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/climate_ir/climate_ir.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace climate_ir_lg {
|
||||
|
||||
// Temperature
|
||||
const uint8_t TEMP_MIN = 18; // Celsius
|
||||
const uint8_t TEMP_MAX = 30; // Celsius
|
||||
|
||||
class LgIrClimate : public climate_ir::ClimateIR {
|
||||
public:
|
||||
LgIrClimate()
|
||||
: climate_ir::ClimateIR(TEMP_MIN, TEMP_MAX, 1.0f, true, false,
|
||||
{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM,
|
||||
climate::CLIMATE_FAN_HIGH},
|
||||
{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL}) {}
|
||||
|
||||
/// Override control to change settings of the climate device.
|
||||
void control(const climate::ClimateCall &call) override {
|
||||
send_swing_cmd_ = call.get_swing_mode().has_value();
|
||||
// swing resets after unit powered off
|
||||
if (call.get_mode().has_value() && *call.get_mode() == climate::CLIMATE_MODE_OFF)
|
||||
this->swing_mode = climate::CLIMATE_SWING_OFF;
|
||||
climate_ir::ClimateIR::control(call);
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Transmit via IR the state of this climate controller.
|
||||
void transmit_state() override;
|
||||
/// Handle received IR Buffer
|
||||
bool on_receive(remote_base::RemoteReceiveData data) override;
|
||||
|
||||
bool send_swing_cmd_{false};
|
||||
|
||||
void calc_checksum_(uint32_t &value);
|
||||
void transmit_(uint32_t value);
|
||||
|
||||
climate::ClimateMode mode_before_{climate::CLIMATE_MODE_OFF};
|
||||
};
|
||||
|
||||
} // namespace climate_ir_lg
|
||||
} // namespace esphome
|
23
esphome/components/color/__init__.py
Normal file
23
esphome/components/color/__init__.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from esphome import config_validation as cv
|
||||
from esphome import codegen as cg
|
||||
from esphome.const import CONF_BLUE, CONF_GREEN, CONF_ID, CONF_RED, CONF_WHITE
|
||||
|
||||
ColorStruct = cg.esphome_ns.struct('Color')
|
||||
|
||||
MULTI_CONF = True
|
||||
CONFIG_SCHEMA = cv.Schema({
|
||||
cv.Required(CONF_ID): cv.declare_id(ColorStruct),
|
||||
cv.Optional(CONF_RED, default=0.0): cv.percentage,
|
||||
cv.Optional(CONF_GREEN, default=0.0): cv.percentage,
|
||||
cv.Optional(CONF_BLUE, default=0.0): cv.percentage,
|
||||
cv.Optional(CONF_WHITE, default=0.0): cv.percentage,
|
||||
}).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
cg.variable(config[CONF_ID], cg.StructInitializer(
|
||||
ColorStruct,
|
||||
('r', config[CONF_RED]),
|
||||
('g', config[CONF_GREEN]),
|
||||
('b', config[CONF_BLUE]),
|
||||
('w', config[CONF_WHITE])))
|
@@ -4,6 +4,7 @@ from esphome.components import climate_ir
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
AUTO_LOAD = ['climate_ir']
|
||||
CODEOWNERS = ['@glmnet']
|
||||
|
||||
coolix_ns = cg.esphome_ns.namespace('coolix')
|
||||
CoolixClimate = coolix_ns.class_('CoolixClimate', climate_ir.ClimateIR)
|
||||
|
@@ -12,15 +12,13 @@ const uint32_t COOLIX_LED = 0xB5F5A5;
|
||||
const uint32_t COOLIX_SILENCE_FP = 0xB5F5B6;
|
||||
|
||||
// On, 25C, Mode: Auto, Fan: Auto, Zone Follow: Off, Sensor Temp: Ignore.
|
||||
const uint32_t COOLIX_DEFAULT_STATE = 0xB2BFC8;
|
||||
const uint32_t COOLIX_DEFAULT_STATE_AUTO_24_FAN = 0xB21F48;
|
||||
const uint8_t COOLIX_COOL = 0b0000;
|
||||
const uint8_t COOLIX_DRY_FAN = 0b0100;
|
||||
const uint8_t COOLIX_AUTO = 0b1000;
|
||||
const uint8_t COOLIX_HEAT = 0b1100;
|
||||
const uint32_t COOLIX_MODE_MASK = 0b1100;
|
||||
const uint32_t COOLIX_FAN_MASK = 0xF000;
|
||||
const uint32_t COOLIX_FAN_DRY = 0x1000;
|
||||
const uint32_t COOLIX_FAN_MODE_AUTO_DRY = 0x1000;
|
||||
const uint32_t COOLIX_FAN_AUTO = 0xB000;
|
||||
const uint32_t COOLIX_FAN_MIN = 0x9000;
|
||||
const uint32_t COOLIX_FAN_MED = 0x5000;
|
||||
@@ -28,23 +26,23 @@ const uint32_t COOLIX_FAN_MAX = 0x3000;
|
||||
|
||||
// Temperature
|
||||
const uint8_t COOLIX_TEMP_RANGE = COOLIX_TEMP_MAX - COOLIX_TEMP_MIN + 1;
|
||||
const uint8_t COOLIX_FAN_TEMP_CODE = 0b1110; // Part of Fan Mode.
|
||||
const uint8_t COOLIX_FAN_TEMP_CODE = 0b11100000; // Part of Fan Mode.
|
||||
const uint32_t COOLIX_TEMP_MASK = 0b11110000;
|
||||
const uint8_t COOLIX_TEMP_MAP[COOLIX_TEMP_RANGE] = {
|
||||
0b0000, // 17C
|
||||
0b0001, // 18c
|
||||
0b0011, // 19C
|
||||
0b0010, // 20C
|
||||
0b0110, // 21C
|
||||
0b0111, // 22C
|
||||
0b0101, // 23C
|
||||
0b0100, // 24C
|
||||
0b1100, // 25C
|
||||
0b1101, // 26C
|
||||
0b1001, // 27C
|
||||
0b1000, // 28C
|
||||
0b1010, // 29C
|
||||
0b1011 // 30C
|
||||
0b00000000, // 17C
|
||||
0b00010000, // 18c
|
||||
0b00110000, // 19C
|
||||
0b00100000, // 20C
|
||||
0b01100000, // 21C
|
||||
0b01110000, // 22C
|
||||
0b01010000, // 23C
|
||||
0b01000000, // 24C
|
||||
0b11000000, // 25C
|
||||
0b11010000, // 26C
|
||||
0b10010000, // 27C
|
||||
0b10000000, // 28C
|
||||
0b10100000, // 29C
|
||||
0b10110000 // 30C
|
||||
};
|
||||
|
||||
// Constants
|
||||
@@ -59,29 +57,60 @@ static const uint32_t FOOTER_SPACE_US = HEADER_SPACE_US;
|
||||
const uint16_t COOLIX_BITS = 24;
|
||||
|
||||
void CoolixClimate::transmit_state() {
|
||||
uint32_t remote_state;
|
||||
uint32_t remote_state = 0xB20F00;
|
||||
|
||||
switch (this->mode) {
|
||||
case climate::CLIMATE_MODE_COOL:
|
||||
remote_state = (COOLIX_DEFAULT_STATE & ~COOLIX_MODE_MASK) | COOLIX_COOL;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_HEAT:
|
||||
remote_state = (COOLIX_DEFAULT_STATE & ~COOLIX_MODE_MASK) | COOLIX_HEAT;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_AUTO:
|
||||
remote_state = COOLIX_DEFAULT_STATE_AUTO_24_FAN;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_OFF:
|
||||
default:
|
||||
remote_state = COOLIX_OFF;
|
||||
break;
|
||||
if (send_swing_cmd_) {
|
||||
send_swing_cmd_ = false;
|
||||
remote_state = COOLIX_SWING;
|
||||
} else {
|
||||
switch (this->mode) {
|
||||
case climate::CLIMATE_MODE_COOL:
|
||||
remote_state |= COOLIX_COOL;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_HEAT:
|
||||
remote_state |= COOLIX_HEAT;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_AUTO:
|
||||
remote_state |= COOLIX_AUTO;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_FAN_ONLY:
|
||||
case climate::CLIMATE_MODE_DRY:
|
||||
remote_state |= COOLIX_DRY_FAN;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_OFF:
|
||||
default:
|
||||
remote_state = COOLIX_OFF;
|
||||
break;
|
||||
}
|
||||
if (this->mode != climate::CLIMATE_MODE_OFF) {
|
||||
if (this->mode != climate::CLIMATE_MODE_FAN_ONLY) {
|
||||
auto temp = (uint8_t) roundf(clamp(this->target_temperature, COOLIX_TEMP_MIN, COOLIX_TEMP_MAX));
|
||||
remote_state |= COOLIX_TEMP_MAP[temp - COOLIX_TEMP_MIN];
|
||||
} else {
|
||||
remote_state |= COOLIX_FAN_TEMP_CODE;
|
||||
}
|
||||
if (this->mode == climate::CLIMATE_MODE_AUTO || this->mode == climate::CLIMATE_MODE_DRY) {
|
||||
this->fan_mode = climate::CLIMATE_FAN_AUTO;
|
||||
remote_state |= COOLIX_FAN_MODE_AUTO_DRY;
|
||||
} else {
|
||||
switch (this->fan_mode) {
|
||||
case climate::CLIMATE_FAN_HIGH:
|
||||
remote_state |= COOLIX_FAN_MAX;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_MEDIUM:
|
||||
remote_state |= COOLIX_FAN_MED;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_LOW:
|
||||
remote_state |= COOLIX_FAN_MIN;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_AUTO:
|
||||
default:
|
||||
remote_state |= COOLIX_FAN_AUTO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this->mode != climate::CLIMATE_MODE_OFF) {
|
||||
auto temp = (uint8_t) roundf(clamp(this->target_temperature, COOLIX_TEMP_MIN, COOLIX_TEMP_MAX));
|
||||
remote_state &= ~COOLIX_TEMP_MASK; // Clear the old temp.
|
||||
remote_state |= COOLIX_TEMP_MAP[temp - COOLIX_TEMP_MIN] << 4;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "Sending coolix code: 0x%02X", remote_state);
|
||||
|
||||
auto transmit = this->transmitter_->transmit();
|
||||
@@ -161,35 +190,35 @@ bool CoolixClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
|
||||
if (remote_state == COOLIX_OFF) {
|
||||
this->mode = climate::CLIMATE_MODE_OFF;
|
||||
} else if (remote_state == COOLIX_SWING) {
|
||||
this->swing_mode =
|
||||
this->swing_mode == climate::CLIMATE_SWING_OFF ? climate::CLIMATE_SWING_VERTICAL : climate::CLIMATE_SWING_OFF;
|
||||
} else {
|
||||
if ((remote_state & COOLIX_MODE_MASK) == COOLIX_HEAT)
|
||||
this->mode = climate::CLIMATE_MODE_HEAT;
|
||||
else if ((remote_state & COOLIX_MODE_MASK) == COOLIX_AUTO)
|
||||
this->mode = climate::CLIMATE_MODE_AUTO;
|
||||
else if ((remote_state & COOLIX_MODE_MASK) == COOLIX_DRY_FAN) {
|
||||
// climate::CLIMATE_MODE_DRY;
|
||||
if ((remote_state & COOLIX_FAN_MASK) == COOLIX_FAN_DRY)
|
||||
ESP_LOGV(TAG, "Not supported DRY mode. Reporting AUTO");
|
||||
if ((remote_state & COOLIX_FAN_MASK) == COOLIX_FAN_MODE_AUTO_DRY)
|
||||
this->mode = climate::CLIMATE_MODE_DRY;
|
||||
else
|
||||
ESP_LOGV(TAG, "Not supported FAN Auto mode. Reporting AUTO");
|
||||
this->mode = climate::CLIMATE_MODE_AUTO;
|
||||
this->mode = climate::CLIMATE_MODE_FAN_ONLY;
|
||||
} else
|
||||
this->mode = climate::CLIMATE_MODE_COOL;
|
||||
|
||||
// Fan Speed
|
||||
// When climate::CLIMATE_MODE_DRY is implemented replace following line with this:
|
||||
// if ((remote_state & COOLIX_FAN_AUTO) == COOLIX_FAN_AUTO || this->mode == climate::CLIMATE_MODE_DRY)
|
||||
if ((remote_state & COOLIX_FAN_AUTO) == COOLIX_FAN_AUTO)
|
||||
ESP_LOGV(TAG, "Not supported FAN speed AUTO");
|
||||
if ((remote_state & COOLIX_FAN_AUTO) == COOLIX_FAN_AUTO || this->mode == climate::CLIMATE_MODE_AUTO ||
|
||||
this->mode == climate::CLIMATE_MODE_DRY)
|
||||
this->fan_mode = climate::CLIMATE_FAN_AUTO;
|
||||
else if ((remote_state & COOLIX_FAN_MIN) == COOLIX_FAN_MIN)
|
||||
ESP_LOGV(TAG, "Not supported FAN speed MIN");
|
||||
this->fan_mode = climate::CLIMATE_FAN_LOW;
|
||||
else if ((remote_state & COOLIX_FAN_MED) == COOLIX_FAN_MED)
|
||||
ESP_LOGV(TAG, "Not supported FAN speed MED");
|
||||
this->fan_mode = climate::CLIMATE_FAN_MEDIUM;
|
||||
else if ((remote_state & COOLIX_FAN_MAX) == COOLIX_FAN_MAX)
|
||||
ESP_LOGV(TAG, "Not supported FAN speed MAX");
|
||||
this->fan_mode = climate::CLIMATE_FAN_HIGH;
|
||||
|
||||
// Temperature
|
||||
uint8_t temperature_code = (remote_state & COOLIX_TEMP_MASK) >> 4;
|
||||
uint8_t temperature_code = remote_state & COOLIX_TEMP_MASK;
|
||||
for (uint8_t i = 0; i < COOLIX_TEMP_RANGE; i++)
|
||||
if (COOLIX_TEMP_MAP[i] == temperature_code)
|
||||
this->target_temperature = i + COOLIX_TEMP_MIN;
|
||||
|
@@ -11,13 +11,28 @@ const uint8_t COOLIX_TEMP_MAX = 30; // Celsius
|
||||
|
||||
class CoolixClimate : public climate_ir::ClimateIR {
|
||||
public:
|
||||
CoolixClimate() : climate_ir::ClimateIR(COOLIX_TEMP_MIN, COOLIX_TEMP_MAX) {}
|
||||
CoolixClimate()
|
||||
: climate_ir::ClimateIR(COOLIX_TEMP_MIN, COOLIX_TEMP_MAX, 1.0f, true, true,
|
||||
{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM,
|
||||
climate::CLIMATE_FAN_HIGH},
|
||||
{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL}) {}
|
||||
|
||||
/// Override control to change settings of the climate device.
|
||||
void control(const climate::ClimateCall &call) override {
|
||||
send_swing_cmd_ = call.get_swing_mode().has_value();
|
||||
// swing resets after unit powered off
|
||||
if (call.get_mode().has_value() && *call.get_mode() == climate::CLIMATE_MODE_OFF)
|
||||
this->swing_mode = climate::CLIMATE_SWING_OFF;
|
||||
climate_ir::ClimateIR::control(call);
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Transmit via IR the state of this climate controller.
|
||||
void transmit_state() override;
|
||||
/// Handle received IR Buffer
|
||||
bool on_receive(remote_base::RemoteReceiveData data) override;
|
||||
|
||||
bool send_swing_cmd_{false};
|
||||
};
|
||||
|
||||
} // namespace coolix
|
||||
|
@@ -9,9 +9,10 @@ from esphome.core import CORE, coroutine, coroutine_with_priority
|
||||
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
CODEOWNERS = ['@esphome/core']
|
||||
DEVICE_CLASSES = [
|
||||
'', 'awning', 'blind', 'curtain', 'damper', 'door', 'garage',
|
||||
'shade', 'shutter', 'window'
|
||||
'gate', 'shade', 'shutter', 'window'
|
||||
]
|
||||
|
||||
cover_ns = cg.esphome_ns.namespace('cover')
|
||||
|
@@ -41,6 +41,10 @@ template<typename... Ts> class ControlAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit ControlAction(Cover *cover) : cover_(cover) {}
|
||||
|
||||
TEMPLATABLE_VALUE(bool, stop)
|
||||
TEMPLATABLE_VALUE(float, position)
|
||||
TEMPLATABLE_VALUE(float, tilt)
|
||||
|
||||
void play(Ts... x) override {
|
||||
auto call = this->cover_->make_call();
|
||||
if (this->stop_.has_value())
|
||||
@@ -52,10 +56,6 @@ template<typename... Ts> class ControlAction : public Action<Ts...> {
|
||||
call.perform();
|
||||
}
|
||||
|
||||
TEMPLATABLE_VALUE(bool, stop)
|
||||
TEMPLATABLE_VALUE(float, position)
|
||||
TEMPLATABLE_VALUE(float, tilt)
|
||||
|
||||
protected:
|
||||
Cover *cover_;
|
||||
};
|
||||
@@ -63,6 +63,10 @@ template<typename... Ts> class ControlAction : public Action<Ts...> {
|
||||
template<typename... Ts> class CoverPublishAction : public Action<Ts...> {
|
||||
public:
|
||||
CoverPublishAction(Cover *cover) : cover_(cover) {}
|
||||
TEMPLATABLE_VALUE(float, position)
|
||||
TEMPLATABLE_VALUE(float, tilt)
|
||||
TEMPLATABLE_VALUE(CoverOperation, current_operation)
|
||||
|
||||
void play(Ts... x) override {
|
||||
if (this->position_.has_value())
|
||||
this->cover_->position = this->position_.value(x...);
|
||||
@@ -73,10 +77,6 @@ template<typename... Ts> class CoverPublishAction : public Action<Ts...> {
|
||||
this->cover_->publish_state();
|
||||
}
|
||||
|
||||
TEMPLATABLE_VALUE(float, position)
|
||||
TEMPLATABLE_VALUE(float, tilt)
|
||||
TEMPLATABLE_VALUE(CoverOperation, current_operation)
|
||||
|
||||
protected:
|
||||
Cover *cover_;
|
||||
};
|
||||
|
@@ -64,6 +64,8 @@ void CTClampSensor::loop() {
|
||||
|
||||
// Perform a single sample
|
||||
float value = this->source_->sample();
|
||||
if (isnan(value))
|
||||
return;
|
||||
|
||||
if (this->is_calibrating_offset_) {
|
||||
this->sample_sum_ += value;
|
||||
|
@@ -4,6 +4,7 @@ from esphome.components import sensor, voltage_sampler
|
||||
from esphome.const import CONF_SENSOR, CONF_ID, ICON_FLASH, UNIT_AMPERE
|
||||
|
||||
AUTO_LOAD = ['voltage_sampler']
|
||||
CODEOWNERS = ['@jesserockz']
|
||||
|
||||
CONF_SAMPLE_DURATION = 'sample_duration'
|
||||
|
||||
|
@@ -10,7 +10,7 @@ CONF_LIGHTS = 'lights'
|
||||
CONFIG_SCHEMA = cv.Schema({
|
||||
cv.GenerateID(): cv.declare_id(CustomLightOutputConstructor),
|
||||
cv.Required(CONF_LAMBDA): cv.returning_lambda,
|
||||
cv.Required(CONF_LIGHTS): cv.ensure_list(light.RGB_LIGHT_SCHEMA),
|
||||
cv.Required(CONF_LIGHTS): cv.ensure_list(light.ADDRESSABLE_LIGHT_SCHEMA),
|
||||
})
|
||||
|
||||
|
||||
|
@@ -13,6 +13,7 @@ class CWWWLightOutput : public light::LightOutput {
|
||||
void set_warm_white(output::FloatOutput *warm_white) { warm_white_ = warm_white; }
|
||||
void set_cold_white_temperature(float cold_white_temperature) { cold_white_temperature_ = cold_white_temperature; }
|
||||
void set_warm_white_temperature(float warm_white_temperature) { warm_white_temperature_ = warm_white_temperature; }
|
||||
void set_constant_brightness(bool constant_brightness) { constant_brightness_ = constant_brightness; }
|
||||
light::LightTraits get_traits() override {
|
||||
auto traits = light::LightTraits();
|
||||
traits.set_supports_brightness(true);
|
||||
@@ -25,7 +26,7 @@ class CWWWLightOutput : public light::LightOutput {
|
||||
}
|
||||
void write_state(light::LightState *state) override {
|
||||
float cwhite, wwhite;
|
||||
state->current_values_as_cwww(&cwhite, &wwhite);
|
||||
state->current_values_as_cwww(&cwhite, &wwhite, this->constant_brightness_);
|
||||
this->cold_white_->set_level(cwhite);
|
||||
this->warm_white_->set_level(wwhite);
|
||||
}
|
||||
@@ -35,6 +36,7 @@ class CWWWLightOutput : public light::LightOutput {
|
||||
output::FloatOutput *warm_white_;
|
||||
float cold_white_temperature_;
|
||||
float warm_white_temperature_;
|
||||
bool constant_brightness_;
|
||||
};
|
||||
|
||||
} // namespace cwww
|
||||
|
@@ -7,12 +7,15 @@ from esphome.const import CONF_OUTPUT_ID, CONF_COLD_WHITE, CONF_WARM_WHITE, \
|
||||
cwww_ns = cg.esphome_ns.namespace('cwww')
|
||||
CWWWLightOutput = cwww_ns.class_('CWWWLightOutput', light.LightOutput)
|
||||
|
||||
CONF_CONSTANT_BRIGHTNESS = 'constant_brightness'
|
||||
|
||||
CONFIG_SCHEMA = light.RGB_LIGHT_SCHEMA.extend({
|
||||
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(CWWWLightOutput),
|
||||
cv.Required(CONF_COLD_WHITE): cv.use_id(output.FloatOutput),
|
||||
cv.Required(CONF_WARM_WHITE): cv.use_id(output.FloatOutput),
|
||||
cv.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature,
|
||||
cv.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): cv.color_temperature,
|
||||
cv.Optional(CONF_CONSTANT_BRIGHTNESS, default=False): cv.boolean,
|
||||
})
|
||||
|
||||
|
||||
@@ -26,3 +29,4 @@ def to_code(config):
|
||||
wwhite = yield cg.get_variable(config[CONF_WARM_WHITE])
|
||||
cg.add(var.set_warm_white(wwhite))
|
||||
cg.add(var.set_warm_white_temperature(config[CONF_WARM_WHITE_COLOR_TEMPERATURE]))
|
||||
cg.add(var.set_constant_brightness(config[CONF_CONSTANT_BRIGHTNESS]))
|
||||
|
0
esphome/components/daikin/__init__.py
Normal file
0
esphome/components/daikin/__init__.py
Normal file
18
esphome/components/daikin/climate.py
Normal file
18
esphome/components/daikin/climate.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import climate_ir
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
AUTO_LOAD = ['climate_ir']
|
||||
|
||||
daikin_ns = cg.esphome_ns.namespace('daikin')
|
||||
DaikinClimate = daikin_ns.class_('DaikinClimate', climate_ir.ClimateIR)
|
||||
|
||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend({
|
||||
cv.GenerateID(): cv.declare_id(DaikinClimate),
|
||||
})
|
||||
|
||||
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield climate_ir.register_climate_ir(var, config)
|
227
esphome/components/daikin/daikin.cpp
Normal file
227
esphome/components/daikin/daikin.cpp
Normal file
@@ -0,0 +1,227 @@
|
||||
#include "daikin.h"
|
||||
#include "esphome/components/remote_base/remote_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace daikin {
|
||||
|
||||
static const char *TAG = "daikin.climate";
|
||||
|
||||
void DaikinClimate::transmit_state() {
|
||||
uint8_t remote_state[35] = {0x11, 0xDA, 0x27, 0x00, 0xC5, 0x00, 0x00, 0xD7, 0x11, 0xDA, 0x27, 0x00,
|
||||
0x42, 0x49, 0x05, 0xA2, 0x11, 0xDA, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00};
|
||||
|
||||
remote_state[21] = this->operation_mode_();
|
||||
remote_state[24] = this->fan_speed_();
|
||||
remote_state[22] = this->temperature_();
|
||||
|
||||
// Calculate checksum
|
||||
for (int i = 16; i < 34; i++) {
|
||||
remote_state[34] += remote_state[i];
|
||||
}
|
||||
|
||||
auto transmit = this->transmitter_->transmit();
|
||||
auto data = transmit.get_data();
|
||||
data->set_carrier_frequency(DAIKIN_IR_FREQUENCY);
|
||||
|
||||
data->mark(DAIKIN_HEADER_MARK);
|
||||
data->space(DAIKIN_HEADER_SPACE);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask
|
||||
data->mark(DAIKIN_BIT_MARK);
|
||||
bool bit = remote_state[i] & mask;
|
||||
data->space(bit ? DAIKIN_ONE_SPACE : DAIKIN_ZERO_SPACE);
|
||||
}
|
||||
}
|
||||
data->mark(DAIKIN_BIT_MARK);
|
||||
data->space(DAIKIN_MESSAGE_SPACE);
|
||||
data->mark(DAIKIN_HEADER_MARK);
|
||||
data->space(DAIKIN_HEADER_SPACE);
|
||||
|
||||
for (int i = 8; i < 16; i++) {
|
||||
for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask
|
||||
data->mark(DAIKIN_BIT_MARK);
|
||||
bool bit = remote_state[i] & mask;
|
||||
data->space(bit ? DAIKIN_ONE_SPACE : DAIKIN_ZERO_SPACE);
|
||||
}
|
||||
}
|
||||
data->mark(DAIKIN_BIT_MARK);
|
||||
data->space(DAIKIN_MESSAGE_SPACE);
|
||||
data->mark(DAIKIN_HEADER_MARK);
|
||||
data->space(DAIKIN_HEADER_SPACE);
|
||||
|
||||
for (int i = 16; i < 35; i++) {
|
||||
for (uint8_t mask = 1; mask > 0; mask <<= 1) { // iterate through bit mask
|
||||
data->mark(DAIKIN_BIT_MARK);
|
||||
bool bit = remote_state[i] & mask;
|
||||
data->space(bit ? DAIKIN_ONE_SPACE : DAIKIN_ZERO_SPACE);
|
||||
}
|
||||
}
|
||||
data->mark(DAIKIN_BIT_MARK);
|
||||
data->space(0);
|
||||
|
||||
transmit.perform();
|
||||
}
|
||||
|
||||
uint8_t DaikinClimate::operation_mode_() {
|
||||
uint8_t operating_mode = DAIKIN_MODE_ON;
|
||||
switch (this->mode) {
|
||||
case climate::CLIMATE_MODE_COOL:
|
||||
operating_mode |= DAIKIN_MODE_COOL;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_DRY:
|
||||
operating_mode |= DAIKIN_MODE_DRY;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_HEAT:
|
||||
operating_mode |= DAIKIN_MODE_HEAT;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_AUTO:
|
||||
operating_mode |= DAIKIN_MODE_AUTO;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_FAN_ONLY:
|
||||
operating_mode |= DAIKIN_MODE_FAN;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_OFF:
|
||||
default:
|
||||
operating_mode = DAIKIN_MODE_OFF;
|
||||
break;
|
||||
}
|
||||
|
||||
return operating_mode;
|
||||
}
|
||||
|
||||
uint8_t DaikinClimate::fan_speed_() {
|
||||
uint8_t fan_speed;
|
||||
switch (this->fan_mode) {
|
||||
case climate::CLIMATE_FAN_LOW:
|
||||
fan_speed = DAIKIN_FAN_1;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_MEDIUM:
|
||||
fan_speed = DAIKIN_FAN_3;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_HIGH:
|
||||
fan_speed = DAIKIN_FAN_5;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_AUTO:
|
||||
default:
|
||||
fan_speed = DAIKIN_FAN_AUTO;
|
||||
}
|
||||
|
||||
// If swing is enabled switch first 4 bits to 1111
|
||||
return this->swing_mode == climate::CLIMATE_SWING_VERTICAL ? fan_speed | 0xF : fan_speed;
|
||||
}
|
||||
|
||||
uint8_t DaikinClimate::temperature_() {
|
||||
// Force special temperatures depending on the mode
|
||||
switch (this->mode) {
|
||||
case climate::CLIMATE_MODE_FAN_ONLY:
|
||||
return 0x32;
|
||||
case climate::CLIMATE_MODE_AUTO:
|
||||
case climate::CLIMATE_MODE_DRY:
|
||||
return 0xc0;
|
||||
default:
|
||||
uint8_t temperature = (uint8_t) roundf(clamp(this->target_temperature, DAIKIN_TEMP_MIN, DAIKIN_TEMP_MAX));
|
||||
return temperature << 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool DaikinClimate::parse_state_frame_(const uint8_t frame[]) {
|
||||
uint8_t checksum = 0;
|
||||
for (int i = 0; i < (DAIKIN_STATE_FRAME_SIZE - 1); i++) {
|
||||
checksum += frame[i];
|
||||
}
|
||||
if (frame[DAIKIN_STATE_FRAME_SIZE - 1] != checksum)
|
||||
return false;
|
||||
uint8_t mode = frame[5];
|
||||
if (mode & DAIKIN_MODE_ON) {
|
||||
switch (mode & 0xF0) {
|
||||
case DAIKIN_MODE_COOL:
|
||||
this->mode = climate::CLIMATE_MODE_COOL;
|
||||
break;
|
||||
case DAIKIN_MODE_DRY:
|
||||
this->mode = climate::CLIMATE_MODE_DRY;
|
||||
break;
|
||||
case DAIKIN_MODE_HEAT:
|
||||
this->mode = climate::CLIMATE_MODE_HEAT;
|
||||
break;
|
||||
case DAIKIN_MODE_AUTO:
|
||||
this->mode = climate::CLIMATE_MODE_AUTO;
|
||||
break;
|
||||
case DAIKIN_MODE_FAN:
|
||||
this->mode = climate::CLIMATE_MODE_FAN_ONLY;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this->mode = climate::CLIMATE_MODE_OFF;
|
||||
}
|
||||
uint8_t temperature = frame[6];
|
||||
if (!(temperature & 0xC0)) {
|
||||
this->target_temperature = temperature >> 1;
|
||||
}
|
||||
uint8_t fan_mode = frame[8];
|
||||
if (fan_mode & 0xF)
|
||||
this->swing_mode = climate::CLIMATE_SWING_VERTICAL;
|
||||
else
|
||||
this->swing_mode = climate::CLIMATE_SWING_OFF;
|
||||
switch (fan_mode & 0xF0) {
|
||||
case DAIKIN_FAN_1:
|
||||
case DAIKIN_FAN_2:
|
||||
this->fan_mode = climate::CLIMATE_FAN_LOW;
|
||||
break;
|
||||
case DAIKIN_FAN_3:
|
||||
this->fan_mode = climate::CLIMATE_FAN_MEDIUM;
|
||||
break;
|
||||
case DAIKIN_FAN_4:
|
||||
case DAIKIN_FAN_5:
|
||||
this->fan_mode = climate::CLIMATE_FAN_HIGH;
|
||||
break;
|
||||
case DAIKIN_FAN_AUTO:
|
||||
this->fan_mode = climate::CLIMATE_FAN_AUTO;
|
||||
break;
|
||||
}
|
||||
this->publish_state();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DaikinClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
uint8_t state_frame[DAIKIN_STATE_FRAME_SIZE] = {};
|
||||
if (!data.expect_item(DAIKIN_HEADER_MARK, DAIKIN_HEADER_SPACE)) {
|
||||
return false;
|
||||
}
|
||||
for (uint8_t pos = 0; pos < DAIKIN_STATE_FRAME_SIZE; pos++) {
|
||||
uint8_t byte = 0;
|
||||
for (int8_t bit = 0; bit < 8; bit++) {
|
||||
if (data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ONE_SPACE))
|
||||
byte |= 1 << bit;
|
||||
else if (!data.expect_item(DAIKIN_BIT_MARK, DAIKIN_ZERO_SPACE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
state_frame[pos] = byte;
|
||||
if (pos == 0) {
|
||||
// frame header
|
||||
if (byte != 0x11)
|
||||
return false;
|
||||
} else if (pos == 1) {
|
||||
// frame header
|
||||
if (byte != 0xDA)
|
||||
return false;
|
||||
} else if (pos == 2) {
|
||||
// frame header
|
||||
if (byte != 0x27)
|
||||
return false;
|
||||
} else if (pos == 3) {
|
||||
// frame header
|
||||
if (byte != 0x00)
|
||||
return false;
|
||||
} else if (pos == 4) {
|
||||
// frame type
|
||||
if (byte != 0x00)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return this->parse_state_frame_(state_frame);
|
||||
}
|
||||
|
||||
} // namespace daikin
|
||||
} // namespace esphome
|
63
esphome/components/daikin/daikin.h
Normal file
63
esphome/components/daikin/daikin.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/climate_ir/climate_ir.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace daikin {
|
||||
|
||||
// Values for Daikin ARC43XXX IR Controllers
|
||||
// Temperature
|
||||
const uint8_t DAIKIN_TEMP_MIN = 10; // Celsius
|
||||
const uint8_t DAIKIN_TEMP_MAX = 30; // Celsius
|
||||
|
||||
// Modes
|
||||
const uint8_t DAIKIN_MODE_AUTO = 0x00;
|
||||
const uint8_t DAIKIN_MODE_COOL = 0x30;
|
||||
const uint8_t DAIKIN_MODE_HEAT = 0x40;
|
||||
const uint8_t DAIKIN_MODE_DRY = 0x20;
|
||||
const uint8_t DAIKIN_MODE_FAN = 0x60;
|
||||
const uint8_t DAIKIN_MODE_OFF = 0x00;
|
||||
const uint8_t DAIKIN_MODE_ON = 0x01;
|
||||
|
||||
// Fan Speed
|
||||
const uint8_t DAIKIN_FAN_AUTO = 0xA0;
|
||||
const uint8_t DAIKIN_FAN_1 = 0x30;
|
||||
const uint8_t DAIKIN_FAN_2 = 0x40;
|
||||
const uint8_t DAIKIN_FAN_3 = 0x50;
|
||||
const uint8_t DAIKIN_FAN_4 = 0x60;
|
||||
const uint8_t DAIKIN_FAN_5 = 0x70;
|
||||
|
||||
// IR Transmission
|
||||
const uint32_t DAIKIN_IR_FREQUENCY = 38000;
|
||||
const uint32_t DAIKIN_HEADER_MARK = 3360;
|
||||
const uint32_t DAIKIN_HEADER_SPACE = 1760;
|
||||
const uint32_t DAIKIN_BIT_MARK = 520;
|
||||
const uint32_t DAIKIN_ONE_SPACE = 1370;
|
||||
const uint32_t DAIKIN_ZERO_SPACE = 360;
|
||||
const uint32_t DAIKIN_MESSAGE_SPACE = 32300;
|
||||
|
||||
// State Frame size
|
||||
const uint8_t DAIKIN_STATE_FRAME_SIZE = 19;
|
||||
|
||||
class DaikinClimate : public climate_ir::ClimateIR {
|
||||
public:
|
||||
DaikinClimate()
|
||||
: climate_ir::ClimateIR(
|
||||
DAIKIN_TEMP_MIN, DAIKIN_TEMP_MAX, 1.0f, true, true,
|
||||
std::vector<climate::ClimateFanMode>{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW,
|
||||
climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH},
|
||||
std::vector<climate::ClimateSwingMode>{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL}) {}
|
||||
|
||||
protected:
|
||||
// Transmit via IR the state of this climate controller.
|
||||
void transmit_state() override;
|
||||
uint8_t operation_mode_();
|
||||
uint8_t fan_speed_();
|
||||
uint8_t temperature_();
|
||||
// Handle received IR Buffer
|
||||
bool on_receive(remote_base::RemoteReceiveData data) override;
|
||||
bool parse_state_frame_(const uint8_t frame[]);
|
||||
};
|
||||
|
||||
} // namespace daikin
|
||||
} // namespace esphome
|
@@ -32,9 +32,11 @@ void DallasComponent::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up DallasComponent...");
|
||||
|
||||
yield();
|
||||
disable_interrupts();
|
||||
std::vector<uint64_t> raw_sensors = this->one_wire_->search_vec();
|
||||
enable_interrupts();
|
||||
std::vector<uint64_t> raw_sensors;
|
||||
{
|
||||
InterruptLock lock;
|
||||
raw_sensors = this->one_wire_->search_vec();
|
||||
}
|
||||
|
||||
for (auto &address : raw_sensors) {
|
||||
std::string s = uint64_to_string(address);
|
||||
@@ -108,16 +110,17 @@ DallasTemperatureSensor *DallasComponent::get_sensor_by_index(uint8_t index, uin
|
||||
void DallasComponent::update() {
|
||||
this->status_clear_warning();
|
||||
|
||||
disable_interrupts();
|
||||
bool result;
|
||||
if (!this->one_wire_->reset()) {
|
||||
result = false;
|
||||
} else {
|
||||
result = true;
|
||||
this->one_wire_->skip();
|
||||
this->one_wire_->write8(DALLAS_COMMAND_START_CONVERSION);
|
||||
{
|
||||
InterruptLock lock;
|
||||
if (!this->one_wire_->reset()) {
|
||||
result = false;
|
||||
} else {
|
||||
result = true;
|
||||
this->one_wire_->skip();
|
||||
this->one_wire_->write8(DALLAS_COMMAND_START_CONVERSION);
|
||||
}
|
||||
}
|
||||
enable_interrupts();
|
||||
|
||||
if (!result) {
|
||||
ESP_LOGE(TAG, "Requesting conversion failed");
|
||||
@@ -127,9 +130,11 @@ void DallasComponent::update() {
|
||||
|
||||
for (auto *sensor : this->sensors_) {
|
||||
this->set_timeout(sensor->get_address_name(), sensor->millis_to_wait_for_conversion(), [this, sensor] {
|
||||
disable_interrupts();
|
||||
bool res = sensor->read_scratch_pad();
|
||||
enable_interrupts();
|
||||
bool res;
|
||||
{
|
||||
InterruptLock lock;
|
||||
res = sensor->read_scratch_pad();
|
||||
}
|
||||
|
||||
if (!res) {
|
||||
ESP_LOGW(TAG, "'%s' - Reseting bus for read failed!", sensor->get_name().c_str());
|
||||
@@ -170,7 +175,7 @@ const std::string &DallasTemperatureSensor::get_address_name() {
|
||||
|
||||
return this->address_name_;
|
||||
}
|
||||
bool DallasTemperatureSensor::read_scratch_pad() {
|
||||
bool ICACHE_RAM_ATTR DallasTemperatureSensor::read_scratch_pad() {
|
||||
ESPOneWire *wire = this->parent_->one_wire_;
|
||||
if (!wire->reset()) {
|
||||
return false;
|
||||
@@ -185,9 +190,11 @@ bool DallasTemperatureSensor::read_scratch_pad() {
|
||||
return true;
|
||||
}
|
||||
bool DallasTemperatureSensor::setup_sensor() {
|
||||
disable_interrupts();
|
||||
bool r = this->read_scratch_pad();
|
||||
enable_interrupts();
|
||||
bool r;
|
||||
{
|
||||
InterruptLock lock;
|
||||
r = this->read_scratch_pad();
|
||||
}
|
||||
|
||||
if (!r) {
|
||||
ESP_LOGE(TAG, "Reading scratchpad failed: reset");
|
||||
@@ -222,20 +229,21 @@ bool DallasTemperatureSensor::setup_sensor() {
|
||||
}
|
||||
|
||||
ESPOneWire *wire = this->parent_->one_wire_;
|
||||
disable_interrupts();
|
||||
if (wire->reset()) {
|
||||
wire->select(this->address_);
|
||||
wire->write8(DALLAS_COMMAND_WRITE_SCRATCH_PAD);
|
||||
wire->write8(this->scratch_pad_[2]); // high alarm temp
|
||||
wire->write8(this->scratch_pad_[3]); // low alarm temp
|
||||
wire->write8(this->scratch_pad_[4]); // resolution
|
||||
wire->reset();
|
||||
{
|
||||
InterruptLock lock;
|
||||
if (wire->reset()) {
|
||||
wire->select(this->address_);
|
||||
wire->write8(DALLAS_COMMAND_WRITE_SCRATCH_PAD);
|
||||
wire->write8(this->scratch_pad_[2]); // high alarm temp
|
||||
wire->write8(this->scratch_pad_[3]); // low alarm temp
|
||||
wire->write8(this->scratch_pad_[4]); // resolution
|
||||
wire->reset();
|
||||
|
||||
// write value to EEPROM
|
||||
wire->select(this->address_);
|
||||
wire->write8(0x48);
|
||||
// write value to EEPROM
|
||||
wire->select(this->address_);
|
||||
wire->write8(0x48);
|
||||
}
|
||||
}
|
||||
enable_interrupts();
|
||||
|
||||
delay(20); // allow it to finish operation
|
||||
wire->reset();
|
||||
|
@@ -12,7 +12,7 @@ const int ONE_WIRE_ROM_SEARCH = 0xF0;
|
||||
|
||||
ESPOneWire::ESPOneWire(GPIOPin *pin) : pin_(pin) {}
|
||||
|
||||
bool HOT ESPOneWire::reset() {
|
||||
bool HOT ICACHE_RAM_ATTR ESPOneWire::reset() {
|
||||
uint8_t retries = 125;
|
||||
|
||||
// Wait for communication to clear
|
||||
@@ -30,7 +30,7 @@ bool HOT ESPOneWire::reset() {
|
||||
|
||||
// Switch into RX mode, letting the pin float
|
||||
this->pin_->pin_mode(INPUT_PULLUP);
|
||||
// after 15µs-60µs wait time, slave pulls low for 60µs-240µs
|
||||
// after 15µs-60µs wait time, responder pulls low for 60µs-240µs
|
||||
// let's have 70µs just in case
|
||||
delayMicroseconds(70);
|
||||
|
||||
@@ -39,7 +39,7 @@ bool HOT ESPOneWire::reset() {
|
||||
return r;
|
||||
}
|
||||
|
||||
void HOT ESPOneWire::write_bit(bool bit) {
|
||||
void HOT ICACHE_RAM_ATTR ESPOneWire::write_bit(bool bit) {
|
||||
// Initiate write/read by pulling low.
|
||||
this->pin_->pin_mode(OUTPUT);
|
||||
this->pin_->digital_write(false);
|
||||
@@ -60,7 +60,7 @@ void HOT ESPOneWire::write_bit(bool bit) {
|
||||
}
|
||||
}
|
||||
|
||||
bool HOT ESPOneWire::read_bit() {
|
||||
bool HOT ICACHE_RAM_ATTR ESPOneWire::read_bit() {
|
||||
// Initiate read slot by pulling LOW for at least 1µs
|
||||
this->pin_->pin_mode(OUTPUT);
|
||||
this->pin_->digital_write(false);
|
||||
@@ -76,43 +76,43 @@ bool HOT ESPOneWire::read_bit() {
|
||||
return r;
|
||||
}
|
||||
|
||||
void ESPOneWire::write8(uint8_t val) {
|
||||
void ICACHE_RAM_ATTR ESPOneWire::write8(uint8_t val) {
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
this->write_bit(bool((1u << i) & val));
|
||||
}
|
||||
}
|
||||
|
||||
void ESPOneWire::write64(uint64_t val) {
|
||||
void ICACHE_RAM_ATTR ESPOneWire::write64(uint64_t val) {
|
||||
for (uint8_t i = 0; i < 64; i++) {
|
||||
this->write_bit(bool((1ULL << i) & val));
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ESPOneWire::read8() {
|
||||
uint8_t ICACHE_RAM_ATTR ESPOneWire::read8() {
|
||||
uint8_t ret = 0;
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
ret |= (uint8_t(this->read_bit()) << i);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
uint64_t ESPOneWire::read64() {
|
||||
uint64_t ICACHE_RAM_ATTR ESPOneWire::read64() {
|
||||
uint64_t ret = 0;
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
ret |= (uint64_t(this->read_bit()) << i);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
void ESPOneWire::select(uint64_t address) {
|
||||
void ICACHE_RAM_ATTR ESPOneWire::select(uint64_t address) {
|
||||
this->write8(ONE_WIRE_ROM_SELECT);
|
||||
this->write64(address);
|
||||
}
|
||||
void ESPOneWire::reset_search() {
|
||||
void ICACHE_RAM_ATTR ESPOneWire::reset_search() {
|
||||
this->last_discrepancy_ = 0;
|
||||
this->last_device_flag_ = false;
|
||||
this->last_family_discrepancy_ = 0;
|
||||
this->rom_number_ = 0;
|
||||
}
|
||||
uint64_t HOT ESPOneWire::search() {
|
||||
uint64_t HOT ICACHE_RAM_ATTR ESPOneWire::search() {
|
||||
if (this->last_device_flag_) {
|
||||
return 0u;
|
||||
}
|
||||
@@ -196,7 +196,7 @@ uint64_t HOT ESPOneWire::search() {
|
||||
|
||||
return this->rom_number_;
|
||||
}
|
||||
std::vector<uint64_t> ESPOneWire::search_vec() {
|
||||
std::vector<uint64_t> ICACHE_RAM_ATTR ESPOneWire::search_vec() {
|
||||
std::vector<uint64_t> res;
|
||||
|
||||
this->reset_search();
|
||||
@@ -206,12 +206,12 @@ std::vector<uint64_t> ESPOneWire::search_vec() {
|
||||
|
||||
return res;
|
||||
}
|
||||
void ESPOneWire::skip() {
|
||||
void ICACHE_RAM_ATTR ESPOneWire::skip() {
|
||||
this->write8(0xCC); // skip ROM
|
||||
}
|
||||
GPIOPin *ESPOneWire::get_pin() { return this->pin_; }
|
||||
|
||||
uint8_t *ESPOneWire::rom_number8_() { return reinterpret_cast<uint8_t *>(&this->rom_number_); }
|
||||
uint8_t ICACHE_RAM_ATTR *ESPOneWire::rom_number8_() { return reinterpret_cast<uint8_t *>(&this->rom_number_); }
|
||||
|
||||
} // namespace dallas
|
||||
} // namespace esphome
|
||||
|
@@ -2,6 +2,7 @@ import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
CODEOWNERS = ['@OttoWinter']
|
||||
DEPENDENCIES = ['logger']
|
||||
|
||||
debug_ns = cg.esphome_ns.namespace('debug')
|
||||
|
@@ -6,10 +6,10 @@ from esphome.const import CONF_ID, CONF_MODE, CONF_NUMBER, CONF_PINS, CONF_RUN_C
|
||||
|
||||
|
||||
def validate_pin_number(value):
|
||||
valid_pins = [0, 2, 4, 12, 13, 14, 15, 25, 26, 27, 32, 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:
|
||||
raise cv.Invalid(u"Only pins {} support wakeup"
|
||||
u"".format(', '.join(str(x) for x in valid_pins)))
|
||||
raise cv.Invalid("Only pins {} support wakeup"
|
||||
"".format(', '.join(str(x) for x in valid_pins)))
|
||||
return value
|
||||
|
||||
|
||||
|
@@ -5,6 +5,7 @@ from esphome.const import CONF_ID, CONF_TRIGGER_ID, CONF_FILE, CONF_DEVICE
|
||||
from esphome.components import uart
|
||||
|
||||
DEPENDENCIES = ['uart']
|
||||
CODEOWNERS = ['@glmnet']
|
||||
|
||||
dfplayer_ns = cg.esphome_ns.namespace('dfplayer')
|
||||
DFPlayer = dfplayer_ns.class_('DFPlayer', cg.Component)
|
||||
|
@@ -8,8 +8,10 @@ static const char* TAG = "dfplayer";
|
||||
|
||||
void DFPlayer::play_folder(uint16_t folder, uint16_t file) {
|
||||
if (folder < 100 && file < 256) {
|
||||
this->ack_set_is_playing_ = true;
|
||||
this->send_cmd_(0x0F, (uint8_t) folder, (uint8_t) file);
|
||||
} else if (folder <= 10 && file <= 1000) {
|
||||
this->ack_set_is_playing_ = true;
|
||||
this->send_cmd_(0x14, (((uint16_t) folder) << 12) | file);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Cannot play folder %d file %d.", folder, file);
|
||||
@@ -93,6 +95,10 @@ void DFPlayer::loop() {
|
||||
ESP_LOGI(TAG, "USB, TF Card available");
|
||||
}
|
||||
break;
|
||||
case 0x40:
|
||||
ESP_LOGV(TAG, "Nack");
|
||||
this->ack_set_is_playing_ = false;
|
||||
this->ack_reset_is_playing_ = false;
|
||||
case 0x41:
|
||||
ESP_LOGV(TAG, "Ack ok");
|
||||
this->is_playing_ |= this->ack_set_is_playing_;
|
||||
|
@@ -27,29 +27,56 @@ class DFPlayer : public uart::UARTDevice, public Component {
|
||||
public:
|
||||
void loop() override;
|
||||
|
||||
void next() { this->send_cmd_(0x01); }
|
||||
void previous() { this->send_cmd_(0x02); }
|
||||
void next() {
|
||||
this->ack_set_is_playing_ = true;
|
||||
this->send_cmd_(0x01);
|
||||
}
|
||||
void previous() {
|
||||
this->ack_set_is_playing_ = true;
|
||||
this->send_cmd_(0x02);
|
||||
}
|
||||
void play_file(uint16_t file) {
|
||||
this->ack_set_is_playing_ = true;
|
||||
this->send_cmd_(0x03, file);
|
||||
}
|
||||
void play_file_loop(uint16_t file) { this->send_cmd_(0x08, file); }
|
||||
void play_file_loop(uint16_t file) {
|
||||
this->ack_set_is_playing_ = true;
|
||||
this->send_cmd_(0x08, file);
|
||||
}
|
||||
void play_folder(uint16_t folder, uint16_t file);
|
||||
void play_folder_loop(uint16_t folder) { this->send_cmd_(0x17, folder); }
|
||||
void play_folder_loop(uint16_t folder) {
|
||||
this->ack_set_is_playing_ = true;
|
||||
this->send_cmd_(0x17, folder);
|
||||
}
|
||||
void volume_up() { this->send_cmd_(0x04); }
|
||||
void volume_down() { this->send_cmd_(0x05); }
|
||||
void set_device(Device device) { this->send_cmd_(0x09, device); }
|
||||
void set_volume(uint8_t volume) { this->send_cmd_(0x06, volume); }
|
||||
void set_eq(EqPreset preset) { this->send_cmd_(0x07, preset); }
|
||||
void sleep() { this->send_cmd_(0x0A); }
|
||||
void reset() { this->send_cmd_(0x0C); }
|
||||
void start() { this->send_cmd_(0x0D); }
|
||||
void sleep() {
|
||||
this->ack_reset_is_playing_ = true;
|
||||
this->send_cmd_(0x0A);
|
||||
}
|
||||
void reset() {
|
||||
this->ack_reset_is_playing_ = true;
|
||||
this->send_cmd_(0x0C);
|
||||
}
|
||||
void start() {
|
||||
this->ack_set_is_playing_ = true;
|
||||
this->send_cmd_(0x0D);
|
||||
}
|
||||
void pause() {
|
||||
this->ack_reset_is_playing_ = true;
|
||||
this->send_cmd_(0x0E);
|
||||
}
|
||||
void stop() { this->send_cmd_(0x16); }
|
||||
void random() { this->send_cmd_(0x18); }
|
||||
void stop() {
|
||||
this->ack_reset_is_playing_ = true;
|
||||
this->send_cmd_(0x16);
|
||||
}
|
||||
void random() {
|
||||
this->ack_set_is_playing_ = true;
|
||||
this->send_cmd_(0x18);
|
||||
}
|
||||
|
||||
bool is_playing() { return is_playing_; }
|
||||
void dump_config() override;
|
||||
@@ -77,7 +104,6 @@ class DFPlayer : public uart::UARTDevice, public Component {
|
||||
|
||||
#define DFPLAYER_SIMPLE_ACTION(ACTION_CLASS, ACTION_METHOD) \
|
||||
template<typename... Ts> class ACTION_CLASS : public Action<Ts...>, public Parented<DFPlayer> { \
|
||||
public: \
|
||||
void play(Ts... x) override { this->parent_->ACTION_METHOD(); } \
|
||||
};
|
||||
|
||||
@@ -88,6 +114,7 @@ template<typename... Ts> class PlayFileAction : public Action<Ts...>, public Par
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint16_t, file)
|
||||
TEMPLATABLE_VALUE(boolean, loop)
|
||||
|
||||
void play(Ts... x) override {
|
||||
auto file = this->file_.value(x...);
|
||||
auto loop = this->loop_.value(x...);
|
||||
@@ -104,6 +131,7 @@ template<typename... Ts> class PlayFolderAction : public Action<Ts...>, public P
|
||||
TEMPLATABLE_VALUE(uint16_t, folder)
|
||||
TEMPLATABLE_VALUE(uint16_t, file)
|
||||
TEMPLATABLE_VALUE(boolean, loop)
|
||||
|
||||
void play(Ts... x) override {
|
||||
auto folder = this->folder_.value(x...);
|
||||
auto file = this->file_.value(x...);
|
||||
@@ -119,6 +147,7 @@ template<typename... Ts> class PlayFolderAction : public Action<Ts...>, public P
|
||||
template<typename... Ts> class SetDeviceAction : public Action<Ts...>, public Parented<DFPlayer> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(Device, device)
|
||||
|
||||
void play(Ts... x) override {
|
||||
auto device = this->device_.value(x...);
|
||||
this->parent_->set_device(device);
|
||||
@@ -128,6 +157,7 @@ template<typename... Ts> class SetDeviceAction : public Action<Ts...>, public Pa
|
||||
template<typename... Ts> class SetVolumeAction : public Action<Ts...>, public Parented<DFPlayer> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint8_t, volume)
|
||||
|
||||
void play(Ts... x) override {
|
||||
auto volume = this->volume_.value(x...);
|
||||
this->parent_->set_volume(volume);
|
||||
@@ -137,6 +167,7 @@ template<typename... Ts> class SetVolumeAction : public Action<Ts...>, public Pa
|
||||
template<typename... Ts> class SetEqAction : public Action<Ts...>, public Parented<DFPlayer> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(EqPreset, eq)
|
||||
|
||||
void play(Ts... x) override {
|
||||
auto eq = this->eq_.value(x...);
|
||||
this->parent_->set_eq(eq);
|
||||
|
@@ -0,0 +1 @@
|
||||
CODEOWNERS = ['@OttoWinter']
|
||||
|
@@ -47,8 +47,10 @@ void DHT::update() {
|
||||
if (error) {
|
||||
ESP_LOGD(TAG, "Got Temperature=%.1f°C Humidity=%.1f%%", temperature, humidity);
|
||||
|
||||
this->temperature_sensor_->publish_state(temperature);
|
||||
this->humidity_sensor_->publish_state(humidity);
|
||||
if (this->temperature_sensor_ != nullptr)
|
||||
this->temperature_sensor_->publish_state(temperature);
|
||||
if (this->humidity_sensor_ != nullptr)
|
||||
this->humidity_sensor_->publish_state(humidity);
|
||||
this->status_clear_warning();
|
||||
} else {
|
||||
const char *str = "";
|
||||
@@ -56,8 +58,10 @@ void DHT::update() {
|
||||
str = " and consider manually specifying the DHT model using the model option";
|
||||
}
|
||||
ESP_LOGW(TAG, "Invalid readings! Please check your wiring (pull-up resistor, pin number)%s.", str);
|
||||
this->temperature_sensor_->publish_state(NAN);
|
||||
this->humidity_sensor_->publish_state(NAN);
|
||||
if (this->temperature_sensor_ != nullptr)
|
||||
this->temperature_sensor_->publish_state(NAN);
|
||||
if (this->humidity_sensor_ != nullptr)
|
||||
this->humidity_sensor_->publish_state(NAN);
|
||||
this->status_set_warning();
|
||||
}
|
||||
}
|
||||
@@ -67,80 +71,103 @@ void DHT::set_dht_model(DHTModel model) {
|
||||
this->model_ = model;
|
||||
this->is_auto_detect_ = model == DHT_MODEL_AUTO_DETECT;
|
||||
}
|
||||
bool HOT DHT::read_sensor_(float *temperature, float *humidity, bool report_errors) {
|
||||
bool HOT ICACHE_RAM_ATTR DHT::read_sensor_(float *temperature, float *humidity, bool report_errors) {
|
||||
*humidity = NAN;
|
||||
*temperature = NAN;
|
||||
|
||||
disable_interrupts();
|
||||
this->pin_->digital_write(false);
|
||||
this->pin_->pin_mode(OUTPUT);
|
||||
this->pin_->digital_write(false);
|
||||
|
||||
if (this->model_ == DHT_MODEL_DHT11) {
|
||||
delayMicroseconds(18000);
|
||||
} else if (this->model_ == DHT_MODEL_SI7021) {
|
||||
delayMicroseconds(500);
|
||||
this->pin_->digital_write(true);
|
||||
delayMicroseconds(40);
|
||||
} else {
|
||||
delayMicroseconds(800);
|
||||
}
|
||||
this->pin_->pin_mode(INPUT_PULLUP);
|
||||
delayMicroseconds(40);
|
||||
|
||||
int error_code = 0;
|
||||
int8_t i = 0;
|
||||
uint8_t data[5] = {0, 0, 0, 0, 0};
|
||||
uint8_t bit = 7;
|
||||
uint8_t byte = 0;
|
||||
|
||||
for (int8_t i = -1; i < 40; i++) {
|
||||
uint32_t start_time = micros();
|
||||
{
|
||||
InterruptLock lock;
|
||||
|
||||
// Wait for rising edge
|
||||
while (!this->pin_->digital_read()) {
|
||||
if (micros() - start_time > 90) {
|
||||
enable_interrupts();
|
||||
if (report_errors) {
|
||||
if (i < 0) {
|
||||
ESP_LOGW(TAG, "Waiting for DHT communication to clear failed!");
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Rising edge for bit %d failed!", i);
|
||||
}
|
||||
this->pin_->digital_write(false);
|
||||
this->pin_->pin_mode(OUTPUT);
|
||||
this->pin_->digital_write(false);
|
||||
|
||||
if (this->model_ == DHT_MODEL_DHT11) {
|
||||
delayMicroseconds(18000);
|
||||
} else if (this->model_ == DHT_MODEL_SI7021) {
|
||||
delayMicroseconds(500);
|
||||
this->pin_->digital_write(true);
|
||||
delayMicroseconds(40);
|
||||
} else if (this->model_ == DHT_MODEL_DHT22_TYPE2) {
|
||||
delayMicroseconds(2000);
|
||||
} else {
|
||||
delayMicroseconds(800);
|
||||
}
|
||||
this->pin_->pin_mode(INPUT_PULLUP);
|
||||
delayMicroseconds(40);
|
||||
|
||||
uint8_t bit = 7;
|
||||
uint8_t byte = 0;
|
||||
|
||||
for (i = -1; i < 40; i++) {
|
||||
uint32_t start_time = micros();
|
||||
|
||||
// Wait for rising edge
|
||||
while (!this->pin_->digital_read()) {
|
||||
if (micros() - start_time > 90) {
|
||||
if (i < 0)
|
||||
error_code = 1;
|
||||
else
|
||||
error_code = 2;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (error_code != 0)
|
||||
break;
|
||||
|
||||
start_time = micros();
|
||||
uint32_t end_time = start_time;
|
||||
start_time = micros();
|
||||
uint32_t end_time = start_time;
|
||||
|
||||
// Wait for falling edge
|
||||
while (this->pin_->digital_read()) {
|
||||
if ((end_time = micros()) - start_time > 90) {
|
||||
enable_interrupts();
|
||||
if (report_errors) {
|
||||
if (i < 0) {
|
||||
ESP_LOGW(TAG, "Requesting data from DHT failed!");
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Falling edge for bit %d failed!", i);
|
||||
}
|
||||
// Wait for falling edge
|
||||
while (this->pin_->digital_read()) {
|
||||
if ((end_time = micros()) - start_time > 90) {
|
||||
if (i < 0)
|
||||
error_code = 3;
|
||||
else
|
||||
error_code = 4;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (error_code != 0)
|
||||
break;
|
||||
|
||||
if (i < 0)
|
||||
continue;
|
||||
if (i < 0)
|
||||
continue;
|
||||
|
||||
if (end_time - start_time >= 40) {
|
||||
data[byte] |= 1 << bit;
|
||||
if (end_time - start_time >= 40) {
|
||||
data[byte] |= 1 << bit;
|
||||
}
|
||||
if (bit == 0) {
|
||||
bit = 7;
|
||||
byte++;
|
||||
} else
|
||||
bit--;
|
||||
}
|
||||
if (bit == 0) {
|
||||
bit = 7;
|
||||
byte++;
|
||||
} else
|
||||
bit--;
|
||||
}
|
||||
enable_interrupts();
|
||||
if (!report_errors && error_code != 0)
|
||||
return false;
|
||||
|
||||
switch (error_code) {
|
||||
case 1:
|
||||
ESP_LOGW(TAG, "Waiting for DHT communication to clear failed!");
|
||||
return false;
|
||||
case 2:
|
||||
ESP_LOGW(TAG, "Rising edge for bit %d failed!", i);
|
||||
return false;
|
||||
case 3:
|
||||
ESP_LOGW(TAG, "Requesting data from DHT failed!");
|
||||
return false;
|
||||
case 4:
|
||||
ESP_LOGW(TAG, "Falling edge for bit %d failed!", i);
|
||||
return false;
|
||||
case 0:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ESP_LOGVV(TAG,
|
||||
"Data: Hum=0b" BYTE_TO_BINARY_PATTERN BYTE_TO_BINARY_PATTERN
|
||||
@@ -161,15 +188,29 @@ bool HOT DHT::read_sensor_(float *temperature, float *humidity, bool report_erro
|
||||
}
|
||||
|
||||
if (this->model_ == DHT_MODEL_DHT11) {
|
||||
*humidity = data[0];
|
||||
if (*humidity > 100)
|
||||
*humidity = NAN;
|
||||
*temperature = data[2];
|
||||
if (checksum_a == data[4]) {
|
||||
// Data format: 8bit integral RH data + 8bit decimal RH data + 8bit integral T data + 8bit decimal T data + 8bit
|
||||
// check sum - some models always have 0 in the decimal part
|
||||
const uint16_t raw_temperature = uint16_t(data[2]) * 10 + (data[3] & 0x7F);
|
||||
*temperature = raw_temperature / 10.0f;
|
||||
if ((data[3] & 0x80) != 0) {
|
||||
// negative
|
||||
*temperature *= -1;
|
||||
}
|
||||
|
||||
const uint16_t raw_humidity = uint16_t(data[0]) * 10 + data[1];
|
||||
*humidity = raw_humidity / 10.0f;
|
||||
} else {
|
||||
// For compatibily with DHT11 models which might only use 2 bytes checksums, only use the data from these two
|
||||
// bytes
|
||||
*temperature = data[2];
|
||||
*humidity = data[0];
|
||||
}
|
||||
} else {
|
||||
uint16_t raw_humidity = (uint16_t(data[0] & 0xFF) << 8) | (data[1] & 0xFF);
|
||||
uint16_t raw_temperature = (uint16_t(data[2] & 0xFF) << 8) | (data[3] & 0xFF);
|
||||
|
||||
if ((raw_temperature & 0x8000) != 0)
|
||||
if (this->model_ != DHT_MODEL_DHT22_TYPE2 && (raw_temperature & 0x8000) != 0)
|
||||
raw_temperature = ~(raw_temperature & 0x7FFF);
|
||||
|
||||
if (raw_temperature == 1 && raw_humidity == 10) {
|
||||
|
@@ -12,7 +12,8 @@ enum DHTModel {
|
||||
DHT_MODEL_DHT22,
|
||||
DHT_MODEL_AM2302,
|
||||
DHT_MODEL_RHT03,
|
||||
DHT_MODEL_SI7021
|
||||
DHT_MODEL_SI7021,
|
||||
DHT_MODEL_DHT22_TYPE2
|
||||
};
|
||||
|
||||
/// Component for reading temperature/humidity measurements from DHT11/DHT22 sensors.
|
||||
@@ -28,6 +29,7 @@ class DHT : public PollingComponent {
|
||||
* - DHT_MODEL_AM2302
|
||||
* - DHT_MODEL_RHT03
|
||||
* - DHT_MODEL_SI7021
|
||||
* - DHT_MODEL_DHT22_TYPE2
|
||||
*
|
||||
* @param model The DHT model.
|
||||
*/
|
||||
|
@@ -15,6 +15,7 @@ DHT_MODELS = {
|
||||
'AM2302': DHTModel.DHT_MODEL_AM2302,
|
||||
'RHT03': DHTModel.DHT_MODEL_RHT03,
|
||||
'SI7021': DHTModel.DHT_MODEL_SI7021,
|
||||
'DHT22_TYPE2': DHTModel.DHT_MODEL_DHT22_TYPE2,
|
||||
}
|
||||
DHT = dht_ns.class_('DHT', cg.PollingComponent)
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user