From a1b6a91642dd326ba87815d37718b04b527f200e Mon Sep 17 00:00:00 2001 From: Aalian Khan <39784853+AalianKhan@users.noreply.github.com> Date: Mon, 13 May 2019 14:51:21 -0400 Subject: [PATCH 01/30] Update nextion.h --- esphome/components/nextion/nextion.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index d8fe3762c9..91aaecb96a 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -23,6 +23,8 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * Set the text of a component to a static string. * @param component The component name. * @param text The static text to set. + * Example: `it.set_component_text("textview", "Hello World!");` + * Component named `textview` `txt` value has been changed to `Hello World`. */ void set_component_text(const char *component, const char *text); /** @@ -30,18 +32,25 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * @param component The component name. * @param format The printf-style format string. * @param ... The arguments to the format. + * Example: `it.set_component_text_printf("textview", "The uptime is: %.1f", id(uptime_sensor).state);` + * Component named `textview` has been changed to `The uptime is:` Then the value of `uptime_sensor`. + * For example when `uptime_sensor` = 506, then, `The uptime is: 506` will be displayed. */ void set_component_text_printf(const char *component, const char *format, ...) __attribute__((format(printf, 3, 4))); /** * Set the integer value of a component * @param component The component name. * @param value The value to set. + * Example: `it.set_component_value("gauge", 50);` + * Component named `gauge` has changed the `val`to 50. */ void set_component_value(const char *component, int value); /** * Set the picture of an image component. * @param component The component name. * @param value The picture name. + * Example: `it.set_component_picture("pic", "4");` + * The picture component named `pic`, has changed the image which has the ID `4`, Which was set in the Nextion editor. */ void set_component_picture(const char *component, const char *picture) { this->send_command_printf("%s.val=%s", component, picture); @@ -50,12 +59,18 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * Set the background color of a component. * @param component The component name. * @param color The color (as a string). + * Example: `it.set_component_background_color("button", "17013"); + * The background color of component named `button`, has been changed to blue. + * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors. */ void set_component_background_color(const char *component, const char *color); /** * Set the pressed background color of a component. * @param component The component name. * @param color The color (as a string). + * Example: `it.set_component_pressed_background_color("button", "17013"` + * The pressed background color of the component named `button` has been changed to blue. + * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors */ void set_component_pressed_background_color(const char *component, const char *color); /** From 6fff2e5957369b6b8928e13eeb2654b2e4840975 Mon Sep 17 00:00:00 2001 From: Aalian Khan <39784853+AalianKhan@users.noreply.github.com> Date: Mon, 13 May 2019 21:24:45 -0400 Subject: [PATCH 02/30] Added more examples --- esphome/components/nextion/nextion.h | 42 ++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index 91aaecb96a..b5bc86ec62 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -68,8 +68,8 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * Set the pressed background color of a component. * @param component The component name. * @param color The color (as a string). - * Example: `it.set_component_pressed_background_color("button", "17013"` - * The pressed background color of the component named `button` has been changed to blue. + * Example: `it.set_component_pressed_background_color("button", "17013"); + * The pressed background color of the component named `button` has been changed to blue. So when the button is pressed, the background color will change to blue. * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors */ void set_component_pressed_background_color(const char *component, const char *color); @@ -77,12 +77,18 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * Set the font color of a component. * @param component The component name. * @param color The color (as a string). + * Example: `it.set_component_font_color("textview", "17013); + * The font color of component named `textview` has been changed to blue. + * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors */ void set_component_font_color(const char *component, const char *color); /** * Set the pressed font color of a component. * @param component The component name. * @param color The color (as a string). + * Example: `it.set_component_pressed_font_color("button", "17013");` + * The pressed font color for component named button has been changed to blue. So when `button` is pressed, the font color will change to blue + * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors */ void set_component_pressed_font_color(const char *component, const char *color); /** @@ -90,12 +96,16 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * @param component The component name. * @param x The x coordinate. * @param y The y coordinate. + * Example: `it.set_component_coordinates("pic", 55, 100);` + * The component named `pic` has moved to the x coordinate `55` and y coordinate `100`. */ void set_component_coordinates(const char *component, int x, int y); /** * Set the font id for a component. * @param component The component name. * @param font_id The ID of the font (number). + * Example: `it.set_component_font("textview", "3");` + * Changes the font of the component named `textveiw`. Font IDs are set in the Nextion Editor. */ void set_component_font(const char *component, uint8_t font_id); #ifdef USE_TIME @@ -109,26 +119,36 @@ class Nextion : public PollingComponent, public uart::UARTDevice { /** * Show the page with a given name. * @param page The name of the page. + * Example: `it.goto_page("main");` + * Switches to the page named `main`. Pages are named in the Nextion Editor. */ void goto_page(const char *page); /** * Hide a component. * @param component The component name. + * Example: `hide_component("button");` + * Hides the component named `button`. */ void hide_component(const char *component); /** * Show a component. * @param component The component name. + * Example: `show_component("button");` + * Shows the component named `button`. */ void show_component(const char *component); /** * Enable touch for a component. * @param component The component name. + * Example: `enable_component_touch("button");` + * Enables touch for component named `button`. */ void enable_component_touch(const char *component); /** * Disable touch for a component. * @param component The component name. + * Examlple: `disable_component_touch("button");` + * Disables touch for component named `button`. */ void disable_component_touch(const char *component); /** @@ -143,6 +163,8 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * @param picture_id The picture id. * @param x1 The x coordinate. * @param y1 The y coordniate. + * Example: `display_picture(2, 15, 25);` + * Displays the picture who has the id `2` at the x coordinates `15` and y coordinates `25`. */ void display_picture(int picture_id, int x_start, int y_start); /** @@ -152,6 +174,9 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * @param width The width to draw. * @param height The height to draw. * @param color The color to draw with (as a string). + * Example: `fill_area(50, 50, 100, 100, "17013");` + * Fills an area that starts at x coordiante `50` and y coordinate `50` with a height of `100` and width of `100` with the color of blue. + * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors */ void fill_area(int x1, int y1, int width, int height, const char *color); /** @@ -161,6 +186,9 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * @param x2 The ending x coordinate. * @param y2 The ending y coordinate. * @param color The color to draw with (as a string). + * Example: `it.line(50, 50, 75, 75, "17013");` + * Makes a line that starts at x coordinate `50` and y coordinate `50` and ends at x coordinate `75` and y coordinate `75` with the color of blue. + * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors. */ void line(int x1, int y1, int x2, int y2, const char *color); /** @@ -170,6 +198,9 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * @param width The width of the rectangle. * @param height The height of the rectangle. * @param color The color to draw with (as a string). + * Example: `it.rectangle(25, 35, 40, 50, "17013");` + * Makes a outline of a rectangle that starts at x coordinate `25` and y coordinate `35` and has a width of `40` and a length of `50` with color of blue. + * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors. */ void rectangle(int x1, int y1, int width, int height, const char *color); /** @@ -186,17 +217,24 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * @param center_y The center y coordinate. * @param radius The circle radius. * @param color The color to draw with (as a string). + * Example: `it.filled_cricle(25, 25, 10, "17013");` + * Makes a filled circle at the x cordinates `25` and y coordinate `25` with a radius of `10` with a color of blue. + * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors. */ void filled_circle(int center_x, int center_y, int radius, const char *color); /** Set the brightness of the backlight. * * @param brightness The brightness, from 0 to 100. + * Example: `it.set_backlight_brightness(30);` + * Changes the brightness of the display to 30%. */ void set_backlight_brightness(uint8_t brightness); /** * Set the touch sleep timeout of the display. * @param timeout Timeout in seconds. + * Example: `it.set_touch_sleep_timeout(30);` + * After 30 seconds the display will go to sleep. Note: the display will only wakeup by a restart or by setting up `thup`. */ void set_touch_sleep_timeout(uint16_t timeout); From bd45f6bd8e7398477b04210a36b492e844cd6a5b Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Fri, 24 May 2019 16:20:28 +0200 Subject: [PATCH 03/30] Register components properly --- esphome/components/bang_bang/climate.py | 2 +- esphome/components/climate/climate_mode.cpp | 8 ++++---- esphome/components/deep_sleep/deep_sleep_component.cpp | 2 +- .../components/homeassistant/binary_sensor/__init__.py | 3 ++- esphome/components/homeassistant/sensor/__init__.py | 3 ++- esphome/components/mqtt/mqtt_climate.cpp | 8 ++++---- 6 files changed, 14 insertions(+), 12 deletions(-) diff --git a/esphome/components/bang_bang/climate.py b/esphome/components/bang_bang/climate.py index 88828ef44f..4ef811c55d 100644 --- a/esphome/components/bang_bang/climate.py +++ b/esphome/components/bang_bang/climate.py @@ -7,7 +7,7 @@ from esphome.const import CONF_AWAY_CONFIG, CONF_COOL_ACTION, \ CONF_ID, CONF_IDLE_ACTION, CONF_SENSOR bang_bang_ns = cg.esphome_ns.namespace('bang_bang') -BangBangClimate = bang_bang_ns.class_('BangBangClimate', climate.Climate) +BangBangClimate = bang_bang_ns.class_('BangBangClimate', climate.Climate, cg.Component) BangBangClimateTargetTempConfig = bang_bang_ns.struct('BangBangClimateTargetTempConfig') CONFIG_SCHEMA = cv.All(climate.CLIMATE_SCHEMA.extend({ diff --git a/esphome/components/climate/climate_mode.cpp b/esphome/components/climate/climate_mode.cpp index 32d42b706f..07b97f4f33 100644 --- a/esphome/components/climate/climate_mode.cpp +++ b/esphome/components/climate/climate_mode.cpp @@ -6,13 +6,13 @@ namespace climate { const char *climate_mode_to_string(ClimateMode mode) { switch (mode) { case CLIMATE_MODE_OFF: - return "off"; + return "OFF"; case CLIMATE_MODE_AUTO: - return "auto"; + return "AUTO"; case CLIMATE_MODE_COOL: - return "cool"; + return "COOL"; case CLIMATE_MODE_HEAT: - return "heat"; + return "HEAT"; default: return "UNKNOWN"; } diff --git a/esphome/components/deep_sleep/deep_sleep_component.cpp b/esphome/components/deep_sleep/deep_sleep_component.cpp index ed14f3c824..5f6aaf24cd 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.cpp +++ b/esphome/components/deep_sleep/deep_sleep_component.cpp @@ -19,7 +19,7 @@ void DeepSleepComponent::setup() { void DeepSleepComponent::dump_config() { ESP_LOGCONFIG(TAG, "Setting up Deep Sleep..."); if (this->sleep_duration_.has_value()) { - ESP_LOGCONFIG(TAG, " Sleep Duration: %llu ms", *this->sleep_duration_ / 1000); + ESP_LOGCONFIG(TAG, " Sleep Duration: %u ms", *this->sleep_duration_ / 1000); } if (this->run_duration_.has_value()) { ESP_LOGCONFIG(TAG, " Run Duration: %u ms", *this->run_duration_); diff --git a/esphome/components/homeassistant/binary_sensor/__init__.py b/esphome/components/homeassistant/binary_sensor/__init__.py index b78836f18f..88e2f2fcb2 100644 --- a/esphome/components/homeassistant/binary_sensor/__init__.py +++ b/esphome/components/homeassistant/binary_sensor/__init__.py @@ -6,7 +6,8 @@ from .. import homeassistant_ns DEPENDENCIES = ['api'] HomeassistantBinarySensor = homeassistant_ns.class_('HomeassistantBinarySensor', - binary_sensor.BinarySensor) + binary_sensor.BinarySensor, + cg.Component) CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({ cv.GenerateID(): cv.declare_id(HomeassistantBinarySensor), diff --git a/esphome/components/homeassistant/sensor/__init__.py b/esphome/components/homeassistant/sensor/__init__.py index cd5e4a74e2..577efca79b 100644 --- a/esphome/components/homeassistant/sensor/__init__.py +++ b/esphome/components/homeassistant/sensor/__init__.py @@ -6,7 +6,8 @@ from .. import homeassistant_ns DEPENDENCIES = ['api'] -HomeassistantSensor = homeassistant_ns.class_('HomeassistantSensor', sensor.Sensor) +HomeassistantSensor = homeassistant_ns.class_('HomeassistantSensor', sensor.Sensor, + cg.Component) CONFIG_SCHEMA = sensor.sensor_schema(UNIT_EMPTY, ICON_EMPTY, 1).extend({ cv.GenerateID(): cv.declare_id(HomeassistantSensor), diff --git a/esphome/components/mqtt/mqtt_climate.cpp b/esphome/components/mqtt/mqtt_climate.cpp index 590a6db7b4..8085fbf0f2 100644 --- a/esphome/components/mqtt/mqtt_climate.cpp +++ b/esphome/components/mqtt/mqtt_climate.cpp @@ -24,12 +24,12 @@ void MQTTClimateComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryC JsonArray &modes = root.createNestedArray("modes"); // sort array for nice UI in HA if (traits.supports_mode(CLIMATE_MODE_AUTO)) - modes.add(climate_mode_to_string(CLIMATE_MODE_AUTO)); - modes.add(climate_mode_to_string(CLIMATE_MODE_OFF)); + modes.add("auto"); + modes.add("off"); if (traits.supports_mode(CLIMATE_MODE_COOL)) - modes.add(climate_mode_to_string(CLIMATE_MODE_COOL)); + modes.add("cool"); if (traits.supports_mode(CLIMATE_MODE_HEAT)) - modes.add(climate_mode_to_string(CLIMATE_MODE_HEAT)); + modes.add("heat"); if (traits.get_supports_two_point_target_temperature()) { // temperature_low_command_topic From aa7389432ed740bfaff7352f3c4d1db3ea4c261b Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Fri, 24 May 2019 16:34:01 +0200 Subject: [PATCH 04/30] Fixes --- esphome/components/ethernet/__init__.py | 3 ++- esphome/components/status/status_binary_sensor.cpp | 9 +++------ esphome/components/status/status_binary_sensor.h | 3 --- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/esphome/components/ethernet/__init__.py b/esphome/components/ethernet/__init__.py index 50a0d99d32..ce7422d05b 100644 --- a/esphome/components/ethernet/__init__.py +++ b/esphome/components/ethernet/__init__.py @@ -67,7 +67,7 @@ CONFIG_SCHEMA = cv.All(cv.Schema({ cv.Optional(CONF_USE_ADDRESS): cv.string_strict, cv.Optional('hostname'): cv.invalid("The hostname option has been removed in 1.11.0"), -}), validate) +}).extend(cv.COMPONENT_SCHEMA), validate) def manual_ip(config): @@ -84,6 +84,7 @@ def manual_ip(config): @coroutine_with_priority(60.0) def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) cg.add(var.set_phy_addr(config[CONF_PHY_ADDR])) cg.add(var.set_mdc_pin(config[CONF_MDC_PIN])) diff --git a/esphome/components/status/status_binary_sensor.cpp b/esphome/components/status/status_binary_sensor.cpp index 5485c6ebcd..d004c70b95 100644 --- a/esphome/components/status/status_binary_sensor.cpp +++ b/esphome/components/status/status_binary_sensor.cpp @@ -18,19 +18,16 @@ void StatusBinarySensor::loop() { bool status = network_is_connected(); #ifdef USE_MQTT if (mqtt::global_mqtt_client != nullptr) { - status = mqtt::global_mqtt_client->is_connected(); + status = status && mqtt::global_mqtt_client->is_connected(); } #endif #ifdef USE_API if (api::global_api_server != nullptr) { - status = api::global_api_server->is_connected(); + status = status && api::global_api_server->is_connected(); } #endif - if (this->last_status_ != status) { - this->publish_state(status); - this->last_status_ = status; - } + this->publish_state(status); } void StatusBinarySensor::setup() { this->publish_state(false); } void StatusBinarySensor::dump_config() { LOG_BINARY_SENSOR("", "Status Binary Sensor", this); } diff --git a/esphome/components/status/status_binary_sensor.h b/esphome/components/status/status_binary_sensor.h index fa121291a1..08aa0fb32f 100644 --- a/esphome/components/status/status_binary_sensor.h +++ b/esphome/components/status/status_binary_sensor.h @@ -16,9 +16,6 @@ class StatusBinarySensor : public binary_sensor::BinarySensor, public Component float get_setup_priority() const override { return setup_priority::DATA; } bool is_status_binary_sensor() const override { return true; } - - protected: - bool last_status_{false}; }; } // namespace status From 422754ed63ac82072a69d5fb6146d3399d4bac84 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Fri, 24 May 2019 17:20:06 +0200 Subject: [PATCH 05/30] Lint --- esphome/components/deep_sleep/deep_sleep_component.cpp | 3 ++- script/clang-format | 4 ++-- script/clang-tidy | 4 ++-- script/helpers.py | 10 ++++++++++ script/lint-python | 8 +++++--- 5 files changed, 21 insertions(+), 8 deletions(-) diff --git a/esphome/components/deep_sleep/deep_sleep_component.cpp b/esphome/components/deep_sleep/deep_sleep_component.cpp index 5f6aaf24cd..217c0cbf0d 100644 --- a/esphome/components/deep_sleep/deep_sleep_component.cpp +++ b/esphome/components/deep_sleep/deep_sleep_component.cpp @@ -19,7 +19,8 @@ void DeepSleepComponent::setup() { void DeepSleepComponent::dump_config() { ESP_LOGCONFIG(TAG, "Setting up Deep Sleep..."); if (this->sleep_duration_.has_value()) { - ESP_LOGCONFIG(TAG, " Sleep Duration: %u ms", *this->sleep_duration_ / 1000); + uint32_t duration = *this->sleep_duration_ / 1000; + ESP_LOGCONFIG(TAG, " Sleep Duration: %u ms", duration); } if (this->run_duration_.has_value()) { ESP_LOGCONFIG(TAG, " Run Duration: %u ms", *this->run_duration_); diff --git a/script/clang-format b/script/clang-format index d5792c783e..89a5acd746 100755 --- a/script/clang-format +++ b/script/clang-format @@ -13,7 +13,7 @@ import threading import click sys.path.append(os.path.dirname(__file__)) -from helpers import basepath, get_output, walk_files, filter_changed +from helpers import basepath, get_output, git_ls_files, filter_changed is_py2 = sys.version[0] == '2' @@ -83,7 +83,7 @@ def main(): return 1 files = [] - for path in walk_files(basepath): + for path in git_ls_files(): filetypes = ('.cpp', '.h', '.tcc') ext = os.path.splitext(path)[1] if ext in filetypes: diff --git a/script/clang-tidy b/script/clang-tidy index 124008a873..39df87df22 100755 --- a/script/clang-tidy +++ b/script/clang-tidy @@ -18,7 +18,7 @@ import threading sys.path.append(os.path.dirname(__file__)) from helpers import basepath, shlex_quote, get_output, build_compile_commands, \ - build_all_include, temp_header_file, walk_files, filter_changed + build_all_include, temp_header_file, git_ls_files, filter_changed is_py2 = sys.version[0] == '2' @@ -100,7 +100,7 @@ def main(): build_compile_commands() files = [] - for path in walk_files(basepath): + for path in git_ls_files(): filetypes = ('.cpp',) ext = os.path.splitext(path)[1] if ext in filetypes: diff --git a/script/helpers.py b/script/helpers.py index 8b37dce570..243dfde49e 100644 --- a/script/helpers.py +++ b/script/helpers.py @@ -126,3 +126,13 @@ def filter_changed(files): for c in files: print(" {}".format(c)) return files + + +def git_ls_files(): + command = ['git', 'ls-files', '-s'] + proc = subprocess.Popen(command, stdout=subprocess.PIPE) + output, err = proc.communicate() + lines = [x.split() for x in output.decode('utf-8').splitlines()] + return { + s[3].strip(): int(s[0]) for s in lines + } diff --git a/script/lint-python b/script/lint-python index cb702bf362..415efa0ebf 100755 --- a/script/lint-python +++ b/script/lint-python @@ -9,7 +9,7 @@ import re import sys sys.path.append(os.path.dirname(__file__)) -from helpers import basepath, get_output, walk_files, filter_changed +from helpers import get_output, git_ls_files, filter_changed def main(): @@ -21,10 +21,10 @@ def main(): args = parser.parse_args() files = [] - for path in walk_files(basepath): + for path in git_ls_files(): filetypes = ('.py',) ext = os.path.splitext(path)[1] - if ext in filetypes: + if ext in filetypes and path.startswith('esphome'): path = os.path.relpath(path, os.getcwd()) files.append(path) # Match against re @@ -35,6 +35,8 @@ def main(): files = filter_changed(files) files.sort() + if not files: + sys.exit(0) errors = collections.defaultdict(list) cmd = ['flake8'] + files From 16c2929bb401d4665155354751ff148751f16980 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Fri, 24 May 2019 19:32:59 +0200 Subject: [PATCH 06/30] Fix parse_float accepting invalid input --- esphome/core/helpers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/core/helpers.cpp b/esphome/core/helpers.cpp index 769a7825da..c65ca919ba 100644 --- a/esphome/core/helpers.cpp +++ b/esphome/core/helpers.cpp @@ -257,7 +257,7 @@ std::string to_string(long double val) { optional parse_float(const std::string &str) { char *end; float value = ::strtof(str.c_str(), &end); - if (end == nullptr) + if (end == nullptr || end != str.end().base()) return {}; return value; } From 12ff280d3be3a12d295ace8548154d91eb319bc2 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Fri, 24 May 2019 23:08:04 +0200 Subject: [PATCH 07/30] Lint --- tests/custom.h | 24 ++++++------------------ tests/livingroom32.cpp | 6 ++---- tests/livingroom8266.cpp | 4 +--- 3 files changed, 9 insertions(+), 25 deletions(-) diff --git a/tests/custom.h b/tests/custom.h index 0c30437b8c..7a5edbbf61 100644 --- a/tests/custom.h +++ b/tests/custom.h @@ -3,38 +3,26 @@ class CustomSensor : public Component, public Sensor { public: - void loop() override { - publish_state(42.0); - } + void loop() override { publish_state(42.0); } }; class CustomTextSensor : public Component, public TextSensor { public: - void loop() override { - publish_state("Hello World"); - } + void loop() override { publish_state("Hello World"); } }; class CustomBinarySensor : public Component, public BinarySensor { public: - void loop() override { - publish_state(false); - } + void loop() override { publish_state(false); } }; class CustomSwitch : public Switch { protected: - void write_state(bool state) override { - ESP_LOGD("custom_switch", "Setting %s", ONOFF(state)); - } + void write_state(bool state) override { ESP_LOGD("custom_switch", "Setting %s", ONOFF(state)); } }; class CustomComponent : public PollingComponent { public: - void setup() override { - ESP_LOGD("custom_component", "Setup"); - } - void update() override { - ESP_LOGD("custom_component", "Update"); - } + void setup() override { ESP_LOGD("custom_component", "Setup"); } + void update() override { ESP_LOGD("custom_component", "Update"); } }; diff --git a/tests/livingroom32.cpp b/tests/livingroom32.cpp index af4a713b6a..7005ec95e0 100644 --- a/tests/livingroom32.cpp +++ b/tests/livingroom32.cpp @@ -11,7 +11,7 @@ void setup() { 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 *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); @@ -23,6 +23,4 @@ void setup() { App.setup(); } -void loop() { - App.loop(); -} +void loop() { App.loop(); } diff --git a/tests/livingroom8266.cpp b/tests/livingroom8266.cpp index cd43395d75..ad017ce73b 100644 --- a/tests/livingroom8266.cpp +++ b/tests/livingroom8266.cpp @@ -29,6 +29,4 @@ void setup() { App.setup(); } -void loop() { - App.loop(); -} +void loop() { App.loop(); } From df50b95e5aa936d37c8f939316863ee083852671 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sat, 25 May 2019 09:02:18 +0200 Subject: [PATCH 08/30] Fix LEDC missing frequency/bit_depth Fixes https://github.com/esphome/issues/issues/338#issuecomment-495810954 --- esphome/components/ledc/output.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esphome/components/ledc/output.py b/esphome/components/ledc/output.py index e5cb073ca9..c507465ff9 100644 --- a/esphome/components/ledc/output.py +++ b/esphome/components/ledc/output.py @@ -71,3 +71,5 @@ def to_code(config): yield output.register_output(var, config) if CONF_CHANNEL in config: cg.add(var.set_channel(config[CONF_CHANNEL])) + cg.add(var.set_frequency(config[CONF_FREQUENCY])) + cg.add(var.set_bit_depth(config[CONF_BIT_DEPTH])) From 9ed06444e168bb754f81ba3cfc2dbd34fd48c7d2 Mon Sep 17 00:00:00 2001 From: Pavel Pletenev Date: Sun, 26 May 2019 11:28:46 +0300 Subject: [PATCH 09/30] Suggested fix for empty domain (#555) * Suggested fix for empty domain Signed-off-by: delphi * Added proposed changes Signed-off-by: delphi --- esphome/config.py | 4 +++- tests/test2.yaml | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/esphome/config.py b/esphome/config.py index ac22db37ad..debd261b41 100644 --- a/esphome/config.py +++ b/esphome/config.py @@ -450,7 +450,9 @@ def validate_config(config): result.remove_output_path([domain], domain) # Ensure conf is a list - if not isinstance(conf, list) and conf: + if not conf: + result[domain] = conf = [] + elif not isinstance(conf, list): result[domain] = conf = [conf] for i, p_config in enumerate(conf): diff --git a/tests/test2.yaml b/tests/test2.yaml index 2b7ddebf3c..e3a9b0da85 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -238,3 +238,6 @@ interval: interval: 5s then: - logger.log: "Interval Run" + +display: + From d8963ea25a148ee4ca5aabce00d8224cf7b3d236 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sun, 26 May 2019 10:31:12 +0200 Subject: [PATCH 10/30] Fix Switch Condition Fixes https://github.com/esphome/issues/issues/322 --- esphome/components/switch/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/switch/__init__.py b/esphome/components/switch/__init__.py index 6425a364a1..3870631e13 100644 --- a/esphome/components/switch/__init__.py +++ b/esphome/components/switch/__init__.py @@ -83,13 +83,13 @@ def switch_toggle_to_code(config, action_id, template_arg, args): @automation.register_condition('switch.is_on', SwitchCondition, SWITCH_ACTION_SCHEMA) def switch_is_on_to_code(config, condition_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) - yield cg.new_Pvariable(condition_id, template_arg, paren) + yield cg.new_Pvariable(condition_id, template_arg, paren, True) @automation.register_condition('switch.is_off', SwitchCondition, SWITCH_ACTION_SCHEMA) def switch_is_off_to_code(config, condition_id, template_arg, args): paren = yield cg.get_variable(config[CONF_ID]) - yield cg.new_Pvariable(condition_id, template_arg, paren) + yield cg.new_Pvariable(condition_id, template_arg, paren, False) @coroutine_with_priority(100.0) From 510e53de708f75e7866fc87965d4e565f4a8a7e1 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sun, 26 May 2019 10:31:29 +0200 Subject: [PATCH 11/30] Add core version 2.5.2 --- esphome/components/pulse_width/sensor.py | 2 +- esphome/components/status/status_binary_sensor.cpp | 1 + esphome/const.py | 1 + esphome/core_config.py | 5 +++-- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/esphome/components/pulse_width/sensor.py b/esphome/components/pulse_width/sensor.py index 0227adffce..8328da2ac0 100644 --- a/esphome/components/pulse_width/sensor.py +++ b/esphome/components/pulse_width/sensor.py @@ -10,7 +10,7 @@ PulseWidthSensor = pulse_width_ns.class_('PulseWidthSensor', sensor.Sensor, cg.P CONFIG_SCHEMA = sensor.sensor_schema(UNIT_SECOND, ICON_TIMER, 3).extend({ cv.GenerateID(): cv.declare_id(PulseWidthSensor), - cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pullup_pin_schema, + cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt), }).extend(cv.polling_component_schema('60s')) diff --git a/esphome/components/status/status_binary_sensor.cpp b/esphome/components/status/status_binary_sensor.cpp index d004c70b95..7fbeb8c171 100644 --- a/esphome/components/status/status_binary_sensor.cpp +++ b/esphome/components/status/status_binary_sensor.cpp @@ -1,6 +1,7 @@ #include "status_binary_sensor.h" #include "esphome/core/log.h" #include "esphome/core/util.h" +#include "esphome/core/defines.h" #ifdef USE_MQTT #include "esphome/components/mqtt/mqtt_client.h" diff --git a/esphome/const.py b/esphome/const.py index d58ed406c7..d3da18448e 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -19,6 +19,7 @@ ARDUINO_VERSION_ESP8266_DEV = 'https://github.com/platformio/platform-espressif8 '/stage' ARDUINO_VERSION_ESP8266_2_5_0 = 'espressif8266@2.0.0' ARDUINO_VERSION_ESP8266_2_5_1 = 'espressif8266@2.1.0' +ARDUINO_VERSION_ESP8266_2_5_2 = 'espressif8266@2.2.0' ARDUINO_VERSION_ESP8266_2_3_0 = 'espressif8266@1.5.0' SOURCE_FILE_EXTENSIONS = {'.cpp', '.hpp', '.h', '.c', '.tcc', '.ino'} HEADER_FILE_EXTENSIONS = {'.h', '.hpp', '.tcc'} diff --git a/esphome/core_config.py b/esphome/core_config.py index a2663b8db8..d02b7c98de 100644 --- a/esphome/core_config.py +++ b/esphome/core_config.py @@ -11,7 +11,7 @@ from esphome.const import ARDUINO_VERSION_ESP32_DEV, ARDUINO_VERSION_ESP8266_DEV CONF_NAME, CONF_ON_BOOT, CONF_ON_LOOP, CONF_ON_SHUTDOWN, CONF_PLATFORM, \ CONF_PLATFORMIO_OPTIONS, CONF_PRIORITY, CONF_TRIGGER_ID, \ CONF_ESP8266_RESTORE_FROM_FLASH, __version__, ARDUINO_VERSION_ESP8266_2_3_0, \ - ARDUINO_VERSION_ESP8266_2_5_0, ARDUINO_VERSION_ESP8266_2_5_1 + ARDUINO_VERSION_ESP8266_2_5_0, ARDUINO_VERSION_ESP8266_2_5_1, ARDUINO_VERSION_ESP8266_2_5_2 from esphome.core import CORE, coroutine_with_priority from esphome.pins import ESP8266_FLASH_SIZES, ESP8266_LD_SCRIPTS @@ -44,6 +44,7 @@ def validate_board(value): validate_platform = cv.one_of('ESP32', 'ESP8266', upper=True) PLATFORMIO_ESP8266_LUT = { + '2.5.2': 'espressif8266@2.2.0', '2.5.1': 'espressif8266@2.1.0', '2.5.0': 'espressif8266@2.0.1', '2.4.2': 'espressif8266@1.8.0', @@ -193,7 +194,7 @@ def to_code(config): 'espressif8266@1.6.0'): ld_script = ld_scripts[0] elif CORE.arduino_version in (ARDUINO_VERSION_ESP8266_DEV, ARDUINO_VERSION_ESP8266_2_5_0, - ARDUINO_VERSION_ESP8266_2_5_1): + ARDUINO_VERSION_ESP8266_2_5_1, ARDUINO_VERSION_ESP8266_2_5_2): ld_script = ld_scripts[1] if ld_script is not None: From dac624231f4cc20315ce10ff00316ecdc6bc4fa4 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 27 May 2019 09:58:55 +0200 Subject: [PATCH 12/30] Fix custom output, add test Fixes https://github.com/esphome/issues/issues/346 --- esphome/config_validation.py | 1 + tests/custom.h | 10 ++++++++++ tests/test3.yaml | 16 ++++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/esphome/config_validation.py b/esphome/config_validation.py index ad0d90fcd3..466c540c81 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1092,6 +1092,7 @@ def typed_schema(schemas, **kwargs): key_v = key_validator(value.pop(key)) value = schemas[key_v](value) value[key] = key_v + return value return validator diff --git a/tests/custom.h b/tests/custom.h index 7a5edbbf61..b392819efb 100644 --- a/tests/custom.h +++ b/tests/custom.h @@ -26,3 +26,13 @@ class CustomComponent : public PollingComponent { void setup() override { ESP_LOGD("custom_component", "Setup"); } void update() override { ESP_LOGD("custom_component", "Update"); } }; + +class CustomBinaryOutput : public BinaryOutput, public Component { + protected: + void write_state(bool state) override { ESP_LOGD("custom_output", "Setting %s", ONOFF(state)); } +}; + +class CustomFloatOutput : public FloatOutput, public Component { + protected: + void write_state(float state) override { ESP_LOGD("custom_output", "Setting %f", state); } +}; diff --git a/tests/test3.yaml b/tests/test3.yaml index 7fcc7e1e60..9b41efd7fb 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -377,6 +377,22 @@ output: id: out pin: D3 frequency: 50Hz + - platform: custom + type: binary + lambda: |- + auto s = new CustomBinaryOutput(); + App.register_component(s); + return {s}; + outputs: + - id: custom_binary + - platform: custom + type: float + lambda: |- + auto s = new CustomFloatOutput(); + App.register_component(s); + return {s}; + outputs: + - id: custom_float mcp23017: id: mcp From 72a80f559a0c1ef905fd4db9e164ebedbd3b1a2d Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 27 May 2019 13:06:45 +0200 Subject: [PATCH 13/30] Use relative include, add check --- esphome/components/adc/adc_sensor.cpp | 2 +- esphome/components/ads1115/sensor.py | 3 +- .../binary_sensor/binary_sensor.cpp | 2 +- esphome/components/binary_sensor/filter.cpp | 4 +- esphome/components/dht/dht.cpp | 2 +- .../components/esp32_touch/binary_sensor.py | 3 +- .../gpio/binary_sensor/gpio_binary_sensor.cpp | 2 +- .../components/gpio/switch/gpio_switch.cpp | 2 +- esphome/components/logger/logger.cpp | 2 +- esphome/components/my9231/output.py | 2 +- esphome/components/ota/ota_component.cpp | 2 +- esphome/components/sensor/filter.cpp | 4 +- esphome/components/sensor/sensor.cpp | 2 +- esphome/components/switch/switch.cpp | 2 +- esphome/components/uptime/uptime_sensor.cpp | 2 +- esphome/components/wifi/wifi_component.cpp | 2 +- .../components/wifi/wifi_component_esp32.cpp | 2 +- .../wifi/wifi_component_esp8266.cpp | 2 +- script/ci-custom.py | 55 +++++++++++++++---- 19 files changed, 65 insertions(+), 32 deletions(-) diff --git a/esphome/components/adc/adc_sensor.cpp b/esphome/components/adc/adc_sensor.cpp index d0cea53d49..2c448d0392 100644 --- a/esphome/components/adc/adc_sensor.cpp +++ b/esphome/components/adc/adc_sensor.cpp @@ -1,4 +1,4 @@ -#include "esphome/components/adc/adc_sensor.h" +#include "adc_sensor.h" #include "esphome/core/log.h" #ifdef USE_ADC_SENSOR_VCC diff --git a/esphome/components/ads1115/sensor.py b/esphome/components/ads1115/sensor.py index 2fe9b6fa86..204ccb99d7 100644 --- a/esphome/components/ads1115/sensor.py +++ b/esphome/components/ads1115/sensor.py @@ -1,10 +1,9 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, voltage_sampler -from esphome.components.ads1115 import ADS1115Component 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 +from . import ads1115_ns, ADS1115Component DEPENDENCIES = ['ads1115'] diff --git a/esphome/components/binary_sensor/binary_sensor.cpp b/esphome/components/binary_sensor/binary_sensor.cpp index 8c982f938d..27c835d38c 100644 --- a/esphome/components/binary_sensor/binary_sensor.cpp +++ b/esphome/components/binary_sensor/binary_sensor.cpp @@ -1,4 +1,4 @@ -#include "esphome/components/binary_sensor/binary_sensor.h" +#include "binary_sensor.h" #include "esphome/core/log.h" namespace esphome { diff --git a/esphome/components/binary_sensor/filter.cpp b/esphome/components/binary_sensor/filter.cpp index 39b88f867c..b7ac2c4a79 100644 --- a/esphome/components/binary_sensor/filter.cpp +++ b/esphome/components/binary_sensor/filter.cpp @@ -1,5 +1,5 @@ -#include "esphome/components/binary_sensor/filter.h" -#include "esphome/components/binary_sensor/binary_sensor.h" +#include "filter.h" +#include "binary_sensor.h" namespace esphome { diff --git a/esphome/components/dht/dht.cpp b/esphome/components/dht/dht.cpp index 79732bb269..24a017e478 100644 --- a/esphome/components/dht/dht.cpp +++ b/esphome/components/dht/dht.cpp @@ -1,4 +1,4 @@ -#include "esphome/components/dht/dht.h" +#include "dht.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" diff --git a/esphome/components/esp32_touch/binary_sensor.py b/esphome/components/esp32_touch/binary_sensor.py index 94748e53e8..a72ca5796f 100644 --- a/esphome/components/esp32_touch/binary_sensor.py +++ b/esphome/components/esp32_touch/binary_sensor.py @@ -1,10 +1,9 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import binary_sensor -from esphome.components.esp32_touch import ESP32TouchComponent from esphome.const import CONF_NAME, CONF_PIN, CONF_THRESHOLD, ESP_PLATFORM_ESP32, CONF_ID from esphome.pins import validate_gpio_pin -from . import esp32_touch_ns +from . import esp32_touch_ns, ESP32TouchComponent ESP_PLATFORMS = [ESP_PLATFORM_ESP32] DEPENDENCIES = ['esp32_touch'] diff --git a/esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp b/esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp index dff3609ce2..f95778af4c 100644 --- a/esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp +++ b/esphome/components/gpio/binary_sensor/gpio_binary_sensor.cpp @@ -1,4 +1,4 @@ -#include "esphome/components/gpio/binary_sensor/gpio_binary_sensor.h" +#include "gpio_binary_sensor.h" #include "esphome/core/log.h" namespace esphome { diff --git a/esphome/components/gpio/switch/gpio_switch.cpp b/esphome/components/gpio/switch/gpio_switch.cpp index 22139d6b9c..d22a74847e 100644 --- a/esphome/components/gpio/switch/gpio_switch.cpp +++ b/esphome/components/gpio/switch/gpio_switch.cpp @@ -1,4 +1,4 @@ -#include "esphome/components/gpio/switch/gpio_switch.h" +#include "gpio_switch.h" #include "esphome/core/log.h" namespace esphome { diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index cd71049f15..78f09989e4 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -1,4 +1,4 @@ -#include "esphome/components/logger/logger.h" +#include "logger.h" #ifdef ARDUINO_ARCH_ESP32 #include diff --git a/esphome/components/my9231/output.py b/esphome/components/my9231/output.py index 9acc8bdcd6..c69649fd5e 100644 --- a/esphome/components/my9231/output.py +++ b/esphome/components/my9231/output.py @@ -1,8 +1,8 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import output -from esphome.components.my9231 import MY9231OutputComponent from esphome.const import CONF_CHANNEL, CONF_ID +from . import MY9231OutputComponent DEPENDENCIES = ['my9231'] diff --git a/esphome/components/ota/ota_component.cpp b/esphome/components/ota/ota_component.cpp index ee2d67c85e..d37a7a0c6a 100644 --- a/esphome/components/ota/ota_component.cpp +++ b/esphome/components/ota/ota_component.cpp @@ -1,4 +1,4 @@ -#include "esphome/components/ota/ota_component.h" +#include "ota_component.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" diff --git a/esphome/components/sensor/filter.cpp b/esphome/components/sensor/filter.cpp index 4923a1c09b..306607dfda 100644 --- a/esphome/components/sensor/filter.cpp +++ b/esphome/components/sensor/filter.cpp @@ -1,5 +1,5 @@ -#include "esphome/components/sensor/filter.h" -#include "esphome/components/sensor/sensor.h" +#include "filter.h" +#include "sensor.h" #include "esphome/core/log.h" namespace esphome { diff --git a/esphome/components/sensor/sensor.cpp b/esphome/components/sensor/sensor.cpp index ca6f4c23bb..e12e55e320 100644 --- a/esphome/components/sensor/sensor.cpp +++ b/esphome/components/sensor/sensor.cpp @@ -1,4 +1,4 @@ -#include "esphome/components/sensor/sensor.h" +#include "sensor.h" #include "esphome/core/log.h" namespace esphome { diff --git a/esphome/components/switch/switch.cpp b/esphome/components/switch/switch.cpp index d2279e6ad6..a6a25dd9dc 100644 --- a/esphome/components/switch/switch.cpp +++ b/esphome/components/switch/switch.cpp @@ -1,4 +1,4 @@ -#include "esphome/components/switch/switch.h" +#include "switch.h" #include "esphome/core/log.h" namespace esphome { diff --git a/esphome/components/uptime/uptime_sensor.cpp b/esphome/components/uptime/uptime_sensor.cpp index a66ca0636f..f047724768 100644 --- a/esphome/components/uptime/uptime_sensor.cpp +++ b/esphome/components/uptime/uptime_sensor.cpp @@ -1,4 +1,4 @@ -#include "esphome/components/uptime/uptime_sensor.h" +#include "uptime_sensor.h" #include "esphome/core/log.h" #include "esphome/core/helpers.h" diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 35735e5f08..882d45f793 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -1,4 +1,4 @@ -#include "esphome/components/wifi/wifi_component.h" +#include "wifi_component.h" #ifdef ARDUINO_ARCH_ESP32 #include diff --git a/esphome/components/wifi/wifi_component_esp32.cpp b/esphome/components/wifi/wifi_component_esp32.cpp index f50acee614..6b118cf0ae 100644 --- a/esphome/components/wifi/wifi_component_esp32.cpp +++ b/esphome/components/wifi/wifi_component_esp32.cpp @@ -1,4 +1,4 @@ -#include "esphome/components/wifi/wifi_component.h" +#include "wifi_component.h" #ifdef ARDUINO_ARCH_ESP32 diff --git a/esphome/components/wifi/wifi_component_esp8266.cpp b/esphome/components/wifi/wifi_component_esp8266.cpp index a2c9ac2551..11981b801b 100644 --- a/esphome/components/wifi/wifi_component_esp8266.cpp +++ b/esphome/components/wifi/wifi_component_esp8266.cpp @@ -1,4 +1,4 @@ -#include "esphome/components/wifi/wifi_component.h" +#include "wifi_component.h" #ifdef ARDUINO_ARCH_ESP8266 diff --git a/script/ci-custom.py b/script/ci-custom.py index 6aff5717cf..27b45dbe27 100755 --- a/script/ci-custom.py +++ b/script/ci-custom.py @@ -86,9 +86,12 @@ def lint_content_find_check(find, **kwargs): decor = lint_content_check(**kwargs) def decorator(func): - def new_func(content): - for line, col in find_all(content, find): - err = func() + def new_func(fname, content): + find_ = find + if callable(find): + find_ = find(fname, content) + for line, col in find_all(content, find_): + err = func(fname) return "{err} See line {line}:{col}.".format(err=err, line=line+1, col=col+1) return decor(new_func) return decorator @@ -123,31 +126,63 @@ def lint_executable_bit(fname): 'esphome/dashboard/static/ace.js', 'esphome/dashboard/static/ext-searchbox.js', 'script/.neopixelbus.patch', ]) -def lint_tabs(): +def lint_tabs(fname): return "File contains tab character. Please convert tabs to spaces." @lint_content_find_check('\r') -def lint_newline(): +def lint_newline(fname): return "File contains windows newline. Please set your editor to unix newline mode." @lint_content_check() -def lint_end_newline(content): +def lint_end_newline(fname, content): if content and not content.endswith('\n'): return "File does not end with a newline, please add an empty line at the end of the file." return None +def relative_cpp_search_text(fname, content): + parts = fname.split('/') + integration = parts[2] + return '#include "esphome/components/{}'.format(integration) + + +@lint_content_find_check(relative_cpp_search_text, include=['esphome/components/*.cpp']) +def lint_relative_cpp_import(fname): + return ("Component contains absolute import - Components must always use " + "relative imports.\n" + "Change:\n" + ' #include "esphome/components/abc/abc.h"\n' + 'to:\n' + ' #include "abc.h"\n\n') + + +def relative_py_search_text(fname, content): + parts = fname.split('/') + integration = parts[2] + return 'esphome.components.{}'.format(integration) + + +@lint_content_find_check(relative_py_search_text, include=['esphome/components/*.py']) +def lint_relative_py_import(fname): + return ("Component contains absolute import - Components must always use " + "relative imports within the integration.\n" + "Change:\n" + ' from esphome.components.abc import abc_ns"\n' + 'to:\n' + ' from . import abc_ns\n\n') + + @lint_content_find_check('"esphome.h"', include=cpp_include, exclude=['tests/custom.h']) -def lint_esphome_h(): +def lint_esphome_h(fname): return ("File contains reference to 'esphome.h' - This file is " "auto-generated and should only be used for *custom* " "components. Please replace with references to the direct files.") @lint_content_check(include=['*.h']) -def lint_pragma_once(content): +def lint_pragma_once(fname, content): if '#pragma once' not in content: return ("Header file contains no 'pragma once' header guard. Please add a " "'#pragma once' line at the top of the file.") @@ -171,7 +206,7 @@ def lint_pragma_once(content): 'esphome/core/log.h', 'tests/custom.h', ]) -def lint_log_in_header(): +def lint_log_in_header(fname): return ('Found reference to ESP_LOG in header file. Using ESP_LOG* in header files ' 'is currently not possible - please move the definition to a source file (.cpp)') @@ -202,7 +237,7 @@ for fname in files: except UnicodeDecodeError: add_errors(fname, "File is not readable as UTF-8. Please set your editor to UTF-8 mode.") continue - run_checks(LINT_CONTENT_CHECKS, fname, content) + run_checks(LINT_CONTENT_CHECKS, fname, fname, content) for f, errs in sorted(errors.items()): print("\033[0;32m************* File \033[1;32m{}\033[0m".format(f)) From 7b5c4359c67c10e851bd3cf57e73b16a27a423b9 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 27 May 2019 15:39:01 +0200 Subject: [PATCH 14/30] Update nextion.h --- esphome/components/nextion/nextion.h | 171 ++++++++++++++++++++++----- 1 file changed, 139 insertions(+), 32 deletions(-) diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index b5bc86ec62..2286e4a7d2 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -32,8 +32,14 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * @param component The component name. * @param format The printf-style format string. * @param ... The arguments to the format. - * Example: `it.set_component_text_printf("textview", "The uptime is: %.1f", id(uptime_sensor).state);` - * Component named `textview` has been changed to `The uptime is:` Then the value of `uptime_sensor`. + * + * Example: + * ```cpp + * it.set_component_text_printf("textview", "The uptime is: %.0f", id(uptime_sensor).state); + * ``` + * + * This will change the text on the component named `textview` to `The uptime is:` Then the value of `uptime_sensor`. + * with zero decimals of accuracy (whole number). * For example when `uptime_sensor` = 506, then, `The uptime is: 506` will be displayed. */ void set_component_text_printf(const char *component, const char *format, ...) __attribute__((format(printf, 3, 4))); @@ -41,16 +47,26 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * Set the integer value of a component * @param component The component name. * @param value The value to set. - * Example: `it.set_component_value("gauge", 50);` - * Component named `gauge` has changed the `val`to 50. + * + * Example: + * ```cpp + * it.set_component_value("gauge", 50); + * ``` + * + * This will change the property `value` of the component `gauge` to 50. */ void set_component_value(const char *component, int value); /** * Set the picture of an image component. * @param component The component name. * @param value The picture name. - * Example: `it.set_component_picture("pic", "4");` - * The picture component named `pic`, has changed the image which has the ID `4`, Which was set in the Nextion editor. + * + * Example: + * ```cpp + * it.set_component_picture("pic", "4"); + * ``` + * + * This will change the image of the component `pic` to the image with ID `4`. */ void set_component_picture(const char *component, const char *picture) { this->send_command_printf("%s.val=%s", component, picture); @@ -59,8 +75,13 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * Set the background color of a component. * @param component The component name. * @param color The color (as a string). - * Example: `it.set_component_background_color("button", "17013"); - * The background color of component named `button`, has been changed to blue. + * + * Example: + * ```cpp + * it.set_component_background_color("button", "17013"); + * ``` + * + * This will change the background color of the component `button` to blue. * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors. */ void set_component_background_color(const char *component, const char *color); @@ -68,27 +89,43 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * Set the pressed background color of a component. * @param component The component name. * @param color The color (as a string). - * Example: `it.set_component_pressed_background_color("button", "17013"); - * The pressed background color of the component named `button` has been changed to blue. So when the button is pressed, the background color will change to blue. - * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors + * + * Example: + * ```cpp + * it.set_component_pressed_background_color("button", "17013"); + * ``` + * + * This will change the pressed background color of the component `button` to blue. This is the background color that is shown when the + * component is pressed. + * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors. */ void set_component_pressed_background_color(const char *component, const char *color); /** * Set the font color of a component. * @param component The component name. * @param color The color (as a string). - * Example: `it.set_component_font_color("textview", "17013); - * The font color of component named `textview` has been changed to blue. - * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors + * + * Example: + * ```cpp + * it.set_component_font_color("textview", "17013"); + * ``` + * + * This will change the font color of the component `textview` to a blue color. + * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors. */ void set_component_font_color(const char *component, const char *color); /** * Set the pressed font color of a component. * @param component The component name. * @param color The color (as a string). - * Example: `it.set_component_pressed_font_color("button", "17013");` - * The pressed font color for component named button has been changed to blue. So when `button` is pressed, the font color will change to blue - * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors + * + * Example: + * ```cpp + * it.set_component_pressed_font_color("button", "17013"); + * ``` + * + * This will change the pressed font color of the component `button` to a blue color. + * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors. */ void set_component_pressed_font_color(const char *component, const char *color); /** @@ -96,15 +133,25 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * @param component The component name. * @param x The x coordinate. * @param y The y coordinate. - * Example: `it.set_component_coordinates("pic", 55, 100);` - * The component named `pic` has moved to the x coordinate `55` and y coordinate `100`. + * + * Example: + * ```cpp + * it.set_component_coordinates("pic", 55, 100); + * ``` + * + * This will move the position of the component `pic` to the x coordinate `55` and y coordinate `100`. */ void set_component_coordinates(const char *component, int x, int y); /** * Set the font id for a component. * @param component The component name. * @param font_id The ID of the font (number). - * Example: `it.set_component_font("textview", "3");` + * + * Example: + * ```cpp + * it.set_component_font("textview", "3"); + * ``` + * * Changes the font of the component named `textveiw`. Font IDs are set in the Nextion Editor. */ void set_component_font(const char *component, uint8_t font_id); @@ -119,35 +166,60 @@ class Nextion : public PollingComponent, public uart::UARTDevice { /** * Show the page with a given name. * @param page The name of the page. - * Example: `it.goto_page("main");` + * + * Example: + * ```cpp + * it.goto_page("main"); + * ``` + * * Switches to the page named `main`. Pages are named in the Nextion Editor. */ void goto_page(const char *page); /** * Hide a component. * @param component The component name. - * Example: `hide_component("button");` + * + * Example: + * ```cpp + * hide_component("button"); + * ``` + * * Hides the component named `button`. */ void hide_component(const char *component); /** * Show a component. * @param component The component name. - * Example: `show_component("button");` + * + * Example: + * ```cpp + * show_component("button"); + * ``` + * * Shows the component named `button`. */ void show_component(const char *component); /** * Enable touch for a component. * @param component The component name. - * Example: `enable_component_touch("button");` + * + * Example: + * ```cpp + * enable_component_touch("button"); + * ``` + * * Enables touch for component named `button`. */ void enable_component_touch(const char *component); /** * Disable touch for a component. * @param component The component name. - * Examlple: `disable_component_touch("button");` + * + * Example: + * ```cpp + * disable_component_touch("button"); + * ``` + * * Disables touch for component named `button`. */ void disable_component_touch(const char *component); @@ -163,7 +235,12 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * @param picture_id The picture id. * @param x1 The x coordinate. * @param y1 The y coordniate. - * Example: `display_picture(2, 15, 25);` + * + * Example: + * ```cpp + * display_picture(2, 15, 25); + * ``` + * * Displays the picture who has the id `2` at the x coordinates `15` and y coordinates `25`. */ void display_picture(int picture_id, int x_start, int y_start); @@ -174,7 +251,12 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * @param width The width to draw. * @param height The height to draw. * @param color The color to draw with (as a string). - * Example: `fill_area(50, 50, 100, 100, "17013");` + * + * Example: + * ```cpp + * fill_area(50, 50, 100, 100, "17013"); + * ``` + * * Fills an area that starts at x coordiante `50` and y coordinate `50` with a height of `100` and width of `100` with the color of blue. * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors */ @@ -186,7 +268,12 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * @param x2 The ending x coordinate. * @param y2 The ending y coordinate. * @param color The color to draw with (as a string). - * Example: `it.line(50, 50, 75, 75, "17013");` + * + * Example: + * ```cpp + * it.line(50, 50, 75, 75, "17013"); + * ``` + * * Makes a line that starts at x coordinate `50` and y coordinate `50` and ends at x coordinate `75` and y coordinate `75` with the color of blue. * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors. */ @@ -198,7 +285,12 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * @param width The width of the rectangle. * @param height The height of the rectangle. * @param color The color to draw with (as a string). - * Example: `it.rectangle(25, 35, 40, 50, "17013");` + * + * Example: + * ```cpp + * it.rectangle(25, 35, 40, 50, "17013"); + * ``` + * * Makes a outline of a rectangle that starts at x coordinate `25` and y coordinate `35` and has a width of `40` and a length of `50` with color of blue. * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors. */ @@ -217,7 +309,12 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * @param center_y The center y coordinate. * @param radius The circle radius. * @param color The color to draw with (as a string). - * Example: `it.filled_cricle(25, 25, 10, "17013");` + * + * Example: + * ```cpp + * it.filled_cricle(25, 25, 10, "17013"); + * ``` + * * Makes a filled circle at the x cordinates `25` and y coordinate `25` with a radius of `10` with a color of blue. * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors. */ @@ -226,14 +323,24 @@ class Nextion : public PollingComponent, public uart::UARTDevice { /** Set the brightness of the backlight. * * @param brightness The brightness, from 0 to 100. - * Example: `it.set_backlight_brightness(30);` + * + * Example: + * ```cpp + * it.set_backlight_brightness(30); + * ``` + * * Changes the brightness of the display to 30%. */ void set_backlight_brightness(uint8_t brightness); /** * Set the touch sleep timeout of the display. * @param timeout Timeout in seconds. - * Example: `it.set_touch_sleep_timeout(30);` + * + * Example: + * ```cpp + * it.set_touch_sleep_timeout(30); + * ``` + * * After 30 seconds the display will go to sleep. Note: the display will only wakeup by a restart or by setting up `thup`. */ void set_touch_sleep_timeout(uint16_t timeout); From 0d9f5ef363f8c78b98a3d4f204e37149a85a7df5 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 27 May 2019 16:51:01 +0200 Subject: [PATCH 15/30] Update nextion.h --- esphome/components/nextion/nextion.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index 2286e4a7d2..68d412cb47 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -23,8 +23,13 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * Set the text of a component to a static string. * @param component The component name. * @param text The static text to set. - * Example: `it.set_component_text("textview", "Hello World!");` - * Component named `textview` `txt` value has been changed to `Hello World`. + * + * Example: + * ```cpp + * it.set_component_text("textview", "Hello World!"); + * ``` + * + * This will set the `txt` property of the component `textview` to `Hello World`. */ void set_component_text(const char *component, const char *text); /** From 72218171b3e7d572491bb587d33aba12eee4761c Mon Sep 17 00:00:00 2001 From: Brandon Davidson Date: Mon, 27 May 2019 08:21:01 -0700 Subject: [PATCH 16/30] Don't fall back to the global availability topic if it is empty (#553) Fixes esphome/issues#320 --- esphome/components/mqtt/mqtt_component.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index 13b48630fa..042e362f75 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -72,11 +72,13 @@ bool MQTTComponent::send_discovery_() { root["command_topic"] = this->get_command_topic_(); if (this->availability_ == nullptr) { - root["availability_topic"] = global_mqtt_client->get_availability().topic; - if (global_mqtt_client->get_availability().payload_available != "online") - root["payload_available"] = global_mqtt_client->get_availability().payload_available; - if (global_mqtt_client->get_availability().payload_not_available != "offline") - root["payload_not_available"] = global_mqtt_client->get_availability().payload_not_available; + if (!global_mqtt_client->get_availability().topic.empty()) { + root["availability_topic"] = global_mqtt_client->get_availability().topic; + if (global_mqtt_client->get_availability().payload_available != "online") + root["payload_available"] = global_mqtt_client->get_availability().payload_available; + if (global_mqtt_client->get_availability().payload_not_available != "offline") + root["payload_not_available"] = global_mqtt_client->get_availability().payload_not_available; + } } else if (!this->availability_->topic.empty()) { root["availability_topic"] = this->availability_->topic; if (this->availability_->payload_available != "online") From 28f2a7f99cb6551f3bb1273c95f4b5517b657af7 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 27 May 2019 19:12:43 +0200 Subject: [PATCH 17/30] Update nextion.h --- esphome/components/nextion/nextion.h | 45 +++++++++++++++++----------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/esphome/components/nextion/nextion.h b/esphome/components/nextion/nextion.h index 68d412cb47..92b41a88af 100644 --- a/esphome/components/nextion/nextion.h +++ b/esphome/components/nextion/nextion.h @@ -57,7 +57,7 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * ```cpp * it.set_component_value("gauge", 50); * ``` - * + * * This will change the property `value` of the component `gauge` to 50. */ void set_component_value(const char *component, int value); @@ -87,7 +87,8 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * ``` * * This will change the background color of the component `button` to blue. - * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors. + * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to + * Nextion HMI colors. */ void set_component_background_color(const char *component, const char *color); /** @@ -100,9 +101,10 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * it.set_component_pressed_background_color("button", "17013"); * ``` * - * This will change the pressed background color of the component `button` to blue. This is the background color that is shown when the - * component is pressed. - * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors. + * This will change the pressed background color of the component `button` to blue. This is the background color that + * is shown when the component is pressed. Use this [color + * picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI + * colors. */ void set_component_pressed_background_color(const char *component, const char *color); /** @@ -116,7 +118,8 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * ``` * * This will change the font color of the component `textview` to a blue color. - * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors. + * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to + * Nextion HMI colors. */ void set_component_font_color(const char *component, const char *color); /** @@ -124,13 +127,14 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * @param component The component name. * @param color The color (as a string). * - * Example: + * Example: * ```cpp * it.set_component_pressed_font_color("button", "17013"); * ``` * * This will change the pressed font color of the component `button` to a blue color. - * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors. + * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to + * Nextion HMI colors. */ void set_component_pressed_font_color(const char *component, const char *color); /** @@ -184,7 +188,7 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * Hide a component. * @param component The component name. * - * Example: + * Example: * ```cpp * hide_component("button"); * ``` @@ -262,8 +266,9 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * fill_area(50, 50, 100, 100, "17013"); * ``` * - * Fills an area that starts at x coordiante `50` and y coordinate `50` with a height of `100` and width of `100` with the color of blue. - * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors + * Fills an area that starts at x coordiante `50` and y coordinate `50` with a height of `100` and width of `100` with + * the color of blue. Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to + * convert color codes to Nextion HMI colors */ void fill_area(int x1, int y1, int width, int height, const char *color); /** @@ -279,8 +284,10 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * it.line(50, 50, 75, 75, "17013"); * ``` * - * Makes a line that starts at x coordinate `50` and y coordinate `50` and ends at x coordinate `75` and y coordinate `75` with the color of blue. - * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors. + * Makes a line that starts at x coordinate `50` and y coordinate `50` and ends at x coordinate `75` and y coordinate + * `75` with the color of blue. Use this [color + * picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI + * colors. */ void line(int x1, int y1, int x2, int y2, const char *color); /** @@ -296,8 +303,10 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * it.rectangle(25, 35, 40, 50, "17013"); * ``` * - * Makes a outline of a rectangle that starts at x coordinate `25` and y coordinate `35` and has a width of `40` and a length of `50` with color of blue. - * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors. + * Makes a outline of a rectangle that starts at x coordinate `25` and y coordinate `35` and has a width of `40` and a + * length of `50` with color of blue. Use this [color + * picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI + * colors. */ void rectangle(int x1, int y1, int width, int height, const char *color); /** @@ -321,7 +330,8 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * ``` * * Makes a filled circle at the x cordinates `25` and y coordinate `25` with a radius of `10` with a color of blue. - * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to Nextion HMI colors. + * Use this [color picker](https://nodtem66.github.io/nextion-hmi-color-convert/index.html) to convert color codes to + * Nextion HMI colors. */ void filled_circle(int center_x, int center_y, int radius, const char *color); @@ -346,7 +356,8 @@ class Nextion : public PollingComponent, public uart::UARTDevice { * it.set_touch_sleep_timeout(30); * ``` * - * After 30 seconds the display will go to sleep. Note: the display will only wakeup by a restart or by setting up `thup`. + * After 30 seconds the display will go to sleep. Note: the display will only wakeup by a restart or by setting up + * `thup`. */ void set_touch_sleep_timeout(uint16_t timeout); From 4bc306772590b6b6df5bf5e28ea356d17e8a272f Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 27 May 2019 19:22:16 +0200 Subject: [PATCH 18/30] Add light restore mode I don't want users to rely on setup priority. Ref https://github.com/esphome/esphome-docs/pull/248 --- esphome/components/light/__init__.py | 14 +++++++++++++- esphome/components/light/light_state.cpp | 23 ++++++++++++++++++----- esphome/components/light/light_state.h | 10 ++++++++++ 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/esphome/components/light/__init__.py b/esphome/components/light/__init__.py index e0ea8f246d..63aac69462 100644 --- a/esphome/components/light/__init__.py +++ b/esphome/components/light/__init__.py @@ -3,7 +3,7 @@ import esphome.config_validation as cv from esphome.components import mqtt, power_supply from esphome.const import CONF_COLOR_CORRECT, \ CONF_DEFAULT_TRANSITION_LENGTH, CONF_EFFECTS, CONF_GAMMA_CORRECT, CONF_ID, \ - CONF_INTERNAL, CONF_NAME, CONF_MQTT_ID, CONF_POWER_SUPPLY + CONF_INTERNAL, CONF_NAME, CONF_MQTT_ID, CONF_POWER_SUPPLY, CONF_RESTORE_MODE from esphome.core import coroutine, coroutine_with_priority from .automation import light_control_to_code # noqa from .effects import validate_effects, BINARY_EFFECTS, \ @@ -12,9 +12,20 @@ from .types import ( # noqa LightState, AddressableLightState, light_ns, LightOutput, AddressableLight) IS_PLATFORM_COMPONENT = True + +LightRestoreMode = light_ns.enum('LightRestoreMode') +RESTORE_MODES = { + 'RESTORE_DEFAULT_OFF': LightRestoreMode.LIGHT_RESTORE_DEFAULT_OFF, + 'RESTORE_DEFAULT_ON': LightRestoreMode.LIGHT_RESTORE_DEFAULT_ON, + 'ALWAYS_OFF': LightRestoreMode.LIGHT_ALWAYS_OFF, + 'ALWAYS_ON': LightRestoreMode.LIGHT_ALWAYS_ON, +} + LIGHT_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend({ cv.GenerateID(): cv.declare_id(LightState), cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTJSONLightComponent), + cv.Optional(CONF_RESTORE_MODE, default='restore_default_off'): + cv.enum(RESTORE_MODES, upper=True, space='_'), }) BINARY_LIGHT_SCHEMA = LIGHT_SCHEMA.extend({ @@ -41,6 +52,7 @@ ADDRESSABLE_LIGHT_SCHEMA = RGB_LIGHT_SCHEMA.extend({ @coroutine def setup_light_core_(light_var, output_var, config): + cg.add(light_var.set_restore_mode(config[CONF_RESTORE_MODE])) if CONF_INTERNAL in config: cg.add(light_var.set_internal(config[CONF_INTERNAL])) if CONF_DEFAULT_TRANSITION_LENGTH in config: diff --git a/esphome/components/light/light_state.cpp b/esphome/components/light/light_state.cpp index 7e61ea245a..cafced27fc 100644 --- a/esphome/components/light/light_state.cpp +++ b/esphome/components/light/light_state.cpp @@ -98,12 +98,25 @@ void LightState::setup() { effect->init_internal(this); } - this->rtc_ = global_preferences.make_preference(this->get_object_id_hash()); - LightStateRTCState recovered{}; - // Attempt to load from preferences, else fall back to default values from struct - this->rtc_.load(&recovered); - auto call = this->make_call(); + LightStateRTCState recovered{}; + switch (this->restore_mode_) { + case LIGHT_RESTORE_DEFAULT_OFF: + case LIGHT_RESTORE_DEFAULT_ON: + this->rtc_ = global_preferences.make_preference(this->get_object_id_hash()); + // Attempt to load from preferences, else fall back to default values from struct + if (!this->rtc_.load(&recovered)) { + recovered.state = this->restore_mode_ == LIGHT_RESTORE_DEFAULT_ON; + } + break; + case LIGHT_ALWAYS_OFF: + recovered.state = false; + break; + case LIGHT_ALWAYS_ON: + recovered.state = true; + break; + } + call.set_state(recovered.state); call.set_brightness_if_supported(recovered.brightness); call.set_red_if_supported(recovered.red); diff --git a/esphome/components/light/light_state.h b/esphome/components/light/light_state.h index 24e659ec8a..d67aa2c53d 100644 --- a/esphome/components/light/light_state.h +++ b/esphome/components/light/light_state.h @@ -160,6 +160,13 @@ class LightCall { bool save_{true}; }; +enum LightRestoreMode { + LIGHT_RESTORE_DEFAULT_OFF, + LIGHT_RESTORE_DEFAULT_ON, + LIGHT_ALWAYS_OFF, + LIGHT_ALWAYS_ON, +}; + /** This class represents the communication layer between the front-end MQTT layer and the * hardware output layer. */ @@ -249,6 +256,7 @@ class LightState : public Nameable, public Component { /// Set the gamma correction factor void set_gamma_correct(float gamma_correct); float get_gamma_correct() const { return this->gamma_correct_; } + void set_restore_mode(LightRestoreMode restore_mode) { restore_mode_ = restore_mode; } const std::vector &get_effects() const; @@ -292,6 +300,8 @@ class LightState : public Nameable, public Component { /// Object used to store the persisted values of the light. ESPPreferenceObject rtc_; + /// Restore mode of the light. + LightRestoreMode restore_mode_; /// Default transition length for all transitions in ms. uint32_t default_transition_length_{}; /// Value for storing the index of the currently active effect. 0 if no effect is active From 4323ca88c37d6bf786072f633806fd31e5d9f41b Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 27 May 2019 19:35:36 +0200 Subject: [PATCH 19/30] Add test for automation light effect --- esphome/components/light/effects.py | 6 +++--- esphome/const.py | 1 + tests/test1.yaml | 16 +++++++++++++++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/esphome/components/light/effects.py b/esphome/components/light/effects.py index 93dea2628d..72a2155a1b 100644 --- a/esphome/components/light/effects.py +++ b/esphome/components/light/effects.py @@ -4,7 +4,7 @@ from esphome import automation from esphome.const import CONF_NAME, CONF_LAMBDA, CONF_UPDATE_INTERVAL, CONF_TRANSITION_LENGTH, \ CONF_COLORS, CONF_STATE, CONF_DURATION, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, \ CONF_WHITE, CONF_ALPHA, CONF_INTENSITY, CONF_SPEED, CONF_WIDTH, CONF_NUM_LEDS, CONF_RANDOM, \ - CONF_THEN + CONF_THEN, CONF_SEQUENCE from esphome.util import Registry from .types import LambdaLightEffect, RandomLightEffect, StrobeLightEffect, \ StrobeLightEffectColor, LightColorValues, AddressableLightRef, AddressableLambdaLightEffect, \ @@ -63,11 +63,11 @@ def lambda_effect_to_code(config, effect_id): @register_effect('automation', AutomationLightEffect, "Automation", { - cv.Required(CONF_THEN): automation.validate_automation(single=True), + cv.Required(CONF_SEQUENCE): automation.validate_automation(single=True), }) def automation_effect_to_code(config, effect_id): var = yield cg.new_Pvariable(effect_id, config[CONF_NAME]) - yield automation.build_automation(var.get_trig(), [], config[CONF_THEN]) + yield automation.build_automation(var.get_trig(), [], config[CONF_SEQUENCE]) yield var diff --git a/esphome/const.py b/esphome/const.py index d3da18448e..d285105df4 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -366,6 +366,7 @@ CONF_SERVICE = 'service' CONF_SERVICES = 'services' CONF_SETUP_MODE = 'setup_mode' CONF_SETUP_PRIORITY = 'setup_priority' +CONF_SEQUENCE = 'sequence' CONF_SHUNT_RESISTANCE = 'shunt_resistance' CONF_SHUNT_VOLTAGE = 'shunt_voltage' CONF_SHUTDOWN_MESSAGE = 'shutdown_message' diff --git a/tests/test1.yaml b/tests/test1.yaml index b9177802fa..bb692d431e 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -17,7 +17,7 @@ esphome: ESP_LOGV("main", "ON LOOP!"); - light.addressable_set: id: addr1 - range_from: 1 + range_from: 0 range_to: 100 red: 100% green: !lambda 'return 255;' @@ -839,6 +839,20 @@ light: name: Flicker Effect With Custom Values update_interval: 16ms intensity: 5% + - automation: + name: Custom Effect + sequence: + - light.addressable_set: + id: addr1 + red: 100% + green: 100% + blue: 0% + - delay: 100ms + - light.addressable_set: + id: addr1 + red: 0% + green: 100% + blue: 0% - platform: fastled_spi id: addr2 chipset: WS2801 From b7daee533a3bec6c72c63da31bb4c3df9040f75f Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 27 May 2019 20:00:00 +0200 Subject: [PATCH 20/30] Lint --- esphome/components/light/effects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/light/effects.py b/esphome/components/light/effects.py index 72a2155a1b..a78165fb8a 100644 --- a/esphome/components/light/effects.py +++ b/esphome/components/light/effects.py @@ -4,7 +4,7 @@ from esphome import automation from esphome.const import CONF_NAME, CONF_LAMBDA, CONF_UPDATE_INTERVAL, CONF_TRANSITION_LENGTH, \ CONF_COLORS, CONF_STATE, CONF_DURATION, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, \ CONF_WHITE, CONF_ALPHA, CONF_INTENSITY, CONF_SPEED, CONF_WIDTH, CONF_NUM_LEDS, CONF_RANDOM, \ - CONF_THEN, CONF_SEQUENCE + CONF_SEQUENCE from esphome.util import Registry from .types import LambdaLightEffect, RandomLightEffect, StrobeLightEffect, \ StrobeLightEffectColor, LightColorValues, AddressableLightRef, AddressableLambdaLightEffect, \ From 3b48aa59113c729ec728935ca9f80d59ee8922a2 Mon Sep 17 00:00:00 2001 From: Teemu Mikkonen Date: Mon, 27 May 2019 21:00:32 +0300 Subject: [PATCH 21/30] Support for AM2320 temperature and humidity sensor (#554) Support for AM2320 temperature and humidity sensor Co-authored-by: Mikkonen Teemu --- esphome/components/am2320/__init__.py | 0 esphome/components/am2320/am2320.cpp | 107 ++++++++++++++++++++++++++ esphome/components/am2320/am2320.h | 29 +++++++ esphome/components/am2320/sensor.py | 30 ++++++++ tests/test3.yaml | 5 ++ 5 files changed, 171 insertions(+) create mode 100644 esphome/components/am2320/__init__.py create mode 100644 esphome/components/am2320/am2320.cpp create mode 100644 esphome/components/am2320/am2320.h create mode 100644 esphome/components/am2320/sensor.py diff --git a/esphome/components/am2320/__init__.py b/esphome/components/am2320/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/am2320/am2320.cpp b/esphome/components/am2320/am2320.cpp new file mode 100644 index 0000000000..59cb977fe8 --- /dev/null +++ b/esphome/components/am2320/am2320.cpp @@ -0,0 +1,107 @@ +// Implementation based on: +// - ESPEasy: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P034_DHT12.ino +// - DHT12_sensor_library: https://github.com/xreef/DHT12_sensor_library/blob/master/DHT12.cpp +// - Arduino - AM2320: https://github.com/EngDial/AM2320/blob/master/src/AM2320.cpp + +#include "am2320.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace am2320 { + +static const char *TAG = "am2320"; + +// ---=== Calc CRC16 ===--- +uint16_t crc_16(uint8_t *ptr, uint8_t length) { + uint16_t crc = 0xFFFF; + uint8_t i; + //------------------------------ + while (length--) { + crc ^= *ptr++; + for (i = 0; i < 8; i++) + if ((crc & 0x01) != 0) { + crc >>= 1; + crc ^= 0xA001; + } else + crc >>= 1; + } + return crc; +} + +void AM2320Component::update() { + uint8_t data[8]; + data[0] = 0; + data[1] = 4; + if (!this->read_data_(data)) { + this->status_set_warning(); + return; + } + + float temperature = (((data[4] & 0x7F) << 8) + data[5]) / 10.0; + temperature = (data[4] & 0x80) ? -temperature : temperature; + float humidity = ((data[2] << 8) + data[3]) / 10.0; + + ESP_LOGD(TAG, "Got temperature=%.1f°C humidity=%.1f%%", temperature, 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(); +} +void AM2320Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up AM2320..."); + uint8_t data[8]; + data[0] = 0; + data[1] = 4; + if (!this->read_data_(data)) { + this->mark_failed(); + return; + } +} +void AM2320Component::dump_config() { + ESP_LOGD(TAG, "AM2320:"); + LOG_I2C_DEVICE(this); + if (this->is_failed()) { + ESP_LOGE(TAG, "Communication with AM2320 failed!"); + } + LOG_SENSOR(" ", "Temperature", this->temperature_sensor_); + LOG_SENSOR(" ", "Humidity", this->humidity_sensor_); +} +float AM2320Component::get_setup_priority() const { return setup_priority::DATA; } + +bool AM2320Component::read_bytes_(uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion) { + if (!this->write_bytes(a_register, data, 2)) { + ESP_LOGW(TAG, "Writing bytes for AM2320 failed!"); + return false; + } + + if (conversion > 0) + delay(conversion); + return this->parent_->raw_receive(this->address_, data, len); +} + +bool AM2320Component::read_data_(uint8_t *data) { + // Wake up + this->write_bytes(0, data, 0); + + // Write instruction 3, 2 bytes, get 8 bytes back (2 preamble, 2 bytes temperature, 2 bytes humidity, 2 bytes CRC) + if (!this->read_bytes_(3, data, 8, 2)) { + ESP_LOGW(TAG, "Updating AM2320 failed!"); + return false; + } + + uint16_t checksum; + + checksum = data[7] << 8; + checksum += data[6]; + + if (crc_16(data, 6) != checksum) { + ESP_LOGW(TAG, "AM2320 Checksum invalid!"); + return false; + } + + return true; +} + +} // namespace am2320 +} // namespace esphome diff --git a/esphome/components/am2320/am2320.h b/esphome/components/am2320/am2320.h new file mode 100644 index 0000000000..33e1d30aa0 --- /dev/null +++ b/esphome/components/am2320/am2320.h @@ -0,0 +1,29 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/i2c/i2c.h" + +namespace esphome { +namespace am2320 { + +class AM2320Component : public PollingComponent, public i2c::I2CDevice { + public: + void setup() override; + void dump_config() override; + float get_setup_priority() const override; + void update() 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: + bool read_data_(uint8_t *data); + bool read_bytes_(uint8_t a_register, uint8_t *data, uint8_t len, uint32_t conversion = 0); + + sensor::Sensor *temperature_sensor_; + sensor::Sensor *humidity_sensor_; +}; + +} // namespace am2320 +} // namespace esphome diff --git a/esphome/components/am2320/sensor.py b/esphome/components/am2320/sensor.py new file mode 100644 index 0000000000..d62899663c --- /dev/null +++ b/esphome/components/am2320/sensor.py @@ -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'] + +am2320_ns = cg.esphome_ns.namespace('am2320') +AM2320Component = am2320_ns.class_('AM2320Component', cg.PollingComponent, i2c.I2CDevice) + +CONFIG_SCHEMA = cv.Schema({ + cv.GenerateID(): cv.declare_id(AM2320Component), + cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), + cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), +}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x5C)) + + +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)) diff --git a/tests/test3.yaml b/tests/test3.yaml index 9b41efd7fb..85dc714173 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -94,6 +94,11 @@ sensor: - platform: homeassistant entity_id: sensor.hello_world id: ha_hello_world + - platform: am2320 + temperature: + name: "Temperature" + humidity: + name: "Humidity" - platform: adc pin: VCC id: my_sensor From e62443933c92f3e34f9fe6d3284e7fa7047f1b7d Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Mon, 27 May 2019 15:17:12 -0300 Subject: [PATCH 22/30] Add TCL112 climate (#523) * Add TCL112 climate * fix default mode * Updates * Update * Update climate.py My mistake :( Co-authored-by: Otto Winter --- esphome/components/tcl112/__init__.py | 0 esphome/components/tcl112/climate.py | 36 ++++++ esphome/components/tcl112/tcl112.cpp | 152 ++++++++++++++++++++++++++ esphome/components/tcl112/tcl112.h | 40 +++++++ tests/test1.yaml | 9 ++ 5 files changed, 237 insertions(+) create mode 100644 esphome/components/tcl112/__init__.py create mode 100644 esphome/components/tcl112/climate.py create mode 100644 esphome/components/tcl112/tcl112.cpp create mode 100644 esphome/components/tcl112/tcl112.h diff --git a/esphome/components/tcl112/__init__.py b/esphome/components/tcl112/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/tcl112/climate.py b/esphome/components/tcl112/climate.py new file mode 100644 index 0000000000..50fef7b125 --- /dev/null +++ b/esphome/components/tcl112/climate.py @@ -0,0 +1,36 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import climate, remote_transmitter, sensor +from esphome.const import CONF_ID, CONF_SENSOR + +AUTO_LOAD = ['sensor'] + +tcl112_ns = cg.esphome_ns.namespace('tcl112') +Tcl112Climate = tcl112_ns.class_('Tcl112Climate', climate.Climate, cg.Component) + +CONF_TRANSMITTER_ID = 'transmitter_id' +CONF_SUPPORTS_HEAT = 'supports_heat' +CONF_SUPPORTS_COOL = 'supports_cool' + +CONFIG_SCHEMA = cv.All(climate.CLIMATE_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(Tcl112Climate), + cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(remote_transmitter.RemoteTransmitterComponent), + cv.Optional(CONF_SUPPORTS_COOL, default=True): cv.boolean, + cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean, + cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor), +}).extend(cv.COMPONENT_SCHEMA)) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield climate.register_climate(var, config) + + cg.add(var.set_supports_cool(config[CONF_SUPPORTS_COOL])) + cg.add(var.set_supports_heat(config[CONF_SUPPORTS_HEAT])) + if CONF_SENSOR in config: + sens = yield cg.get_variable(config[CONF_SENSOR]) + cg.add(var.set_sensor(sens)) + + transmitter = yield cg.get_variable(config[CONF_TRANSMITTER_ID]) + cg.add(var.set_transmitter(transmitter)) diff --git a/esphome/components/tcl112/tcl112.cpp b/esphome/components/tcl112/tcl112.cpp new file mode 100644 index 0000000000..cbe2f53402 --- /dev/null +++ b/esphome/components/tcl112/tcl112.cpp @@ -0,0 +1,152 @@ +#include "tcl112.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace tcl112 { + +static const char *TAG = "tcl112.climate"; + +const uint16_t TCL112_STATE_LENGTH = 14; +const uint16_t TCL112_BITS = TCL112_STATE_LENGTH * 8; + +const uint8_t TCL112_HEAT = 1; +const uint8_t TCL112_DRY = 2; +const uint8_t TCL112_COOL = 3; +const uint8_t TCL112_FAN = 7; +const uint8_t TCL112_AUTO = 8; + +const uint8_t TCL112_POWER_MASK = 0x04; + +const uint8_t TCL112_HALF_DEGREE = 0b00100000; +const float TCL112_TEMP_MAX = 31.0; +const float TCL112_TEMP_MIN = 16.0; + +const uint16_t TCL112_HEADER_MARK = 3000; +const uint16_t TCL112_HEADER_SPACE = 1650; +const uint16_t TCL112_BIT_MARK = 500; +const uint16_t TCL112_ONE_SPACE = 1050; +const uint16_t TCL112_ZERO_SPACE = 325; +const uint32_t TCL112_GAP = TCL112_HEADER_SPACE; + +climate::ClimateTraits Tcl112Climate::traits() { + auto traits = climate::ClimateTraits(); + traits.set_supports_current_temperature(this->sensor_ != nullptr); + 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_two_point_target_temperature(false); + traits.set_supports_away(false); + traits.set_visual_min_temperature(TCL112_TEMP_MIN); + traits.set_visual_max_temperature(TCL112_TEMP_MAX); + traits.set_visual_temperature_step(.5f); + return traits; +} + +void Tcl112Climate::setup() { + if (this->sensor_) { + this->sensor_->add_on_state_callback([this](float state) { + this->current_temperature = state; + // current temperature changed, publish state + this->publish_state(); + }); + this->current_temperature = this->sensor_->state; + } else + this->current_temperature = NAN; + // restore set points + auto restore = this->restore_state_(); + if (restore.has_value()) { + restore->apply(this); + } else { + // restore from defaults + this->mode = climate::CLIMATE_MODE_OFF; + this->target_temperature = 24; + } +} + +void Tcl112Climate::control(const climate::ClimateCall &call) { + if (call.get_mode().has_value()) + this->mode = *call.get_mode(); + if (call.get_target_temperature().has_value()) + this->target_temperature = *call.get_target_temperature(); + + this->transmit_state_(); + this->publish_state(); +} + +void Tcl112Climate::transmit_state_() { + uint8_t remote_state[TCL112_STATE_LENGTH] = {0}; + + // A known good state. (On, Cool, 24C) + remote_state[0] = 0x23; + remote_state[1] = 0xCB; + remote_state[2] = 0x26; + remote_state[3] = 0x01; + remote_state[5] = 0x24; + remote_state[6] = 0x03; + remote_state[7] = 0x07; + remote_state[8] = 0x40; + + // Set mode + switch (this->mode) { + case climate::CLIMATE_MODE_AUTO: + remote_state[6] &= 0xF0; + remote_state[6] |= TCL112_AUTO; + break; + case climate::CLIMATE_MODE_COOL: + remote_state[6] &= 0xF0; + remote_state[6] |= TCL112_COOL; + break; + case climate::CLIMATE_MODE_HEAT: + remote_state[6] &= 0xF0; + remote_state[6] |= TCL112_HEAT; + break; + case climate::CLIMATE_MODE_OFF: + default: + remote_state[5] &= ~TCL112_POWER_MASK; + break; + } + + // Set temperature + // Make sure we have desired temp in the correct range. + float safecelsius = std::max(this->target_temperature, TCL112_TEMP_MIN); + safecelsius = std::min(safecelsius, TCL112_TEMP_MAX); + // Convert to integer nr. of half degrees. + auto half_degrees = static_cast(safecelsius * 2); + if (half_degrees & 1) // Do we have a half degree celsius? + remote_state[12] |= TCL112_HALF_DEGREE; // Add 0.5 degrees + else + remote_state[12] &= ~TCL112_HALF_DEGREE; // Clear the half degree. + remote_state[7] &= 0xF0; // Clear temp bits. + remote_state[7] |= ((uint8_t) TCL112_TEMP_MAX - half_degrees / 2); + + // Calculate & set the checksum for the current internal state of the remote. + // Stored the checksum value in the last byte. + for (uint8_t checksum_byte = 0; checksum_byte < TCL112_STATE_LENGTH - 1; checksum_byte++) + remote_state[TCL112_STATE_LENGTH - 1] += remote_state[checksum_byte]; + + ESP_LOGV(TAG, "Sending tcl code: %u", remote_state[7]); + + auto transmit = this->transmitter_->transmit(); + auto data = transmit.get_data(); + + data->set_carrier_frequency(38000); + + // Header + data->mark(TCL112_HEADER_MARK); + data->space(TCL112_HEADER_SPACE); + // Data + for (uint8_t i : remote_state) + for (uint8_t j = 0; j < 8; j++) { + data->mark(TCL112_BIT_MARK); + bool bit = i & (1 << j); + data->space(bit ? TCL112_ONE_SPACE : TCL112_ZERO_SPACE); + } + // Footer + data->mark(TCL112_BIT_MARK); + data->space(TCL112_GAP); + + transmit.perform(); +} + +} // namespace tcl112 +} // namespace esphome diff --git a/esphome/components/tcl112/tcl112.h b/esphome/components/tcl112/tcl112.h new file mode 100644 index 0000000000..0b80dedbef --- /dev/null +++ b/esphome/components/tcl112/tcl112.h @@ -0,0 +1,40 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/automation.h" +#include "esphome/components/climate/climate.h" +#include "esphome/components/remote_base/remote_base.h" +#include "esphome/components/remote_transmitter/remote_transmitter.h" +#include "esphome/components/sensor/sensor.h" + +namespace esphome { +namespace tcl112 { + +class Tcl112Climate : public climate::Climate, public Component { + public: + void setup() override; + void set_transmitter(remote_transmitter::RemoteTransmitterComponent *transmitter) { + this->transmitter_ = transmitter; + } + void set_supports_cool(bool supports_cool) { this->supports_cool_ = supports_cool; } + void set_supports_heat(bool supports_heat) { this->supports_heat_ = supports_heat; } + void set_sensor(sensor::Sensor *sensor) { this->sensor_ = sensor; } + + protected: + /// Override control to change settings of the climate device. + void control(const climate::ClimateCall &call) override; + /// Return the traits of this controller. + climate::ClimateTraits traits() override; + + /// Transmit via IR the state of this climate controller. + void transmit_state_(); + + bool supports_cool_{true}; + bool supports_heat_{true}; + + remote_transmitter::RemoteTransmitterComponent *transmitter_; + sensor::Sensor *sensor_{nullptr}; +}; + +} // namespace tcl112 +} // namespace esphome diff --git a/tests/test1.yaml b/tests/test1.yaml index bb692d431e..ba8abc3ead 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -895,6 +895,15 @@ remote_transmitter: - pin: 32 carrier_duty_percent: 100% +climate: + - platform: tcl112 + name: TCL112 Climate With Sensor + supports_heat: True + supports_cool: True + sensor: my_sensor + - platform: tcl112 + name: TCL112 Climate + switch: - platform: gpio pin: GPIO25 From 85513476cec6c61d19b11c5e775926a41fca064a Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Mon, 27 May 2019 16:09:16 -0300 Subject: [PATCH 23/30] =?UTF-8?q?Add=20coolix=20climate=20=E2=9D=84=20?= =?UTF-8?q?=F0=9F=94=A5=20(#521)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Lint * add coolix climate ❄ 🔥 * Fixes * Reviewed * Fix for dev ClimateDevice was renamed to Climate * Remove stale method * Lint * Initialize target temperature, avoid NAN value * Use clamp and round value * Set to verbose message Not really relevant to the user * Remove constructor Name is now set in climate.register_climate - saves integrations from having to declare a constructor * Fix, add test Co-authored-by: Otto Winter --- esphome/components/coolix/__init__.py | 0 esphome/components/coolix/climate.py | 36 ++++++ esphome/components/coolix/coolix.cpp | 170 ++++++++++++++++++++++++++ esphome/components/coolix/coolix.h | 40 ++++++ tests/test1.yaml | 7 ++ 5 files changed, 253 insertions(+) create mode 100644 esphome/components/coolix/__init__.py create mode 100644 esphome/components/coolix/climate.py create mode 100644 esphome/components/coolix/coolix.cpp create mode 100644 esphome/components/coolix/coolix.h diff --git a/esphome/components/coolix/__init__.py b/esphome/components/coolix/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/coolix/climate.py b/esphome/components/coolix/climate.py new file mode 100644 index 0000000000..750a97d087 --- /dev/null +++ b/esphome/components/coolix/climate.py @@ -0,0 +1,36 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import climate, remote_transmitter, sensor +from esphome.const import CONF_ID, CONF_SENSOR + +AUTO_LOAD = ['sensor'] + +coolix_ns = cg.esphome_ns.namespace('coolix') +CoolixClimate = coolix_ns.class_('CoolixClimate', climate.Climate, cg.Component) + +CONF_TRANSMITTER_ID = 'transmitter_id' +CONF_SUPPORTS_HEAT = 'supports_heat' +CONF_SUPPORTS_COOL = 'supports_cool' + +CONFIG_SCHEMA = cv.All(climate.CLIMATE_SCHEMA.extend({ + cv.GenerateID(): cv.declare_id(CoolixClimate), + cv.GenerateID(CONF_TRANSMITTER_ID): cv.use_id(remote_transmitter.RemoteTransmitterComponent), + cv.Optional(CONF_SUPPORTS_COOL, default=True): cv.boolean, + cv.Optional(CONF_SUPPORTS_HEAT, default=True): cv.boolean, + cv.Optional(CONF_SENSOR): cv.use_id(sensor.Sensor), +}).extend(cv.COMPONENT_SCHEMA)) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield climate.register_climate(var, config) + + cg.add(var.set_supports_cool(config[CONF_SUPPORTS_COOL])) + cg.add(var.set_supports_heat(config[CONF_SUPPORTS_HEAT])) + if CONF_SENSOR in config: + sens = yield cg.get_variable(config[CONF_SENSOR]) + cg.add(var.set_sensor(sens)) + + transmitter = yield cg.get_variable(config[CONF_TRANSMITTER_ID]) + cg.add(var.set_transmitter(transmitter)) diff --git a/esphome/components/coolix/coolix.cpp b/esphome/components/coolix/coolix.cpp new file mode 100644 index 0000000000..ffc67adeb3 --- /dev/null +++ b/esphome/components/coolix/coolix.cpp @@ -0,0 +1,170 @@ +#include "coolix.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace coolix { + +static const char *TAG = "coolix.climate"; + +const uint32_t COOLIX_OFF = 0xB27BE0; +// 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 = 0b00; +const uint8_t COOLIX_DRY = 0b01; +const uint8_t COOLIX_AUTO = 0b10; +const uint8_t COOLIX_HEAT = 0b11; +const uint8_t COOLIX_FAN = 4; // Synthetic. +const uint32_t COOLIX_MODE_MASK = 0b000000000000000000001100; // 0xC + +// Temperature +const uint8_t COOLIX_TEMP_MIN = 17; // Celsius +const uint8_t COOLIX_TEMP_MAX = 30; // Celsius +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 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 +}; + +// Constants +// Pulse parms are *50-100 for the Mark and *50+100 for the space +// First MARK is the one after the long gap +// pulse parameters in usec +const uint16_t COOLIX_TICK = 560; // Approximately 21 cycles at 38kHz +const uint16_t COOLIX_BIT_MARK_TICKS = 1; +const uint16_t COOLIX_BIT_MARK = COOLIX_BIT_MARK_TICKS * COOLIX_TICK; +const uint16_t COOLIX_ONE_SPACE_TICKS = 3; +const uint16_t COOLIX_ONE_SPACE = COOLIX_ONE_SPACE_TICKS * COOLIX_TICK; +const uint16_t COOLIX_ZERO_SPACE_TICKS = 1; +const uint16_t COOLIX_ZERO_SPACE = COOLIX_ZERO_SPACE_TICKS * COOLIX_TICK; +const uint16_t COOLIX_HEADER_MARK_TICKS = 8; +const uint16_t COOLIX_HEADER_MARK = COOLIX_HEADER_MARK_TICKS * COOLIX_TICK; +const uint16_t COOLIX_HEADER_SPACE_TICKS = 8; +const uint16_t COOLIX_HEADER_SPACE = COOLIX_HEADER_SPACE_TICKS * COOLIX_TICK; +const uint16_t COOLIX_MIN_GAP_TICKS = COOLIX_HEADER_MARK_TICKS + COOLIX_ZERO_SPACE_TICKS; +const uint16_t COOLIX_MIN_GAP = COOLIX_MIN_GAP_TICKS * COOLIX_TICK; + +const uint16_t COOLIX_BITS = 24; + +climate::ClimateTraits CoolixClimate::traits() { + auto traits = climate::ClimateTraits(); + traits.set_supports_current_temperature(this->sensor_ != nullptr); + 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_two_point_target_temperature(false); + traits.set_supports_away(false); + traits.set_visual_min_temperature(17); + traits.set_visual_max_temperature(30); + traits.set_visual_temperature_step(1); + return traits; +} + +void CoolixClimate::setup() { + if (this->sensor_) { + this->sensor_->add_on_state_callback([this](float state) { + this->current_temperature = state; + // current temperature changed, publish state + this->publish_state(); + }); + this->current_temperature = this->sensor_->state; + } else + this->current_temperature = NAN; + // restore set points + auto restore = this->restore_state_(); + if (restore.has_value()) { + restore->apply(this); + } else { + // restore from defaults + this->mode = climate::CLIMATE_MODE_AUTO; + // initialize target temperature to some value so that it's not NAN + this->target_temperature = roundf(this->current_temperature); + } +} + +void CoolixClimate::control(const climate::ClimateCall &call) { + if (call.get_mode().has_value()) + this->mode = *call.get_mode(); + if (call.get_target_temperature().has_value()) + this->target_temperature = *call.get_target_temperature(); + + this->transmit_state_(); + this->publish_state(); +} + +void CoolixClimate::transmit_state_() { + uint32_t remote_state; + + switch (this->mode) { + case climate::CLIMATE_MODE_COOL: + remote_state = (COOLIX_DEFAULT_STATE & ~COOLIX_MODE_MASK) | (COOLIX_COOL << 2); + break; + case climate::CLIMATE_MODE_HEAT: + remote_state = (COOLIX_DEFAULT_STATE & ~COOLIX_MODE_MASK) | (COOLIX_HEAT << 2); + 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 (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: %u", remote_state); + + auto transmit = this->transmitter_->transmit(); + auto data = transmit.get_data(); + + data->set_carrier_frequency(38000); + uint16_t repeat = 1; + for (uint16_t r = 0; r <= repeat; r++) { + // Header + data->mark(COOLIX_HEADER_MARK); + data->space(COOLIX_HEADER_SPACE); + // Data + // Break data into byte segments, starting at the Most Significant + // Byte. Each byte then being sent normal, then followed inverted. + for (uint16_t i = 8; i <= COOLIX_BITS; i += 8) { + // Grab a bytes worth of data. + uint8_t segment = (remote_state >> (COOLIX_BITS - i)) & 0xFF; + // Normal + for (uint64_t mask = 1ULL << 7; mask; mask >>= 1) { + data->mark(COOLIX_BIT_MARK); + data->space((segment & mask) ? COOLIX_ONE_SPACE : COOLIX_ZERO_SPACE); + } + // Inverted + for (uint64_t mask = 1ULL << 7; mask; mask >>= 1) { + data->mark(COOLIX_BIT_MARK); + data->space(!(segment & mask) ? COOLIX_ONE_SPACE : COOLIX_ZERO_SPACE); + } + } + // Footer + data->mark(COOLIX_BIT_MARK); + data->space(COOLIX_MIN_GAP); // Pause before repeating + } + + transmit.perform(); +} + +} // namespace coolix +} // namespace esphome diff --git a/esphome/components/coolix/coolix.h b/esphome/components/coolix/coolix.h new file mode 100644 index 0000000000..0d52018d2a --- /dev/null +++ b/esphome/components/coolix/coolix.h @@ -0,0 +1,40 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/core/automation.h" +#include "esphome/components/climate/climate.h" +#include "esphome/components/remote_base/remote_base.h" +#include "esphome/components/remote_transmitter/remote_transmitter.h" +#include "esphome/components/sensor/sensor.h" + +namespace esphome { +namespace coolix { + +class CoolixClimate : public climate::Climate, public Component { + public: + void setup() override; + void set_transmitter(remote_transmitter::RemoteTransmitterComponent *transmitter) { + this->transmitter_ = transmitter; + } + void set_supports_cool(bool supports_cool) { this->supports_cool_ = supports_cool; } + void set_supports_heat(bool supports_heat) { this->supports_heat_ = supports_heat; } + void set_sensor(sensor::Sensor *sensor) { this->sensor_ = sensor; } + + protected: + /// Override control to change settings of the climate device. + void control(const climate::ClimateCall &call) override; + /// Return the traits of this controller. + climate::ClimateTraits traits() override; + + /// Transmit via IR the state of this climate controller. + void transmit_state_(); + + bool supports_cool_{true}; + bool supports_heat_{true}; + + remote_transmitter::RemoteTransmitterComponent *transmitter_; + sensor::Sensor *sensor_{nullptr}; +}; + +} // namespace coolix +} // namespace esphome diff --git a/tests/test1.yaml b/tests/test1.yaml index ba8abc3ead..8ff29d8c5b 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -903,6 +903,13 @@ climate: sensor: my_sensor - platform: tcl112 name: TCL112 Climate + - platform: coolix + name: Coolix Climate With Sensor + supports_heat: True + supports_cool: True + sensor: my_sensor + - platform: coolix + name: Coolix Climate switch: - platform: gpio From 6e7d25ed42175819c4ebe88fda064098449d2498 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Mon, 27 May 2019 21:51:43 +0200 Subject: [PATCH 24/30] Bump version to v1.14.0-dev --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index d285105df4..2ba461262c 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -2,7 +2,7 @@ """Constants used by esphome.""" MAJOR_VERSION = 1 -MINOR_VERSION = 13 +MINOR_VERSION = 14 PATCH_VERSION = '0-dev' __short_version__ = '{}.{}'.format(MAJOR_VERSION, MINOR_VERSION) __version__ = '{}.{}'.format(__short_version__, PATCH_VERSION) From f39d45955546a7b136aa019d348e014ba0f4b92e Mon Sep 17 00:00:00 2001 From: gitolicious Date: Tue, 28 May 2019 10:19:17 +0200 Subject: [PATCH 25/30] added link from dashboard to web server, if configured (#556) * added link from dashboard to web server, if configured * linter fixes * simplified integration lookup * included loaded_integration in storage json * included loaded_integration in storage json * fixed loaded_integrations plus linter changes * fixed comment: List Co-Authored-By: Otto Winter * return empty list Co-Authored-By: Otto Winter * convert to list Co-Authored-By: Otto Winter * default to empty list on missing loaded_integrations Co-Authored-By: Otto Winter * None check no longer needed Co-Authored-By: Otto Winter * None check no longer needed Co-Authored-By: Otto Winter * removed newline --- esphome/const.py | 1 + esphome/dashboard/dashboard.py | 6 ++++++ esphome/dashboard/templates/index.html | 3 +++ esphome/storage_json.py | 11 +++++++++-- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/esphome/const.py b/esphome/const.py index 2ba461262c..4c60151e72 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -201,6 +201,7 @@ CONF_LEVEL = 'level' CONF_LG = 'lg' CONF_LIBRARIES = 'libraries' CONF_LIGHT = 'light' +CONF_LOADED_INTEGRATIONS = 'loaded_integrations' CONF_LOCAL = 'local' CONF_LOGGER = 'logger' CONF_LOGS = 'logs' diff --git a/esphome/dashboard/dashboard.py b/esphome/dashboard/dashboard.py index 0d6a26c6ee..14b06a3c82 100644 --- a/esphome/dashboard/dashboard.py +++ b/esphome/dashboard/dashboard.py @@ -430,6 +430,12 @@ class DashboardEntry(object): def update_new(self): return const.__version__ + @property + def loaded_integrations(self): + if self.storage is None: + return [] + return self.storage.loaded_integrations + class MainRequestHandler(BaseHandler): @authenticated diff --git a/esphome/dashboard/templates/index.html b/esphome/dashboard/templates/index.html index b7df4509cc..b912bf8467 100644 --- a/esphome/dashboard/templates/index.html +++ b/esphome/dashboard/templates/index.html @@ -67,6 +67,9 @@
{{ escape(entry.name) }} + {% if 'web_server' in entry.loaded_integrations %} + launch + {% end %} more_vert

diff --git a/esphome/storage_json.py b/esphome/storage_json.py index 4d789b969c..b04f056f11 100644 --- a/esphome/storage_json.py +++ b/esphome/storage_json.py @@ -37,7 +37,7 @@ def trash_storage_path(base_path): # type: (str) -> str class StorageJSON(object): def __init__(self, storage_version, name, esphome_version, src_version, arduino_version, address, esp_platform, board, build_path, - firmware_bin_path): + firmware_bin_path, loaded_integrations): # Version of the storage JSON schema assert storage_version is None or isinstance(storage_version, int) self.storage_version = storage_version # type: int @@ -61,6 +61,9 @@ class StorageJSON(object): self.build_path = build_path # type: str # The absolute path to the firmware binary self.firmware_bin_path = firmware_bin_path # type: str + # A list of strings of names of loaded integrations + self.loaded_integrations = loaded_integrations # type: List[str] + self.loaded_integrations.sort() def as_dict(self): return { @@ -74,6 +77,7 @@ class StorageJSON(object): 'board': self.board, 'build_path': self.build_path, 'firmware_bin_path': self.firmware_bin_path, + 'loaded_integrations': self.loaded_integrations, } def to_json(self): @@ -97,6 +101,7 @@ class StorageJSON(object): board=esph.board, build_path=esph.build_path, firmware_bin_path=esph.firmware_bin, + loaded_integrations=list(esph.loaded_integrations), ) @staticmethod @@ -113,6 +118,7 @@ class StorageJSON(object): board=board, build_path=None, firmware_bin_path=None, + loaded_integrations=[], ) @staticmethod @@ -130,9 +136,10 @@ class StorageJSON(object): board = storage.get('board') build_path = storage.get('build_path') firmware_bin_path = storage.get('firmware_bin_path') + loaded_integrations = storage.get('loaded_integrations', []) return StorageJSON(storage_version, name, esphome_version, src_version, arduino_version, address, esp_platform, board, build_path, - firmware_bin_path) + firmware_bin_path, loaded_integrations) @staticmethod def load(path): # type: (str) -> Optional[StorageJSON] From a23ebead687ea84597a5f407164c2c77f040f36b Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Tue, 28 May 2019 10:23:15 +0200 Subject: [PATCH 26/30] Update gitlab CI script, add cpp lint --- .gitlab-ci.yml | 184 ++++++++++++++++++++--------------------- docker/Dockerfile.lint | 20 ++++- requirements_test.txt | 1 + script/lint-python | 29 ++++--- 4 files changed, 127 insertions(+), 107 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 94116bcee1..5b247e386f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,6 +3,8 @@ variables: DOCKER_DRIVER: overlay2 DOCKER_HOST: tcp://docker:2375/ + BASE_VERSION: '1.5.1' + TZ: UTC stages: - lint @@ -10,23 +12,20 @@ stages: - deploy .lint: &lint - image: esphome/esphome-base-amd64 + image: esphome/esphome-lint:latest stage: lint before_script: - - pip install -e . - - pip install flake8==3.6.0 pylint==1.9.4 pillow + - script/setup tags: - docker .test: &test - image: esphome/esphome-base-amd64 + image: esphome/esphome-base-amd64:${BASE_VERSION} stage: test before_script: - - pip install -e . + - script/setup tags: - docker - variables: - TZ: UTC .docker-base: &docker-base image: esphome/esphome-base-builder @@ -41,11 +40,11 @@ stages: - | if [[ "${IS_HASSIO}" == "YES" ]]; then - BUILD_FROM=esphome/esphome-hassio-base-${BUILD_ARCH}:1.5.1 + BUILD_FROM=esphome/esphome-hassio-base-${BUILD_ARCH}:${BASE_VERSION} BUILD_TO=esphome/esphome-hassio-${BUILD_ARCH} DOCKERFILE=docker/Dockerfile.hassio else - BUILD_FROM=esphome/esphome-base-${BUILD_ARCH}:1.5.1 + BUILD_FROM=esphome/esphome-base-${BUILD_ARCH}:${BASE_VERSION} if [[ "${BUILD_ARCH}" == "amd64" ]]; then BUILD_TO=esphome/esphome else @@ -94,15 +93,32 @@ stages: - docker stage: deploy -flake8: +lint-custom: <<: *lint script: - - flake8 esphome + - script/ci-custom.py -pylint: +lint-python: <<: *lint script: - - pylint esphome + - script/lint-python + +lint-tidy: + <<: *lint + script: + - pio init --ide atom + - | + if ! patch -R -p0 -s -f --dry-run