From 8a45dfac5ce7f411a09fcc8919e6432b71c58d3f Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Wed, 29 Jul 2020 20:45:47 +0200 Subject: [PATCH 01/12] Fix release.yml invalid bash syntax (#1226) --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ad1749de92..901dece924 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -226,7 +226,7 @@ jobs: cache_tag="beta" else cache_tag="latest" - end + fi # Set env variables so these values don't need to be calculated again echo "::set-env name=BUILD_FROM::${build_from}" From b7436c0b2212a7b5a2876cd0e78d87a2ecc6a515 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Wed, 29 Jul 2020 23:29:38 +0200 Subject: [PATCH 02/12] Bump ESP8266 Arduino framework from 2.7.2 to 2.7.3 (#1229) --- .github/workflows/ci-docker.yml | 2 +- .github/workflows/release-dev.yml | 2 +- .github/workflows/release.yml | 2 +- docker/Dockerfile | 2 +- docker/Dockerfile.dev | 2 +- docker/Dockerfile.lint | 2 +- esphome/const.py | 1 + esphome/core_config.py | 2 +- 8 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index b7aeeda471..22109138fe 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -25,7 +25,7 @@ jobs: - uses: actions/checkout@v2 - name: Set up env variables run: | - base_version="2.4.1" + base_version="2.5.0" if [[ "${{ matrix.build_type }}" == "hassio" ]]; then build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index 2a427d6c95..81b3cef124 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -190,7 +190,7 @@ jobs: echo "::set-env name=TAG::${TAG}" - name: Set up env variables run: | - base_version="2.4.1" + base_version="2.5.0" if [[ "${{ matrix.build_type }}" == "hassio" ]]; then build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 901dece924..41ffbfa87d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -210,7 +210,7 @@ jobs: echo "::set-env name=TAG::${TAG}" - name: Set up env variables run: | - base_version="2.4.1" + base_version="2.5.0" if [[ "${{ matrix.build_type }}" == "hassio" ]]; then build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" diff --git a/docker/Dockerfile b/docker/Dockerfile index 8845c9f58c..c843f2f098 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -ARG BUILD_FROM=esphome/esphome-base-amd64:2.4.1 +ARG BUILD_FROM=esphome/esphome-base-amd64:2.5.0 FROM ${BUILD_FROM} # First install requirements to leverage caching when requirements don't change diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index 032e29ae30..b3578e16b6 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -1,4 +1,4 @@ -FROM esphome/esphome-base-amd64:2.4.1 +FROM esphome/esphome-base-amd64:2.5.0 COPY . . diff --git a/docker/Dockerfile.lint b/docker/Dockerfile.lint index 2d34f300e3..9a29c5066f 100644 --- a/docker/Dockerfile.lint +++ b/docker/Dockerfile.lint @@ -1,4 +1,4 @@ -FROM esphome/esphome-lint-base:2.4.1 +FROM esphome/esphome-lint-base:2.5.0 COPY requirements.txt requirements_test.txt / RUN pip3 install --no-cache-dir -r /requirements.txt -r /requirements_test.txt diff --git a/esphome/const.py b/esphome/const.py index e1862d893b..c91244e186 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -25,6 +25,7 @@ ARDUINO_VERSION_ESP32 = { # See also https://github.com/platformio/platform-espressif8266/releases ARDUINO_VERSION_ESP8266 = { 'dev': 'https://github.com/platformio/platform-espressif8266.git', + '2.7.3': 'espressif8266@2.6.1', '2.7.2': 'espressif8266@2.6.0', '2.7.1': 'espressif8266@2.5.3', '2.7.0': 'espressif8266@2.5.0', diff --git a/esphome/core_config.py b/esphome/core_config.py index ff959fae27..b0d0c55399 100644 --- a/esphome/core_config.py +++ b/esphome/core_config.py @@ -44,7 +44,7 @@ validate_platform = cv.one_of(*ESP_PLATFORMS, upper=True) PLATFORMIO_ESP8266_LUT = { **ARDUINO_VERSION_ESP8266, - 'RECOMMENDED': ARDUINO_VERSION_ESP8266['2.7.2'], + 'RECOMMENDED': ARDUINO_VERSION_ESP8266['2.7.3'], 'LATEST': 'espressif8266', 'DEV': ARDUINO_VERSION_ESP8266['dev'], } From 61bfd347ea3bc7aa1bdbdc8b0cdbd7b5340f3a45 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Thu, 30 Jul 2020 11:38:57 +0200 Subject: [PATCH 03/12] Bump ESPAsyncTCP from 1.2.2 to 1.2.3 (#1227) --- esphome/components/async_tcp/__init__.py | 2 +- platformio.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/async_tcp/__init__.py b/esphome/components/async_tcp/__init__.py index 8af33bc4ef..b2307d5a7b 100644 --- a/esphome/components/async_tcp/__init__.py +++ b/esphome/components/async_tcp/__init__.py @@ -12,4 +12,4 @@ def to_code(config): cg.add_library('AsyncTCP-esphome', '1.1.1') elif CORE.is_esp8266: # https://github.com/OttoWinter/ESPAsyncTCP - cg.add_library('ESPAsyncTCP-esphome', '1.2.2') + cg.add_library('ESPAsyncTCP-esphome', '1.2.3') diff --git a/platformio.ini b/platformio.ini index e3f3de808c..557803bb29 100644 --- a/platformio.ini +++ b/platformio.ini @@ -16,7 +16,7 @@ lib_deps = ESPAsyncWebServer-esphome@1.2.7 FastLED@3.3.2 NeoPixelBus-esphome@2.5.7 - ESPAsyncTCP-esphome@1.2.2 + ESPAsyncTCP-esphome@1.2.3 1655@1.0.2 ; TinyGPSPlus (has name conflict) 6865@1.0.0 ; TM1651 Battery Display 6306@1.0.3 ; HM3301 From 29cfcfaf0f563ae5a84398a0ca6b871ff976f13c Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Thu, 30 Jul 2020 11:41:06 +0200 Subject: [PATCH 04/12] Fix ESP8266 core has a broken settimeofday implementation (#1231) --- esphome/components/time/real_time_clock.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/esphome/components/time/real_time_clock.cpp b/esphome/components/time/real_time_clock.cpp index cb66dc3ce6..cdcfcb14ad 100644 --- a/esphome/components/time/real_time_clock.cpp +++ b/esphome/components/time/real_time_clock.cpp @@ -4,6 +4,7 @@ #ifdef ARDUINO_ARCH_ESP8266 #include "sys/time.h" #endif +#include "errno.h" namespace esphome { namespace time { @@ -20,8 +21,18 @@ void RealTimeClock::synchronize_epoch_(uint32_t epoch) { struct timeval timev { .tv_sec = static_cast(epoch), .tv_usec = 0, }; + ESP_LOGVV(TAG, "Got epoch %u", epoch); timezone tz = {0, 0}; - settimeofday(&timev, &tz); + int ret = settimeofday(&timev, &tz); + if (ret == EINVAL) { + // Some ESP8266 frameworks abort when timezone parameter is not NULL + // while ESP32 expects it not to be NULL + ret = settimeofday(&timev, nullptr); + } + + if (ret != 0) { + ESP_LOGW(TAG, "setimeofday() failed with code %d", ret); + } auto time = this->now(); char buf[128]; From 5da9b2ede705b65e0cc81d08b0f333527da41755 Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Thu, 30 Jul 2020 17:26:40 +0200 Subject: [PATCH 05/12] Fix tuya.cpp compile warning (#1232) --- esphome/components/tuya/tuya.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esphome/components/tuya/tuya.cpp b/esphome/components/tuya/tuya.cpp index b21df81d1e..644babbdec 100644 --- a/esphome/components/tuya/tuya.cpp +++ b/esphome/components/tuya/tuya.cpp @@ -22,8 +22,8 @@ void Tuya::loop() { void Tuya::dump_config() { ESP_LOGCONFIG(TAG, "Tuya:"); if (this->init_state_ != TuyaInitState::INIT_DONE) { - ESP_LOGCONFIG(TAG, " Configuration will be reported when setup is complete. Current init_state: %u", // NOLINT - this->init_state_); + ESP_LOGCONFIG(TAG, " Configuration will be reported when setup is complete. Current init_state: %u", + static_cast(this->init_state_)); ESP_LOGCONFIG(TAG, " If no further output is received, confirm that this is a supported Tuya device."); return; } From dc4a88029c87707bcd4ace30877b7b63df6d8e5f Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Fri, 7 Aug 2020 03:08:48 +1200 Subject: [PATCH 06/12] Script mode fix (#1238) --- esphome/components/script/__init__.py | 8 ++++---- esphome/components/script/script.cpp | 2 +- tests/test2.yaml | 17 +++++++++++++++-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/esphome/components/script/__init__.py b/esphome/components/script/__init__.py index d33f64a23b..cdb334446a 100644 --- a/esphome/components/script/__init__.py +++ b/esphome/components/script/__init__.py @@ -18,14 +18,14 @@ ParallelScript = script_ns.class_('ParallelScript', Script) CONF_SINGLE = 'single' CONF_RESTART = 'restart' -CONF_QUEUE = 'queue' +CONF_QUEUED = 'queued' CONF_PARALLEL = 'parallel' CONF_MAX_RUNS = 'max_runs' SCRIPT_MODES = { CONF_SINGLE: SingleScript, CONF_RESTART: RestartScript, - CONF_QUEUE: QueueingScript, + CONF_QUEUED: QueueingScript, CONF_PARALLEL: ParallelScript, } @@ -33,7 +33,7 @@ SCRIPT_MODES = { def check_max_runs(value): if CONF_MAX_RUNS not in value: return value - if value[CONF_MODE] not in [CONF_QUEUE, CONF_PARALLEL]: + if value[CONF_MODE] not in [CONF_QUEUED, CONF_PARALLEL]: raise cv.Invalid("The option 'max_runs' is only valid in 'queue' and 'parallel' mode.", path=[CONF_MAX_RUNS]) return value @@ -65,7 +65,7 @@ def to_code(config): if CONF_MAX_RUNS in conf: cg.add(trigger.set_max_runs(conf[CONF_MAX_RUNS])) - if conf[CONF_MODE] == CONF_QUEUE: + if conf[CONF_MODE] == CONF_QUEUED: yield cg.register_component(trigger, conf) triggers.append((trigger, conf)) diff --git a/esphome/components/script/script.cpp b/esphome/components/script/script.cpp index f4441b7dcd..e99931ced6 100644 --- a/esphome/components/script/script.cpp +++ b/esphome/components/script/script.cpp @@ -33,7 +33,7 @@ void QueueingScript::execute() { return; } - ESP_LOGD(TAG, "Script '%s' queueing new instance (mode: queue)", this->name_.c_str()); + ESP_LOGD(TAG, "Script '%s' queueing new instance (mode: queued)", this->name_.c_str()); this->num_runs_++; return; } diff --git a/tests/test2.yaml b/tests/test2.yaml index 34b7ad6b07..077ceab886 100644 --- a/tests/test2.yaml +++ b/tests/test2.yaml @@ -292,6 +292,21 @@ text_sensor: script: - id: my_script + mode: single + then: + - lambda: 'ESP_LOGD("main", "Hello World!");' + - id: my_script_queued + mode: queued + max_runs: 2 + then: + - lambda: 'ESP_LOGD("main", "Hello World!");' + - id: my_script_parallel + mode: parallel + max_runs: 2 + then: + - lambda: 'ESP_LOGD("main", "Hello World!");' + - id: my_script_restart + mode: restart then: - lambda: 'ESP_LOGD("main", "Hello World!");' @@ -316,5 +331,3 @@ interval: - logger.log: "Interval Run" display: - - From 86df4c1d8d3668233d6d4cd09cbdfb2efe9b623f Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Thu, 6 Aug 2020 12:13:19 -0300 Subject: [PATCH 07/12] make powered on assume public (#1240) --- esphome/components/whirlpool/whirlpool.cpp | 10 +++++----- esphome/components/whirlpool/whirlpool.h | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/esphome/components/whirlpool/whirlpool.cpp b/esphome/components/whirlpool/whirlpool.cpp index 0956f816ce..d8db3b7353 100644 --- a/esphome/components/whirlpool/whirlpool.cpp +++ b/esphome/components/whirlpool/whirlpool.cpp @@ -41,11 +41,11 @@ void WhirlpoolClimate::transmit_state() { remote_state[18] = 0x08; auto powered_on = this->mode != climate::CLIMATE_MODE_OFF; - if (powered_on != this->powered_on_assumed_) { + if (powered_on != this->powered_on_assumed) { // Set power toggle command remote_state[2] = 4; remote_state[15] = 1; - this->powered_on_assumed_ = powered_on; + this->powered_on_assumed = powered_on; } switch (this->mode) { case climate::CLIMATE_MODE_AUTO: @@ -215,14 +215,14 @@ bool WhirlpoolClimate::on_receive(remote_base::RemoteReceiveData data) { if (powered_on) { this->mode = climate::CLIMATE_MODE_OFF; - this->powered_on_assumed_ = false; + this->powered_on_assumed = false; } else { - this->powered_on_assumed_ = true; + this->powered_on_assumed = true; } } // Set received mode - if (powered_on_assumed_) { + if (powered_on_assumed) { auto mode = remote_state[3] & 0x7; ESP_LOGV(TAG, "Mode: %02X", mode); switch (mode) { diff --git a/esphome/components/whirlpool/whirlpool.h b/esphome/components/whirlpool/whirlpool.h index 44116b340c..7f31894df9 100644 --- a/esphome/components/whirlpool/whirlpool.h +++ b/esphome/components/whirlpool/whirlpool.h @@ -28,7 +28,7 @@ class WhirlpoolClimate : public climate_ir::ClimateIR { void setup() override { climate_ir::ClimateIR::setup(); - this->powered_on_assumed_ = this->mode != climate::CLIMATE_MODE_OFF; + this->powered_on_assumed = this->mode != climate::CLIMATE_MODE_OFF; } /// Override control to change settings of the climate device. @@ -39,15 +39,15 @@ class WhirlpoolClimate : public climate_ir::ClimateIR { void set_model(Model model) { this->model_ = model; } + // used to track when to send the power toggle command + bool powered_on_assumed; + protected: /// Transmit via IR the state of this climate controller. void transmit_state() override; /// Handle received IR Buffer bool on_receive(remote_base::RemoteReceiveData data) override; - // used to track when to send the power toggle command - bool powered_on_assumed_; - bool send_swing_cmd_{false}; Model model_; From 32ae8fc2d08cffeb9bec8f7b4f8a492faa47048e Mon Sep 17 00:00:00 2001 From: Otto Winter Date: Sat, 8 Aug 2020 18:42:21 +0200 Subject: [PATCH 08/12] Bump docker base image to 2.6.0 (#1245) --- .github/workflows/ci-docker.yml | 2 +- .github/workflows/release-dev.yml | 2 +- .github/workflows/release.yml | 2 +- docker/Dockerfile | 2 +- docker/Dockerfile.dev | 2 +- docker/Dockerfile.lint | 2 +- esphome/const.py | 1 + esphome/core_config.py | 2 +- script/bump-docker-base-version.py | 59 ++++++++++++++++++++++++++++++ 9 files changed, 67 insertions(+), 7 deletions(-) create mode 100755 script/bump-docker-base-version.py diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 22109138fe..8036470f52 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -25,7 +25,7 @@ jobs: - uses: actions/checkout@v2 - name: Set up env variables run: | - base_version="2.5.0" + base_version="2.6.0" if [[ "${{ matrix.build_type }}" == "hassio" ]]; then build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml index 81b3cef124..81c3a80b05 100644 --- a/.github/workflows/release-dev.yml +++ b/.github/workflows/release-dev.yml @@ -190,7 +190,7 @@ jobs: echo "::set-env name=TAG::${TAG}" - name: Set up env variables run: | - base_version="2.5.0" + base_version="2.6.0" if [[ "${{ matrix.build_type }}" == "hassio" ]]; then build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 41ffbfa87d..7ac80355f2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -210,7 +210,7 @@ jobs: echo "::set-env name=TAG::${TAG}" - name: Set up env variables run: | - base_version="2.5.0" + base_version="2.6.0" if [[ "${{ matrix.build_type }}" == "hassio" ]]; then build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" diff --git a/docker/Dockerfile b/docker/Dockerfile index c843f2f098..865741a39f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -ARG BUILD_FROM=esphome/esphome-base-amd64:2.5.0 +ARG BUILD_FROM=esphome/esphome-base-amd64:2.6.0 FROM ${BUILD_FROM} # First install requirements to leverage caching when requirements don't change diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev index b3578e16b6..989802ab1e 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -1,4 +1,4 @@ -FROM esphome/esphome-base-amd64:2.5.0 +FROM esphome/esphome-base-amd64:2.6.0 COPY . . diff --git a/docker/Dockerfile.lint b/docker/Dockerfile.lint index 9a29c5066f..27f04dd33d 100644 --- a/docker/Dockerfile.lint +++ b/docker/Dockerfile.lint @@ -1,4 +1,4 @@ -FROM esphome/esphome-lint-base:2.5.0 +FROM esphome/esphome-lint-base:2.6.0 COPY requirements.txt requirements_test.txt / RUN pip3 install --no-cache-dir -r /requirements.txt -r /requirements_test.txt diff --git a/esphome/const.py b/esphome/const.py index c91244e186..c3eccd6311 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -25,6 +25,7 @@ ARDUINO_VERSION_ESP32 = { # See also https://github.com/platformio/platform-espressif8266/releases ARDUINO_VERSION_ESP8266 = { 'dev': 'https://github.com/platformio/platform-espressif8266.git', + '2.7.4': 'espressif8266@2.6.2', '2.7.3': 'espressif8266@2.6.1', '2.7.2': 'espressif8266@2.6.0', '2.7.1': 'espressif8266@2.5.3', diff --git a/esphome/core_config.py b/esphome/core_config.py index b0d0c55399..f167eb8d8b 100644 --- a/esphome/core_config.py +++ b/esphome/core_config.py @@ -44,7 +44,7 @@ validate_platform = cv.one_of(*ESP_PLATFORMS, upper=True) PLATFORMIO_ESP8266_LUT = { **ARDUINO_VERSION_ESP8266, - 'RECOMMENDED': ARDUINO_VERSION_ESP8266['2.7.3'], + 'RECOMMENDED': ARDUINO_VERSION_ESP8266['2.7.4'], 'LATEST': 'espressif8266', 'DEV': ARDUINO_VERSION_ESP8266['dev'], } diff --git a/script/bump-docker-base-version.py b/script/bump-docker-base-version.py new file mode 100755 index 0000000000..178643cea6 --- /dev/null +++ b/script/bump-docker-base-version.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 + +import argparse +import re +import sys + + +def sub(path, pattern, repl, expected_count=1): + with open(path) as fh: + content = fh.read() + content, count = re.subn(pattern, repl, content, flags=re.MULTILINE) + if expected_count is not None: + assert count == expected_count, f"Pattern {pattern} replacement failed!" + with open(path, "wt") as fh: + fh.write(content) + + +def write_version(version: str): + for p in [ + ".github/workflows/ci-docker.yml", + ".github/workflows/release-dev.yml", + ".github/workflows/release.yml" + ]: + sub( + p, + r'base_version=".*"', + f'base_version="{version}"' + ) + + sub( + "docker/Dockerfile", + r"ARG BUILD_FROM=esphome/esphome-base-amd64:.*", + f"ARG BUILD_FROM=esphome/esphome-base-amd64:{version}" + ) + sub( + "docker/Dockerfile.dev", + r"FROM esphome/esphome-base-amd64:.*", + f"FROM esphome/esphome-base-amd64:{version}" + ) + sub( + "docker/Dockerfile.lint", + r"FROM esphome/esphome-lint-base:.*", + f"FROM esphome/esphome-lint-base:{version}" + ) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('new_version', type=str) + args = parser.parse_args() + + version = args.new_version + print(f"Bumping to {version}") + write_version(version) + return 0 + + +if __name__ == "__main__": + sys.exit(main() or 0) From b7352b13454bfb52f38367818dcb4dec69bd1056 Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Tue, 11 Aug 2020 10:28:30 -0300 Subject: [PATCH 09/12] Image bit dephts (#1241) --- esphome/components/display/display_buffer.cpp | 51 ++++++------ esphome/components/display/display_buffer.h | 19 +++-- esphome/components/image/__init__.py | 79 ++++++++++--------- 3 files changed, 79 insertions(+), 70 deletions(-) diff --git a/esphome/components/display/display_buffer.cpp b/esphome/components/display/display_buffer.cpp index 4f62a62c6d..bd468dcbc3 100644 --- a/esphome/components/display/display_buffer.cpp +++ b/esphome/components/display/display_buffer.cpp @@ -204,31 +204,33 @@ void DisplayBuffer::vprintf_(int x, int y, Font *font, Color color, TextAlign al if (ret > 0) this->print(x, y, font, color, align, buffer); } -void DisplayBuffer::image(int x, int y, Image *image) { this->image(x, y, COLOR_ON, image); } -void DisplayBuffer::image(int x, int y, Color color, Image *image, bool invert) { - if (image->get_type() == BINARY) { - for (int img_x = 0; img_x < image->get_width(); img_x++) { - for (int img_y = 0; img_y < image->get_height(); img_y++) { - if (invert) - this->draw_pixel_at(x + img_x, y + img_y, image->get_pixel(img_x, img_y) ? COLOR_OFF : color); - else - this->draw_pixel_at(x + img_x, y + img_y, image->get_pixel(img_x, img_y) ? color : COLOR_OFF); + +void DisplayBuffer::image(int x, int y, Image *image, Color color_on, Color color_off) { + switch (image->get_type()) { + case IMAGE_TYPE_BINARY: + for (int img_x = 0; img_x < image->get_width(); img_x++) { + for (int img_y = 0; img_y < image->get_height(); img_y++) { + this->draw_pixel_at(x + img_x, y + img_y, image->get_pixel(img_x, img_y) ? color_on : color_off); + } } - } - } else if (image->get_type() == GRAYSCALE) { - for (int img_x = 0; img_x < image->get_width(); img_x++) { - for (int img_y = 0; img_y < image->get_height(); img_y++) { - this->draw_pixel_at(x + img_x, y + img_y, image->get_grayscale_pixel(img_x, img_y)); + break; + case IMAGE_TYPE_GRAYSCALE: + for (int img_x = 0; img_x < image->get_width(); img_x++) { + for (int img_y = 0; img_y < image->get_height(); img_y++) { + this->draw_pixel_at(x + img_x, y + img_y, image->get_grayscale_pixel(img_x, img_y)); + } } - } - } else if (image->get_type() == RGB) { - for (int img_x = 0; img_x < image->get_width(); img_x++) { - for (int img_y = 0; img_y < image->get_height(); img_y++) { - this->draw_pixel_at(x + img_x, y + img_y, image->get_color_pixel(img_x, img_y)); + break; + case IMAGE_TYPE_RGB24: + for (int img_x = 0; img_x < image->get_width(); img_x++) { + for (int img_y = 0; img_y < image->get_height(); img_y++) { + this->draw_pixel_at(x + img_x, y + img_y, image->get_color_pixel(img_x, img_y)); + } } - } + break; } } + void DisplayBuffer::get_text_bounds(int x, int y, const char *text, Font *font, TextAlign align, int *x1, int *y1, int *width, int *height) { int x_offset, baseline; @@ -463,15 +465,14 @@ Color Image::get_grayscale_pixel(int x, int y) const { if (x < 0 || x >= this->width_ || y < 0 || y >= this->height_) return 0; const uint32_t pos = (x + y * this->width_); - return Color(pgm_read_byte(this->data_start_ + pos) << 24); + const uint8_t gray = pgm_read_byte(this->data_start_ + pos); + return Color(gray | gray << 8 | gray << 16 | gray << 24); } int Image::get_width() const { return this->width_; } int Image::get_height() const { return this->height_; } ImageType Image::get_type() const { return this->type_; } -Image::Image(const uint8_t *data_start, int width, int height) - : width_(width), height_(height), data_start_(data_start) {} -Image::Image(const uint8_t *data_start, int width, int height, int type) - : width_(width), height_(height), type_((ImageType) type), data_start_(data_start) {} +Image::Image(const uint8_t *data_start, int width, int height, ImageType type) + : width_(width), height_(height), type_(type), data_start_(data_start) {} DisplayPage::DisplayPage(const display_writer_t &writer) : writer_(writer) {} void DisplayPage::show() { this->parent_->show_page(this); } diff --git a/esphome/components/display/display_buffer.h b/esphome/components/display/display_buffer.h index a8a308538e..e402b4b021 100644 --- a/esphome/components/display/display_buffer.h +++ b/esphome/components/display/display_buffer.h @@ -68,7 +68,7 @@ extern const Color COLOR_OFF; /// Turn the pixel ON. extern const Color COLOR_ON; -enum ImageType { BINARY = 0, GRAYSCALE = 1, RGB = 2 }; +enum ImageType { IMAGE_TYPE_BINARY = 0, IMAGE_TYPE_GRAYSCALE = 1, IMAGE_TYPE_RGB24 = 2 }; enum DisplayRotation { DISPLAY_ROTATION_0_DEGREES = 0, @@ -262,9 +262,15 @@ class DisplayBuffer { __attribute__((format(strftime, 5, 0))); #endif - /// Draw the `image` with the top-left corner at [x,y] to the screen. - void image(int x, int y, Image *image); - void image(int x, int y, Color color, Image *image, bool invert = false); + /** Draw the `image` with the top-left corner at [x,y] to the screen. + * + * @param x The x coordinate of the upper left corner. + * @param y The y coordinate of the upper left corner. + * @param image The image to draw + * @param color_on The color to replace in binary images for the on bits. + * @param color_off The color to replace in binary images for the off bits. + */ + void image(int x, int y, Image *image, Color color_on = COLOR_ON, Color color_off = COLOR_OFF); /** Get the text bounds of the given string. * @@ -381,8 +387,7 @@ class Font { class Image { public: - Image(const uint8_t *data_start, int width, int height); - Image(const uint8_t *data_start, int width, int height, int type); + Image(const uint8_t *data_start, int width, int height, ImageType type); bool get_pixel(int x, int y) const; Color get_color_pixel(int x, int y) const; Color get_grayscale_pixel(int x, int y) const; @@ -393,7 +398,7 @@ class Image { protected: int width_; int height_; - ImageType type_{BINARY}; + ImageType type_; const uint8_t *data_start_; }; diff --git a/esphome/components/image/__init__.py b/esphome/components/image/__init__.py index b5c9e29f97..dfe387afd1 100644 --- a/esphome/components/image/__init__.py +++ b/esphome/components/image/__init__.py @@ -4,14 +4,20 @@ from esphome import core from esphome.components import display, font import esphome.config_validation as cv import esphome.codegen as cg -from esphome.const import CONF_FILE, CONF_ID, CONF_RESIZE, CONF_TYPE +from esphome.const import CONF_FILE, CONF_ID, CONF_TYPE, CONF_RESIZE from esphome.core import CORE, HexInt _LOGGER = logging.getLogger(__name__) DEPENDENCIES = ['display'] MULTI_CONF = True -ImageType = {'binary': 0, 'grayscale': 1, 'rgb': 2} + +ImageType = display.display_ns.enum('ImageType') +IMAGE_TYPE = { + 'BINARY': ImageType.IMAGE_TYPE_BINARY, + 'GRAYSCALE': ImageType.IMAGE_TYPE_GRAYSCALE, + 'RGB24': ImageType.IMAGE_TYPE_RGB24, +} Image_ = display.display_ns.class_('Image') @@ -21,7 +27,7 @@ IMAGE_SCHEMA = cv.Schema({ cv.Required(CONF_ID): cv.declare_id(Image_), cv.Required(CONF_FILE): cv.file_, cv.Optional(CONF_RESIZE): cv.dimensions, - cv.Optional(CONF_TYPE): cv.string, + cv.Optional(CONF_TYPE, default='BINARY'): cv.enum(IMAGE_TYPE, upper=True), cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8), }) @@ -37,44 +43,40 @@ def to_code(config): except Exception as e: raise core.EsphomeError(f"Could not load image file {path}: {e}") + width, height = image.size + if CONF_RESIZE in config: image.thumbnail(config[CONF_RESIZE]) - - if CONF_TYPE in config: - if config[CONF_TYPE].startswith('GRAYSCALE'): - width, height = image.size - image = image.convert('L', dither=Image.NONE) - pixels = list(image.getdata()) - data = [0 for _ in range(height * width)] - pos = 0 - for pix in pixels: - data[pos] = pix - pos += 1 - rhs = [HexInt(x) for x in data] - prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) - cg.new_Pvariable(config[CONF_ID], prog_arr, width, height, ImageType['grayscale']) - elif config[CONF_TYPE].startswith('RGB'): - width, height = image.size - image = image.convert('RGB') - pixels = list(image.getdata()) - data = [0 for _ in range(height * width * 3)] - pos = 0 - for pix in pixels: - data[pos] = pix[0] - pos += 1 - data[pos] = pix[1] - pos += 1 - data[pos] = pix[2] - pos += 1 - rhs = [HexInt(x) for x in data] - prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) - cg.new_Pvariable(config[CONF_ID], prog_arr, width, height, ImageType['rgb']) - else: - image = image.convert('1', dither=Image.NONE) width, height = image.size + else: if width > 500 or height > 500: _LOGGER.warning("The image you requested is very big. Please consider using" " the resize parameter.") + + if config[CONF_TYPE] == 'GRAYSCALE': + image = image.convert('L', dither=Image.NONE) + pixels = list(image.getdata()) + data = [0 for _ in range(height * width)] + pos = 0 + for pix in pixels: + data[pos] = pix + pos += 1 + + elif config[CONF_TYPE] == 'RGB24': + image = image.convert('RGB') + pixels = list(image.getdata()) + data = [0 for _ in range(height * width * 3)] + pos = 0 + for pix in pixels: + data[pos] = pix[0] + pos += 1 + data[pos] = pix[1] + pos += 1 + data[pos] = pix[2] + pos += 1 + + elif config[CONF_TYPE] == 'BINARY': + image = image.convert('1', dither=Image.NONE) width8 = ((width + 7) // 8) * 8 data = [0 for _ in range(height * width8 // 8)] for y in range(height): @@ -84,6 +86,7 @@ def to_code(config): pos = x + y * width8 data[pos // 8] |= 0x80 >> (pos % 8) - rhs = [HexInt(x) for x in data] - prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) - cg.new_Pvariable(config[CONF_ID], prog_arr, width, height) + rhs = [HexInt(x) for x in data] + prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs) + cg.new_Pvariable(config[CONF_ID], prog_arr, width, height, + IMAGE_TYPE[config[CONF_TYPE]]) From cfe4638665de6646bc69abe44b53a481431e23ad Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Thu, 13 Aug 2020 23:21:19 -0300 Subject: [PATCH 10/12] fixes deg symbol not shown (#1248) --- esphome/components/tm1637/tm1637.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/tm1637/tm1637.cpp b/esphome/components/tm1637/tm1637.cpp index d9b9a870ff..833e3caecd 100644 --- a/esphome/components/tm1637/tm1637.cpp +++ b/esphome/components/tm1637/tm1637.cpp @@ -232,7 +232,7 @@ uint8_t TM1637Display::print(uint8_t start_pos, const char* str) { uint8_t pos = start_pos; for (; *str != '\0'; str++) { uint8_t data = TM1637_UNKNOWN_CHAR; - if (*str >= ' ' && *str <= '}') + if (*str >= ' ' && *str <= '~') data = pgm_read_byte(&TM1637_ASCII_TO_RAW[*str - ' ']); if (data == TM1637_UNKNOWN_CHAR) { From 228670df78023b9ec233749407f1a12e53747321 Mon Sep 17 00:00:00 2001 From: Keith Burzinski Date: Sun, 23 Aug 2020 18:36:11 -0500 Subject: [PATCH 11/12] Fix SSD1306 post-setup brightness control (#1090) * Fix post-setup brightness control * Add turn_on() and turn_off() methods * Added is_on() method * Set brightness later in setup() * Use clamp() for brightness validation --- esphome/components/ssd1306_base/__init__.py | 2 +- .../components/ssd1306_base/ssd1306_base.cpp | 51 ++++++++++--------- .../components/ssd1306_base/ssd1306_base.h | 8 ++- 3 files changed, 34 insertions(+), 27 deletions(-) diff --git a/esphome/components/ssd1306_base/__init__.py b/esphome/components/ssd1306_base/__init__.py index 047ddddcac..a8b2a2a7bb 100644 --- a/esphome/components/ssd1306_base/__init__.py +++ b/esphome/components/ssd1306_base/__init__.py @@ -41,7 +41,7 @@ def setup_ssd1036(var, config): reset = yield cg.gpio_pin_expression(config[CONF_RESET_PIN]) cg.add(var.set_reset_pin(reset)) if CONF_BRIGHTNESS in config: - cg.add(var.set_brightness(config[CONF_BRIGHTNESS])) + cg.add(var.init_brightness(config[CONF_BRIGHTNESS])) if CONF_EXTERNAL_VCC in config: cg.add(var.set_external_vcc(config[CONF_EXTERNAL_VCC])) if CONF_LAMBDA in config: diff --git a/esphome/components/ssd1306_base/ssd1306_base.cpp b/esphome/components/ssd1306_base/ssd1306_base.cpp index 2c1e5c6de8..1537d3b526 100644 --- a/esphome/components/ssd1306_base/ssd1306_base.cpp +++ b/esphome/components/ssd1306_base/ssd1306_base.cpp @@ -5,7 +5,11 @@ namespace esphome { namespace ssd1306_base { -static const char *TAG = "sd1306"; +static const char *TAG = "ssd1306"; + +static const uint8_t BLACK = 0; +static const uint8_t WHITE = 1; +static const uint8_t SSD1306_MAX_CONTRAST = 255; static const uint8_t SSD1306_COMMAND_DISPLAY_OFF = 0xAE; static const uint8_t SSD1306_COMMAND_DISPLAY_ON = 0xAF; @@ -69,27 +73,6 @@ void SSD1306::setup() { break; } - this->command(SSD1306_COMMAND_SET_CONTRAST); - switch (this->model_) { - case SSD1306_MODEL_128_32: - case SH1106_MODEL_128_32: - this->command(0x8F); - break; - case SSD1306_MODEL_128_64: - case SH1106_MODEL_128_64: - case SSD1306_MODEL_64_48: - case SH1106_MODEL_64_48: - this->command(int(255 * (this->brightness_))); - break; - case SSD1306_MODEL_96_16: - case SH1106_MODEL_96_16: - if (this->external_vcc_) - this->command(0x10); - else - this->command(0xAF); - break; - } - this->command(SSD1306_COMMAND_SET_PRE_CHARGE); if (this->external_vcc_) this->command(0x22); @@ -104,7 +87,12 @@ void SSD1306::setup() { this->command(SSD1306_COMMAND_DEACTIVATE_SCROLL); - this->command(SSD1306_COMMAND_DISPLAY_ON); + set_brightness(this->brightness_); + + this->fill(BLACK); // clear display - ensures we do not see garbage at power-on + this->display(); // ...write buffer, which actually clears the display's memory + + this->turn_on(); } void SSD1306::display() { if (this->is_sh1106_()) { @@ -140,6 +128,22 @@ void SSD1306::update() { this->do_update_(); this->display(); } +void SSD1306::set_brightness(float brightness) { + // validation + this->brightness_ = clamp(brightness, 0, 1); + // now write the new brightness level to the display + this->command(SSD1306_COMMAND_SET_CONTRAST); + this->command(int(SSD1306_MAX_CONTRAST * (this->brightness_))); +} +bool SSD1306::is_on() { return this->is_on_; } +void SSD1306::turn_on() { + this->command(SSD1306_COMMAND_DISPLAY_ON); + this->is_on_ = true; +} +void SSD1306::turn_off() { + this->command(SSD1306_COMMAND_DISPLAY_OFF); + this->is_on_ = false; +} int SSD1306::get_height_internal() { switch (this->model_) { case SSD1306_MODEL_128_32: @@ -178,7 +182,6 @@ int SSD1306::get_width_internal() { size_t SSD1306::get_buffer_length_() { return size_t(this->get_width_internal()) * size_t(this->get_height_internal()) / 8u; } - void HOT SSD1306::draw_absolute_pixel_internal(int x, int y, Color color) { if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) return; diff --git a/esphome/components/ssd1306_base/ssd1306_base.h b/esphome/components/ssd1306_base/ssd1306_base.h index 3e46ef9cc7..0fe09709e7 100644 --- a/esphome/components/ssd1306_base/ssd1306_base.h +++ b/esphome/components/ssd1306_base/ssd1306_base.h @@ -29,8 +29,11 @@ class SSD1306 : public PollingComponent, public display::DisplayBuffer { void set_model(SSD1306Model model) { this->model_ = model; } void set_reset_pin(GPIOPin *reset_pin) { this->reset_pin_ = reset_pin; } void set_external_vcc(bool external_vcc) { this->external_vcc_ = external_vcc; } - void set_brightness(float brightness) { this->brightness_ = brightness; } - + void init_brightness(float brightness) { this->brightness_ = brightness; } + void set_brightness(float brightness); + bool is_on(); + void turn_on(); + void turn_off(); float get_setup_priority() const override { return setup_priority::PROCESSOR; } void fill(Color color) override; @@ -51,6 +54,7 @@ class SSD1306 : public PollingComponent, public display::DisplayBuffer { SSD1306Model model_{SSD1306_MODEL_128_64}; GPIOPin *reset_pin_{nullptr}; bool external_vcc_{false}; + bool is_on_{false}; float brightness_{1.0}; }; From ff050d634ad8929a441a0b5eb88ceb6db40ea1e4 Mon Sep 17 00:00:00 2001 From: Guillermo Ruffino Date: Sun, 23 Aug 2020 21:53:50 -0300 Subject: [PATCH 12/12] Bump version to v1.15.0b4 --- esphome/const.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/const.py b/esphome/const.py index c3eccd6311..153e2902d4 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -2,7 +2,7 @@ MAJOR_VERSION = 1 MINOR_VERSION = 15 -PATCH_VERSION = '0b3' +PATCH_VERSION = '0b4' __short_version__ = f'{MAJOR_VERSION}.{MINOR_VERSION}' __version__ = f'{__short_version__}.{PATCH_VERSION}'