diff --git a/.clang-tidy b/.clang-tidy index 5e486e6a0c..1202d12c27 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -4,14 +4,35 @@ Checks: >- -abseil-*, -android-*, -boost-*, + -bugprone-branch-clone, -bugprone-macro-parentheses, + -bugprone-narrowing-conversions, + -bugprone-reserved-identifier, + -bugprone-signed-char-misuse, + -bugprone-suspicious-include, + -bugprone-too-small-loop-variable, + -bugprone-unhandled-self-assignment, + -cert-dcl37-c, -cert-dcl50-cpp, + -cert-dcl51-cpp, -cert-err58-cpp, + -cert-oop54-cpp, + -cert-oop57-cpp, + -cert-str34-c, -clang-analyzer-core.CallAndMessage, + -clang-analyzer-optin.*, -clang-analyzer-osx.*, -clang-analyzer-security.*, + -clang-diagnostic-shadow-field, + -cppcoreguidelines-avoid-c-arrays, -cppcoreguidelines-avoid-goto, + -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-avoid-non-const-global-variables, -cppcoreguidelines-c-copy-assignment-signature, + -cppcoreguidelines-init-variables, + -cppcoreguidelines-macro-usage, + -cppcoreguidelines-narrowing-conversions, + -cppcoreguidelines-non-private-member-variables-in-classes, -cppcoreguidelines-owning-memory, -cppcoreguidelines-pro-bounds-array-to-pointer-decay, -cppcoreguidelines-pro-bounds-constant-array-index, @@ -37,10 +58,16 @@ Checks: >- -google-runtime-int, -google-runtime-references, -hicpp-*, + -llvm-else-after-return, -llvm-header-guard, -llvm-include-order, + -llvm-qualified-auto, + -llvmlibc-*, + -misc-non-private-member-variables-in-classes, + -misc-no-recursion, -misc-unconventional-assign-operator, -misc-unused-parameters, + -modernize-avoid-c-arrays, -modernize-deprecated-headers, -modernize-pass-by-value, -modernize-pass-by-value, @@ -48,14 +75,25 @@ Checks: >- -modernize-use-auto, -modernize-use-default-member-init, -modernize-use-equals-default, + -modernize-use-trailing-return-type, -mpi-*, -objc-*, -performance-unnecessary-value-param, -readability-braces-around-statements, + -readability-const-return-type, + -readability-convert-member-functions-to-static, -readability-else-after-return, -readability-implicit-bool-conversion, + -readability-isolate-declaration, + -readability-magic-numbers, + -readability-make-member-function-const, -readability-named-parameter, + -readability-qualified-auto, + -readability-redundant-access-specifiers, -readability-redundant-member-init, + -readability-redundant-string-init, + -readability-uppercase-literal-suffix, + -readability-use-anyofallof, -warnings-as-errors, -zircon-* WarningsAsErrors: '*' diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 0d77eee7aa..aa90ef365f 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,25 +1,22 @@ # What does this implement/fix? -Quick description +Quick description and explanation of changes ## Types of changes - [ ] Bugfix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) -- [ ] Configuration change (this will require users to update their yaml configuration files to keep working) +- [ ] Other **Related issue or feature (if applicable):** fixes **Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs# - -# Test Environment + +## Test Environment - [ ] ESP32 - [ ] ESP8266 -- [ ] Windows -- [ ] Mac OS -- [ ] Linux ## Example entry for `config.yaml`: - - - - - - - - - - - - - - - - - - - - - - - - -
- - add - -
- - - - - - - - - - - - {% if begin and len(entries) == 1 %} - - {% end %} - - - - diff --git a/esphome/dashboard/templates/login.html b/esphome/dashboard/templates/login.html deleted file mode 100644 index 14116484af..0000000000 --- a/esphome/dashboard/templates/login.html +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - Login - ESPHome - - - - - - - - - - - - -
-
-
-
-
-
-
- - v{{ version }} - Dashboard Login -

- {% if hassio %} - Login by entering your Home Assistant login credentials. - {% else %} - Login by entering your ESPHome login credentials. - {% end %} -

- - {% if error is not None %} -
- Error! - {{ escape(error) }} -
- - - {% end %} - -
- {% if has_username or hassio %} -
-
- person - - -
-
- {% end %} - -
-
- lock - - -
-
-
-
- -
- -
-
-
-
-
- - - -
-
- - - diff --git a/esphome/espota2.py b/esphome/espota2.py index 785cb155df..351f6feda9 100644 --- a/esphome/espota2.py +++ b/esphome/espota2.py @@ -296,15 +296,14 @@ def run_ota_impl_(remote_host, remote_port, password, filename): _LOGGER.error("Connecting to %s:%s failed: %s", remote_host, remote_port, err) return 1 - file_handle = open(filename, "rb") - try: - perform_ota(sock, password, file_handle, filename) - except OTAError as err: - _LOGGER.error(str(err)) - return 1 - finally: - sock.close() - file_handle.close() + with open(filename, "rb") as file_handle: + try: + perform_ota(sock, password, file_handle, filename) + except OTAError as err: + _LOGGER.error(str(err)) + return 1 + finally: + sock.close() return 0 diff --git a/esphome/helpers.py b/esphome/helpers.py index d9730f96a7..ad7b8272b2 100644 --- a/esphome/helpers.py +++ b/esphome/helpers.py @@ -60,10 +60,10 @@ def cpp_string_escape(string, encoding="utf-8"): def run_system_command(*args): import subprocess - p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - stdout, stderr = p.communicate() - rc = p.returncode - return rc, stdout, stderr + with subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p: + stdout, stderr = p.communicate() + rc = p.returncode + return rc, stdout, stderr def mkdir_p(path): diff --git a/esphome/loader.py b/esphome/loader.py index c418008453..d9d407d787 100644 --- a/esphome/loader.py +++ b/esphome/loader.py @@ -80,6 +80,10 @@ class ComponentManifest: def codeowners(self) -> List[str]: return getattr(self.module, "CODEOWNERS", []) + @property + def validate(self): + return getattr(self.module, "validate", None) + @property def source_files(self) -> Dict[Path, SourceFile]: ret = {} diff --git a/esphome/wizard.py b/esphome/wizard.py index 3550e39392..0d912e4bbf 100644 --- a/esphome/wizard.py +++ b/esphome/wizard.py @@ -49,17 +49,6 @@ BASE_CONFIG = """esphome: platform: {platform} board: {board} -wifi: - ssid: "{ssid}" - password: "{psk}" - - # Enable fallback hotspot (captive portal) in case wifi connection fails - ap: - ssid: "{fallback_name}" - password: "{fallback_psk}" - -captive_portal: - # Enable logging logger: @@ -83,12 +72,43 @@ def wizard_file(**kwargs): config = BASE_CONFIG.format(**kwargs) - if kwargs["password"]: - config += ' password: "{0}"\n\nota:\n password: "{0}"\n'.format( - kwargs["password"] + # Configure API + if "password" in kwargs: + config += ' password: "{0}"\n'.format(kwargs["password"]) + + # Configure OTA + config += "\nota:\n" + if "ota_password" in kwargs: + config += ' password: "{0}"'.format(kwargs["ota_password"]) + elif "password" in kwargs: + config += ' password: "{0}"'.format(kwargs["password"]) + + # Configuring wifi + config += "\n\nwifi:\n" + + if "ssid" in kwargs: + config += """ ssid: "{ssid}" + password: "{psk}" +""".format( + **kwargs ) else: - config += "\nota:\n" + config += """ # ssid: "My SSID" + # password: "mypassword" + + networks: +""" + + config += """ + # Enable fallback hotspot (captive portal) in case wifi connection fails + ap: + ssid: "{fallback_name}" + password: "{fallback_psk}" + +captive_portal: +""".format( + **kwargs + ) return config @@ -97,9 +117,9 @@ def wizard_write(path, **kwargs): name = kwargs["name"] board = kwargs["board"] - kwargs["ssid"] = sanitize_double_quotes(kwargs["ssid"]) - kwargs["psk"] = sanitize_double_quotes(kwargs["psk"]) - kwargs["password"] = sanitize_double_quotes(kwargs["password"]) + for key in ("ssid", "psk", "password", "ota_password"): + if key in kwargs: + kwargs[key] = sanitize_double_quotes(kwargs[key]) if "platform" not in kwargs: kwargs["platform"] = "ESP8266" if board in ESP8266_BOARD_PINS else "ESP32" @@ -295,7 +315,7 @@ def wizard(path): safe_print( "First, what's the " + color(Fore.GREEN, "SSID") - + f" (the name) of the WiFi network {name} I should connect to?" + + f" (the name) of the WiFi network {name} should connect to?" ) sleep(1.5) safe_print('For example "{}".'.format(color(Fore.BOLD_WHITE, "Abraham Linksys"))) diff --git a/platformio.ini b/platformio.ini index 7a63ea67a9..ba7724ad24 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,43 +10,49 @@ include_dir = include [common] lib_deps = - esphome/AsyncTCP-esphome@1.2.2 AsyncMqttClient-esphome@0.8.4 ArduinoJson-esphomelib@5.13.3 ESPAsyncWebServer-esphome@1.2.7 - fastled/FastLED@3.3.2 - NeoPixelBus-esphome@2.5.7 - ESPAsyncTCP-esphome@1.2.3 + FastLED@3.3.2 + NeoPixelBus-esphome@2.6.2 1655@1.0.2 ; TinyGPSPlus (has name conflict) 6865@1.0.0 ; TM1651 Battery Display 6306@1.0.3 ; HM3301 build_flags = - -Wno-reorder - -DUSE_WEB_SERVER - -DUSE_FAST_LED_LIGHT - -DUSE_NEO_PIXEL_BUS_LIGHT + -fno-exceptions + -Wno-sign-compare + -Wno-unused-but-set-variable + -Wno-unused-variable -DCLANG_TIDY -DESPHOME_LOG_LEVEL=ESPHOME_LOG_LEVEL_VERY_VERBOSE -; Don't use FlashStringHelper for debug builds because CLion freaks out for all -; log messages -src_filter = + +src_filter = + + + + [env:livingroom8266] -; use Arduino framework v2.3.0 for clang-tidy (latest 2.5.2 breaks static code analysis, see #760) -platform = espressif8266@1.8.0 -board = nodemcuv2 +; use Arduino framework v2.4.2 for clang-tidy (latest 2.5.2 breaks static code analysis, see #760) +platform = platformio/espressif8266@1.8.0 framework = arduino +board = nodemcuv2 lib_deps = ${common.lib_deps} ESP8266WiFi - Hash + ESPAsyncTCP-esphome@1.2.3 + Update build_flags = ${common.build_flags} -src_filter = ${common.src_filter} + +src_filter = ${common.src_filter} [env:livingroom32] -platform = espressif32@1.12.4 -board = nodemcu-32s +platform = platformio/espressif32@3.2.0 framework = arduino -lib_deps = ${common.lib_deps} -build_flags = ${common.build_flags} -DUSE_ETHERNET -src_filter = ${common.src_filter} + +board = nodemcu-32s +lib_deps = + ${common.lib_deps} + esphome/AsyncTCP-esphome@1.2.2 + Update +build_flags = + ${common.build_flags} + -DUSE_ETHERNET +src_filter = + ${common.src_filter} + - diff --git a/requirements.txt b/requirements.txt index f749358001..33436fb1b1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ PyYAML==5.4.1 paho-mqtt==1.5.1 colorama==0.4.4 tornado==6.1 -protobuf==3.15.8 +protobuf==3.17.0 tzlocal==2.1 pytz==2021.1 pyserial==3.5 @@ -11,3 +11,4 @@ ifaddr==0.1.7 platformio==5.1.1 esptool==2.8 click==7.1.2 +esphome-dashboard==20210615.0 diff --git a/requirements_optional.txt b/requirements_optional.txt new file mode 100644 index 0000000000..2c73430109 --- /dev/null +++ b/requirements_optional.txt @@ -0,0 +1,2 @@ +pillow>4.0.0 +cryptography>=2.0.0,<4 diff --git a/requirements_test.txt b/requirements_test.txt index fdd1e91b20..15593a8e12 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,14 +1,13 @@ -pylint==2.7.2 -flake8==3.9.0 -black==20.8b1 -pillow>4.0.0 -cryptography>=2.0.0,<4 +pylint==2.8.2 +flake8==3.9.2 +black==21.5b1 pexpect==4.8.0 pre-commit # Unit tests -pytest==6.2.3 +pytest==6.2.4 pytest-cov==2.11.1 pytest-mock==3.5.1 +pytest-asyncio==0.14.0 asyncmock==0.4.2 hypothesis==5.21.0 diff --git a/script/api_protobuf/api_protobuf.py b/script/api_protobuf/api_protobuf.py index f86145df2f..97cc95e556 100644 --- a/script/api_protobuf/api_protobuf.py +++ b/script/api_protobuf/api_protobuf.py @@ -40,7 +40,16 @@ d = descriptor.FileDescriptorSet.FromString(content) def indent_list(text, padding=" "): - return [padding + line for line in text.splitlines()] + lines = [] + for line in text.splitlines(): + if line == "": + p = "" + elif line.startswith("#ifdef") or line.startswith("#endif"): + p = "" + else: + p = padding + lines.append(p + line) + return lines def indent(text, padding=" "): @@ -103,7 +112,7 @@ class TypeInfo: @property def class_member(self) -> str: - return f"{self.cpp_type} {self.field_name}{{{self.default_value}}}; // NOLINT" + return f"{self.cpp_type} {self.field_name}{{{self.default_value}}};" @property def decode_varint_content(self) -> str: @@ -432,7 +441,7 @@ class SInt64Type(TypeInfo): decode_varint = "value.as_sint64()" encode_func = "encode_sin64" - def dump(self): + def dump(self, name): o = f'sprintf(buffer, "%ll", {name});\n' o += f"out.append(buffer);" return o @@ -514,10 +523,10 @@ class RepeatedTypeInfo(TypeInfo): @property def encode_content(self): - return f"""\ - for (auto {'' if self._ti_is_bool else '&'}it : this->{self.field_name}) {{ - buffer.{self._ti.encode_func}({self.number}, it, true); - }}""" + o = f"for (auto {'' if self._ti_is_bool else '&'}it : this->{self.field_name}) {{\n" + o += f" buffer.{self._ti.encode_func}({self.number}, it, true);\n" + o += f"}}" + return o @property def dump_content(self): @@ -536,12 +545,13 @@ def build_enum_type(desc): out += f" {v.name} = {v.number},\n" out += "};\n" - cpp = f"template<>\n" - cpp += f"const char *proto_enum_to_string(enums::{name} value) {{\n" + cpp = f"template<> const char *proto_enum_to_string(enums::{name} value) {{\n" cpp += f" switch (value) {{\n" for v in desc.value: - cpp += f' case enums::{v.name}: return "{v.name}";\n' - cpp += f' default: return "UNKNOWN";\n' + cpp += f" case enums::{v.name}:\n" + cpp += f' return "{v.name}";\n' + cpp += f" default:\n" + cpp += f' return "UNKNOWN";\n' cpp += f" }}\n" cpp += f"}}\n" @@ -620,21 +630,35 @@ def build_message_type(desc): prot = "bool decode_64bit(uint32_t field_id, Proto64bit value) override;" protected_content.insert(0, prot) - o = f"void {desc.name}::encode(ProtoWriteBuffer buffer) const {{\n" - o += indent("\n".join(encode)) + "\n" + o = f"void {desc.name}::encode(ProtoWriteBuffer buffer) const {{" + if encode: + if len(encode) == 1 and len(encode[0]) + len(o) + 3 < 120: + o += f" {encode[0]} " + else: + o += "\n" + o += indent("\n".join(encode)) + "\n" o += "}\n" cpp += o prot = "void encode(ProtoWriteBuffer buffer) const override;" public_content.append(prot) - o = f"void {desc.name}::dump_to(std::string &out) const {{\n" + o = f"void {desc.name}::dump_to(std::string &out) const {{" if dump: - o += f" char buffer[64];\n" - o += f' out.append("{desc.name} {{\\n");\n' - o += indent("\n".join(dump)) + "\n" - o += f' out.append("}}");\n' + if len(dump) == 1 and len(dump[0]) + len(o) + 3 < 120: + o += f" {dump[0]} " + else: + o += "\n" + o += f" char buffer[64];\n" + o += f' out.append("{desc.name} {{\\n");\n' + o += indent("\n".join(dump)) + "\n" + o += f' out.append("}}");\n' else: - o += f' out.append("{desc.name} {{}}");\n' + o2 = f'out.append("{desc.name} {{}}");' + if len(o) + len(o2) + 3 < 120: + o += f" {o2} " + else: + o += "\n" + o += f" {o2}\n" o += "}\n" cpp += o prot = "void dump_to(std::string &out) const override;" @@ -643,8 +667,11 @@ def build_message_type(desc): out = f"class {desc.name} : public ProtoMessage {{\n" out += " public:\n" out += indent("\n".join(public_content)) + "\n" + out += "\n" out += " protected:\n" - out += indent("\n".join(protected_content)) + "\n" + out += indent("\n".join(protected_content)) + if len(protected_content) > 0: + out += "\n" out += "};\n" return out, cpp @@ -814,13 +841,13 @@ cases.sort() hpp += " protected:\n" hpp += f" bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;\n" out = f"bool {class_name}::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {{\n" -out += f" switch(msg_type) {{\n" +out += f" switch (msg_type) {{\n" for i, case in cases: c = f"case {i}: {{\n" c += indent(case) + "\n" c += f"}}" out += indent(c, " ") + "\n" -out += " default: \n" +out += " default:\n" out += " return false;\n" out += " }\n" out += " return true;\n" diff --git a/script/build_jsonschema.py b/script/build_jsonschema.py index 6d19e25e29..89d621fd5a 100644 --- a/script/build_jsonschema.py +++ b/script/build_jsonschema.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +from esphome.cpp_generator import MockObj import json import argparse import os @@ -54,7 +55,7 @@ def is_ref(jschema): def unref(jschema): - return definitions[jschema[JSC_REF][len("#/definitions/") :]] + return definitions.get(jschema[JSC_REF][len("#/definitions/") :]) def add_definition_array_or_single_object(ref): @@ -104,8 +105,11 @@ def add_registry(registry_name, registry): for name in registry.keys(): schema = get_jschema(str(name), registry[name].schema, create_return_ref=False) if not schema: - schema = {"type": "string"} + schema = {"type": "null"} o_schema = {"type": "object", JSC_PROPERTIES: {name: schema}} + o_schema = create_ref( + registry_name + "-" + name, str(registry[name].schema) + "x", o_schema + ) validators.append(o_schema) definitions[registry_name] = {JSC_ANYOF: validators} @@ -134,7 +138,7 @@ def add_module_schemas(name, module): def get_dirs(): - from esphome.config import CORE_COMPONENTS_PATH + from esphome.loader import CORE_COMPONENTS_PATH dir_names = [ d @@ -146,7 +150,7 @@ def get_dirs(): def get_logger_tags(): - from esphome.config import CORE_COMPONENTS_PATH + from esphome.loader import CORE_COMPONENTS_PATH import glob pattern = re.compile(r'^static const char(\*\s|\s\*)TAG = "(\w.*)";', re.MULTILINE) @@ -241,7 +245,7 @@ def add_components(): elif c.config_schema is not None: # adds root components which are not platforms, e.g. api: logger: - if c.is_multi_conf: + if c.multi_conf: schema = get_jschema(domain, c.config_schema) schema = add_definition_array_or_single_object(schema) else: @@ -322,7 +326,6 @@ def get_entry(parent_key, vschema): elif str(vschema) in ejs.list_schemas: ref = get_jschema(parent_key, ejs.list_schemas[str(vschema)][0]) entry = {JSC_ANYOF: [ref, {"type": "array", "items": ref}]} - elif str(vschema) in ejs.typed_schemas: schema_types = [{"type": "object", "properties": {"type": {"type": "string"}}}] entry = {"allOf": schema_types} @@ -342,6 +345,22 @@ def get_entry(parent_key, vschema): entry = get_automation_schema(parent_key, inner_vschema) elif type == "maybe": entry = get_jschema(parent_key, inner_vschema) + elif type == "one_of": + entry = {"enum": list(inner_vschema)} + elif type == "enum": + entry = {"enum": list(inner_vschema.keys())} + elif type == "effects": + # Like list schema but subset from list. + subset_list = inner_vschema[0] + # get_jschema('strobex', registry['strobe'].schema) + registry_schemas = [] + for name in subset_list: + registry_schemas.append(get_ref("light.EFFECTS_REGISTRY-" + name)) + + entry = { + JSC_ANYOF: [{"type": "array", "items": {JSC_ANYOF: registry_schemas}}] + } + else: raise ValueError("Unknown extracted schema type") elif str(vschema).startswith(" #include #include @@ -7,7 +12,7 @@ using namespace esphome; void setup() { - App.pre_setup("livingroom", __DATE__ " " __TIME__); + App.pre_setup("livingroom", __DATE__ ", " __TIME__, false); auto *log = new logger::Logger(115200, 512, logger::UART_SELECTION_UART0); log->pre_setup(); App.register_component(log); @@ -19,10 +24,12 @@ void setup() { ap.set_password("password1"); wifi->add_sta(ap); - auto *ota = new ota::OTAComponent(8266); - ota->start_safe_mode(); + auto *ota = new ota::OTAComponent(); + ota->set_port(8266); - auto *gpio = new gpio::GPIOSwitch("GPIO Switch", new GPIOPin(8, OUTPUT)); + auto *gpio = new gpio::GPIOSwitch(); + gpio->set_name("GPIO Switch"); + gpio->set_pin(new GPIOPin(8, OUTPUT, false)); App.register_component(gpio); App.register_switch(gpio); diff --git a/tests/livingroom32.cpp b/tests/livingroom32.cpp deleted file mode 100644 index 7005ec95e0..0000000000 --- a/tests/livingroom32.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include - -using namespace esphome; - -void setup() { - App.set_name("livingroom32"); - App.init_log(); - - App.init_wifi("YOUR_SSID", "YOUR_PASSWORD"); - App.init_mqtt("MQTT_HOST", "USERNAME", "PASSWORD"); - App.init_ota()->start_safe_mode(); - - // LEDC is only available on ESP32! for the ESP8266, take a look at App.make_esp8266_pwm_output(). - auto *red = App.make_ledc_output(32); // on pin 32 - auto *green = App.make_ledc_output(33); - auto *blue = App.make_ledc_output(34); - App.make_rgb_light("Livingroom Light", red, green, blue); - - App.make_dht_sensor("Livingroom Temperature", "Livingroom Humidity", 12); - App.make_status_binary_sensor("Livingroom Node Status"); - App.make_restart_switch("Livingroom Restart"); - - App.setup(); -} - -void loop() { App.loop(); } diff --git a/tests/test1.yaml b/tests/test1.yaml index 8fedf4eb20..29df5857d3 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -247,10 +247,10 @@ ble_client: id: ble_blah on_connect: then: - - switch.turn_on: ble1_status + - switch.turn_on: ble1_status on_disconnect: then: - - switch.turn_on: ble1_status + - switch.turn_on: ble1_status mcp23s08: - id: 'mcp23s08_hub' cs_pin: GPIO12 @@ -261,16 +261,18 @@ mcp23s17: cs_pin: GPIO12 deviceaddress: 1 - sensor: - platform: ble_client ble_client_id: ble_foo - name: "Green iTag btn" + name: 'Green iTag btn' service_uuid: 'ffe0' characteristic_uuid: 'ffe1' descriptor_uuid: 'ffe2' notify: true update_interval: never + lambda: |- + ESP_LOGD("main", "Length of data is %i", x.size()); + return x[0]; on_notify: then: - lambda: |- @@ -659,9 +661,14 @@ sensor: update_interval: 15s - platform: pulse_meter name: 'Pulse Meter' + id: pulse_meter_sensor pin: GPIO12 internal_filter: 100ms timeout: 2 min + on_value: + - pulse_meter.set_total_pulses: + id: pulse_meter_sensor + value: 12345 total: name: 'Pulse Meter Total' - platform: rotary_encoder @@ -786,6 +793,7 @@ sensor: update_interval: 15s - platform: template name: 'Template Sensor' + state_class: measurement id: template_sensor lambda: |- if (id(ultrasonic_sensor1).state > 1) { @@ -883,25 +891,11 @@ sensor: name: 'AQI' calculation_type: 'CAQI' - platform: teleinfo - uart_id: uart0 - tags: - - tag_name: 'HCHC' - sensor: - name: 'hchc' - unit_of_measurement: 'Wh' - icon: mdi:flash - - tag_name: 'HCHP' - sensor: - name: 'hchp' - unit_of_measurement: 'Wh' - icon: mdi:flash - - tag_name: 'PAPP' - sensor: - name: 'papp' - unit_of_measurement: 'VA' - icon: mdi:flash - update_interval: 60s - historical_mode: true + tag_name: "HCHC" + name: "hchc" + unit_of_measurement: "Wh" + icon: mdi:flash + teleinfo_id: myteleinfo - platform: mcp9808 name: 'MCP9808 Temperature' update_interval: 15s @@ -909,6 +903,28 @@ sensor: id: ph_ezo address: 99 unit_of_measurement: 'pH' + - platform: cs5460a + id: cs5460a1 + current: + name: "Socket current" + voltage: + name: "Mains voltage" + power: + name: "Socket power" + on_value: + then: + cs5460a.restart: cs5460a1 + samples: 1600 + pga_gain: 10X + current_gain: 0.01 + voltage_gain: 0.000573 + current_hpf: on + voltage_hpf: on + phase_offset: 20 + pulse_energy: 0.01 kWh + cs_pin: + mcp23xxx: mcp23017_hub + number: 14 esp32_touch: setup_mode: False @@ -1509,23 +1525,6 @@ climate: name: Toshiba Climate - platform: hitachi_ac344 name: Hitachi Climate - - platform: midea_ac - visual: - min_temperature: 18 °C - max_temperature: 25 °C - temperature_step: 0.1 °C - name: 'Electrolux EACS' - beeper: true - outdoor_temperature: - name: 'Temp' - power_usage: - name: 'Power' - humidity_setpoint: - name: 'Hum' - -midea_dongle: - uart_id: uart0 - strength_icon: true switch: - platform: gpio @@ -1869,6 +1868,12 @@ display: - id: page2 lambda: |- // Nothing + on_page_change: + from: page1 + to: page2 + then: + lambda: |- + ESP_LOGD("display", "1 -> 2"); - platform: ssd1306_spi model: 'SSD1306 128x64' cs_pin: GPIO23 @@ -1942,6 +1947,7 @@ display: row_start: 0 lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); + tm1651: id: tm1651_battery clk_pin: GPIO23 @@ -1964,6 +1970,12 @@ pn532_spi: - mqtt.publish: topic: the/topic payload: !lambda 'return x;' + on_tag_removed: + - lambda: |- + ESP_LOGD("main", "Removed tag %s", x.c_str()); + - mqtt.publish: + topic: the/topic + payload: !lambda 'return x;' pn532_i2c: @@ -2043,7 +2055,6 @@ tca9548a: multiplexer: id: multiplex0 channel: 0 - pcf8574: - id: 'pcf8574_hub' @@ -2125,6 +2136,10 @@ text_sensor: - platform: version name: 'ESPHome Version No Timestamp' hide_timestamp: True + - platform: teleinfo + tag_name: "OPTARIF" + name: "optarif" + teleinfo_id: myteleinfo sn74hc595: - id: 'sn74hc595_hub' @@ -2155,3 +2170,9 @@ canbus: lambda: 'return x[0] == 0x11;' then: light.toggle: ${roomname}_lights + +teleinfo: + id: myteleinfo + uart_id: uart0 + update_interval: 60s + historical_mode: true diff --git a/tests/test2.yaml b/tests/test2.yaml index 34724ee955..faa76300cc 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -63,6 +63,10 @@ sensor: - platform: homeassistant entity_id: sensor.hello_world id: ha_hello_world + - platform: homeassistant + entity_id: climate.living_room + attribute: temperature + id: ha_hello_world_temperature - platform: ble_rssi mac_address: AC:37:43:77:5F:4C name: 'BLE Google Home Mini RSSI value' @@ -246,6 +250,10 @@ binary_sensor: - platform: homeassistant entity_id: binary_sensor.hello_world id: ha_hello_world_binary + - platform: homeassistant + entity_id: binary_sensor.hello + attribute: world + id: ha_hello_world_binary_attribute - platform: ble_presence mac_address: AC:37:43:77:5F:4C name: 'ESP32 BLE Tracker Google Home Mini' @@ -346,6 +354,8 @@ text_sensor: - homeassistant.tag_scanned: 1234-abcd - deep_sleep.enter: sleep_duration: 30min + - deep_sleep.enter: + sleep_duration: !lambda "return 30 * 60 * 1000;" - platform: template name: 'Template Text Sensor' lambda: |- @@ -353,6 +363,10 @@ text_sensor: - platform: homeassistant entity_id: sensor.hello_world2 id: ha_hello_world2 + - platform: homeassistant + entity_id: sensor.hello_world3 + id: ha_hello_world3 + attribute: some_attribute - platform: ble_scanner name: Scanner diff --git a/tests/test3.yaml b/tests/test3.yaml index 8104c70fbf..af5398b604 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -1,6 +1,6 @@ esphome: - name: $devicename - comment: $devicecomment + name: $device_name + comment: $device_comment platform: ESP8266 board: d1_mini build_path: build/test3 @@ -13,8 +13,8 @@ esphome: - custom.h substitutions: - devicename: test3 - devicecomment: test3 device + device_name: test3 + device_comment: test3 device min_sub: '0.03' max_sub: '12.0%' @@ -141,6 +141,14 @@ api: then: - dfplayer.random + - service: dfplayer_volume_up + then: + - dfplayer.volume_up + + - service: dfplayer_volume_down + then: + - dfplayer.volume_down + - service: battery_level_percent variables: level_percent: int @@ -213,9 +221,33 @@ spi: miso_pin: GPIO14 uart: - - tx_pin: GPIO1 + - id: uart1 + tx_pin: GPIO1 rx_pin: GPIO3 baud_rate: 115200 + - id: uart2 + tx_pin: GPIO4 + rx_pin: GPIO5 + baud_rate: 9600 + - id: uart3 + tx_pin: GPIO4 + rx_pin: GPIO5 + baud_rate: 4800 + - id: uart4 + tx_pin: GPIO4 + rx_pin: GPIO5 + baud_rate: 9600 + - id: uart5 + tx_pin: GPIO4 + rx_pin: GPIO5 + baud_rate: 9600 + - id: uart6 + tx_pin: GPIO4 + rx_pin: GPIO5 + baud_rate: 9600 + +modbus: + uart_id: uart1 ota: safe_mode: True @@ -369,6 +401,7 @@ sensor: active_power_b: name: ADE7953 Active Power B - platform: pzem004t + uart_id: uart3 voltage: name: 'PZEM00T Voltage' current: @@ -408,6 +441,7 @@ sensor: name: 'AQI' calculation_type: 'AQI' - platform: pmsx003 + uart_id: uart2 type: PMSX003 pm_1_0: name: 'PM 1.0 Concentration' @@ -415,25 +449,8 @@ sensor: name: 'PM 2.5 Concentration' pm_10_0: name: 'PM 10.0 Concentration' - - platform: pmsx003 - type: PMS5003T - pm_2_5: - name: 'PM 2.5 Concentration' - temperature: - name: 'PMS Temperature' - humidity: - name: 'PMS Humidity' - - platform: pmsx003 - type: PMS5003ST - pm_2_5: - name: 'PM 2.5 Concentration' - temperature: - name: 'PMS Temperature' - humidity: - name: 'PMS Humidity' - formaldehyde: - name: 'PMS Formaldehyde Concentration' - platform: cse7766 + uart_id: uart3 voltage: name: 'CSE7766 Voltage' current: @@ -443,7 +460,7 @@ sensor: - platform: ezo id: ph_ezo address: 99 - unit_of_measurement: 'pH' + unit_of_measurement: 'pH' - platform: tof10120 name: "Distance sensor" update_interval: 5s @@ -460,6 +477,62 @@ sensor: name: "Fingerprint Last Finger ID" last_confidence: name: "Fingerprint Last Confidence" + - platform: sdm_meter + phase_a: + current: + name: 'Phase A Current' + voltage: + name: 'Phase A Voltage' + active_power: + name: 'Phase A Power' + power_factor: + name: 'Phase A Power Factor' + apparent_power: + name: 'Phase A Apparent Power' + reactive_power: + name: 'Phase A Reactive Power' + phase_angle: + name: 'Phase A Phase Angle' + phase_b: + current: + name: 'Phase B Current' + voltage: + name: 'Phase B Voltage' + active_power: + name: 'Phase B Power' + power_factor: + name: 'Phase B Power Factor' + apparent_power: + name: 'Phase B Apparent Power' + reactive_power: + name: 'Phase B Reactive Power' + phase_angle: + name: 'Phase B Phase Angle' + phase_c: + current: + name: 'Phase C Current' + voltage: + name: 'Phase C Voltage' + active_power: + name: 'Phase C Power' + power_factor: + name: 'Phase C Power Factor' + apparent_power: + name: 'Phase C Apparent Power' + reactive_power: + name: 'Phase C Reactive Power' + phase_angle: + name: 'Phase C Phase Angle' + frequency: + name: 'Frequency' + import_active_energy: + name: 'Import Active Energy' + export_active_energy: + name: 'Export Active Energy' + import_reactive_energy: + name: 'Import Reactive Energy' + export_reactive_energy: + name: 'Export Reactive Energy' time: - platform: homeassistant @@ -585,6 +658,17 @@ script: - id: my_script then: - lambda: 'ESP_LOGD("main", "Hello World!");' + - id: climate_custom + then: + - climate.control: + id: midea_ac_unit + custom_preset: FREEZE_PROTECTION + custom_fan_mode: SILENT + - id: climate_preset + then: + - climate.control: + id: midea_ac_unit + preset: SLEEP sm2135: data_pin: GPIO12 @@ -746,6 +830,32 @@ climate: kp: 0.0 ki: 0.0 kd: 0.0 + - platform: midea_ac + id: midea_ac_unit + visual: + min_temperature: 18 °C + max_temperature: 25 °C + temperature_step: 0.1 °C + name: "Electrolux EACS" + beeper: true + custom_fan_modes: + - SILENT + - TURBO + preset_eco: true + preset_sleep: true + preset_boost: true + custom_presets: + - FREEZE_PROTECTION + outdoor_temperature: + name: "Temp" + power_usage: + name: "Power" + humidity_setpoint: + name: "Hum" + +midea_dongle: + uart_id: uart1 + strength_icon: true cover: - platform: endstop @@ -867,6 +977,7 @@ light: effects: - wled: - adalight: + uart_id: uart3 - e131: universe: 1 - platform: hbridge @@ -888,6 +999,7 @@ ttp229_bsf: scl_pin: D1 sim800l: + uart_id: uart4 on_sms_received: - lambda: |- std::string str; @@ -900,6 +1012,7 @@ sim800l: recipient: '+1234' dfplayer: + uart_id: uart5 on_finished_playback: then: if: @@ -913,6 +1026,7 @@ tm1651: dio_pin: D5 rf_bridge: + uart_id: uart5 on_code_received: - lambda: |- uint32_t test; @@ -1006,3 +1120,4 @@ fingerprint_grow: event: esphome.${devicename}_fingerprint_grow_enrollment_failed data: finger_id: !lambda 'return finger_id;' + uart_id: uart6 diff --git a/tests/test4.yaml b/tests/test4.yaml index e85cdfbc19..7868fd4968 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -86,6 +86,29 @@ binary_sensor: - platform: tuya id: tuya_binary_sensor sensor_datapoint: 1 + - platform: template + id: ar1 + lambda: 'return {};' + filters: + - autorepeat: + - delay: 2s + time_off: 100ms + time_on: 900ms + - delay: 4s + time_off: 100ms + time_on: 400ms + on_state: + then: + - lambda: 'ESP_LOGI("ar1:", "%d", x);' + - platform: xpt2046 + xpt2046_id: touchscreen + id: touch_key0 + x_min: 80 + x_max: 160 + y_min: 106 + y_max: 212 + on_state: + - lambda: 'ESP_LOGI("main", "key0: %s", (x ? "touch" : "release"));' climate: - platform: tuya @@ -110,6 +133,17 @@ light: rgb_order: GRB default_transition_length: 0s color_correct: [50%, 50%, 50%] + - platform: tuya + id: tuya_light + switch_datapoint: 1 + dimmer_datapoint: 2 + min_value_datapoint: 3 + color_temperature_datapoint: 4 + min_value: 1 + max_value: 100 + cold_white_color_temperature: 153 mireds + warm_white_color_temperature: 500 mireds + gamma_correct: 1 display: - platform: addressable_light @@ -132,6 +166,7 @@ display: it.rectangle(3, 3, it.get_width()-6, it.get_height()-6, red); rotation: 0° update_interval: 16ms + - platform: waveshare_epaper cs_pin: GPIO23 dc_pin: GPIO23 @@ -160,9 +195,51 @@ display: lambda: |- it.rectangle(0, 0, it.get_width(), it.get_height()); +esp32_camera: + name: ESP-32 Camera + data_pins: [GPIO17, GPIO35, GPIO34, GPIO5, GPIO39, GPIO18, GPIO36, GPIO19] + vsync_pin: GPIO22 + href_pin: GPIO26 + pixel_clock_pin: GPIO21 + external_clock: + pin: GPIO27 + frequency: 20MHz + i2c_pins: + sda: GPIO25 + scl: GPIO23 + reset_pin: GPIO15 + power_down_pin: GPIO1 + resolution: 640x480 + jpeg_quality: 10 + external_components: - source: github://esphome/esphome@dev refresh: 1d components: ["bh1750"] - source: ../esphome/components components: ["sntp"] +xpt2046: + id: touchscreen + cs_pin: 17 + irq_pin: 16 + update_interval: 50ms + report_interval: 1s + threshold: 400 + dimension_x: 240 + dimension_y: 320 + calibration_x_min: 3860 + calibration_x_max: 280 + calibration_y_min: 340 + calibration_y_max: 3860 + swap_x_y: False + on_state: + - lambda: |- + ESP_LOGI("main", "args x=%d, y=%d, touched=%s", x, y, (touched ? "touch" : "release")); + ESP_LOGI("main", "member x=%d, y=%d, touched=%d, x_raw=%d, y_raw=%d, z_raw=%d", + id(touchscreen).x, + id(touchscreen).y, + (int) id(touchscreen).touched, + id(touchscreen).x_raw, + id(touchscreen).y_raw, + id(touchscreen).z_raw + ); diff --git a/tests/test5.yaml b/tests/test5.yaml new file mode 100644 index 0000000000..ba047721e2 --- /dev/null +++ b/tests/test5.yaml @@ -0,0 +1,40 @@ +esphome: + name: test5 + platform: ESP32 + board: nodemcu-32s + build_path: build/test5 + project: + name: esphome.test5_project + version: "1.0.0" + +wifi: + networks: + - ssid: 'MySSID' + password: 'password1' + +api: + +ota: + +logger: + +binary_sensor: + - platform: gpio + pin: GPIO0 + id: io0_button + +output: + - platform: gpio + pin: GPIO2 + id: built_in_led + +esp32_ble: + +esp32_ble_server: + manufacturer: "ESPHome" + model: "Test5" + +esp32_improv: + authorizer: io0_button + authorized_duration: 1min + status_indicator: built_in_led diff --git a/tests/unit_tests/test_core.py b/tests/unit_tests/test_core.py index 37a4920224..4e60880033 100644 --- a/tests/unit_tests/test_core.py +++ b/tests/unit_tests/test_core.py @@ -490,11 +490,15 @@ class TestEsphomeCore: def test_reset(self, target): """Call reset on target and compare to new instance""" - other = core.EsphomeCore() + other = core.EsphomeCore().__dict__ target.reset() + t = target.__dict__ + # ignore event loop + del other["event_loop"] + del t["event_loop"] - assert target.__dict__ == other.__dict__ + assert t == other def test_address__none(self, target): target.config = {} diff --git a/tests/unit_tests/test_cpp_helpers.py b/tests/unit_tests/test_cpp_helpers.py index c6f37f6b5d..3e317589a9 100644 --- a/tests/unit_tests/test_cpp_helpers.py +++ b/tests/unit_tests/test_cpp_helpers.py @@ -6,25 +6,24 @@ from esphome import const from esphome.cpp_generator import MockObj -def test_gpio_pin_expression__conf_is_none(monkeypatch): - target = ch.gpio_pin_expression(None) - - actual = next(target) +@pytest.mark.asyncio +async def test_gpio_pin_expression__conf_is_none(monkeypatch): + actual = await ch.gpio_pin_expression(None) assert actual is None -def test_gpio_pin_expression__new_pin(monkeypatch): - target = ch.gpio_pin_expression( +@pytest.mark.asyncio +async def test_gpio_pin_expression__new_pin(monkeypatch): + actual = await ch.gpio_pin_expression( {const.CONF_NUMBER: 42, const.CONF_MODE: "input", const.CONF_INVERTED: False} ) - actual = next(target) - assert isinstance(actual, MockObj) -def test_register_component(monkeypatch): +@pytest.mark.asyncio +async def test_register_component(monkeypatch): var = Mock(base="foo.bar") app_mock = Mock(register_component=Mock(return_value=var)) @@ -36,9 +35,7 @@ def test_register_component(monkeypatch): add_mock = Mock() monkeypatch.setattr(ch, "add", add_mock) - target = ch.register_component(var, {}) - - actual = next(target) + actual = await ch.register_component(var, {}) assert actual is var add_mock.assert_called_once() @@ -46,18 +43,19 @@ def test_register_component(monkeypatch): assert core_mock.component_ids == [] -def test_register_component__no_component_id(monkeypatch): +@pytest.mark.asyncio +async def test_register_component__no_component_id(monkeypatch): var = Mock(base="foo.eek") core_mock = Mock(component_ids=["foo.bar"]) monkeypatch.setattr(ch, "CORE", core_mock) with pytest.raises(ValueError, match="Component ID foo.eek was not declared to"): - target = ch.register_component(var, {}) - next(target) + await ch.register_component(var, {}) -def test_register_component__with_setup_priority(monkeypatch): +@pytest.mark.asyncio +async def test_register_component__with_setup_priority(monkeypatch): var = Mock(base="foo.bar") app_mock = Mock(register_component=Mock(return_value=var)) @@ -69,7 +67,7 @@ def test_register_component__with_setup_priority(monkeypatch): add_mock = Mock() monkeypatch.setattr(ch, "add", add_mock) - target = ch.register_component( + actual = await ch.register_component( var, { const.CONF_SETUP_PRIORITY: "123", @@ -77,8 +75,6 @@ def test_register_component__with_setup_priority(monkeypatch): }, ) - actual = next(target) - assert actual is var add_mock.assert_called() assert add_mock.call_count == 3