diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index eedda109ca..b7aeeda471 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.0" + base_version="2.4.1" 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 fc9afc06ec..2a427d6c95 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.0" + base_version="2.4.1" 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 c657655926..ad1749de92 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.0" + base_version="2.4.1" if [[ "${{ matrix.build_type }}" == "hassio" ]]; then build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}" @@ -222,13 +222,20 @@ jobs: dockerfile="docker/Dockerfile" fi + if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then + cache_tag="beta" + else + cache_tag="latest" + end + # Set env variables so these values don't need to be calculated again echo "::set-env name=BUILD_FROM::${build_from}" echo "::set-env name=BUILD_TO::${build_to}" echo "::set-env name=DOCKERFILE::${dockerfile}" + echo "::set-env name=CACHE_TAG::${cache_tag}" - name: Pull for cache run: | - docker pull "${BUILD_TO}:latest" || true + docker pull "${BUILD_TO}:${CACHE_TAG}" || true - name: Register QEMU binfmt run: docker run --rm --privileged multiarch/qemu-user-static:5.0.0-2 --reset -p yes - run: | @@ -236,7 +243,7 @@ jobs: --build-arg "BUILD_FROM=${BUILD_FROM}" \ --build-arg "BUILD_VERSION=${TAG}" \ --tag "${BUILD_TO}:${TAG}" \ - --cache-from "${BUILD_TO}:latest" \ + --cache-from "${BUILD_TO}:${CACHE_TAG}" \ --file "${DOCKERFILE}" \ . - name: Log in to docker hub @@ -300,3 +307,19 @@ jobs: esphome/esphome-amd64:${TAG} \ esphome/esphome-armv7:${TAG} docker manifest push esphome/esphome:latest + + deploy-hassio-repo: + if: github.repository == 'esphome/esphome' + runs-on: ubuntu-latest + needs: [deploy-docker] + steps: + - env: + TOKEN: ${{ secrets.DEPLOY_HASSIO_TOKEN }} + run: | + TAG="${GITHUB_REF#refs/tags/v}" + curl \ + -u ":$TOKEN" \ + -X POST \ + -H "Accept: application/vnd.github.v3+json" \ + https://api.github.com/repos/esphome/hassio/actions/workflows/bump-version.yml/dispatches \ + -d "{\"ref\":\"master\",\"inputs\":{\"version\":\"$TAG\"}}" diff --git a/MANIFEST.in b/MANIFEST.in index cdea2df2a6..0fe80762b3 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,6 @@ include LICENSE include README.md +include requirements.txt include esphome/dashboard/templates/*.html recursive-include esphome/dashboard/static *.ico *.js *.css *.woff* LICENSE recursive-include esphome *.cpp *.h *.tcc diff --git a/docker/Dockerfile b/docker/Dockerfile index e4097d59d8..8845c9f58c 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -ARG BUILD_FROM=esphome/esphome-base-amd64:2.4.0 +ARG BUILD_FROM=esphome/esphome-base-amd64:2.4.1 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 bb051b49ea..032e29ae30 100644 --- a/docker/Dockerfile.dev +++ b/docker/Dockerfile.dev @@ -1,4 +1,4 @@ -FROM esphome/esphome-base-amd64:2.4.0 +FROM esphome/esphome-base-amd64:2.4.1 COPY . . diff --git a/docker/Dockerfile.lint b/docker/Dockerfile.lint index 4e0b281130..2d34f300e3 100644 --- a/docker/Dockerfile.lint +++ b/docker/Dockerfile.lint @@ -1,4 +1,4 @@ -FROM esphome/esphome-lint-base:2.4.0 +FROM esphome/esphome-lint-base:2.4.1 COPY requirements.txt requirements_test.txt / RUN pip3 install --no-cache-dir -r /requirements.txt -r /requirements_test.txt diff --git a/esphome/components/ccs811/sensor.py b/esphome/components/ccs811/sensor.py index a8020c77f7..0f755d5c11 100644 --- a/esphome/components/ccs811/sensor.py +++ b/esphome/components/ccs811/sensor.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_ID, ICON_RADIATOR, UNIT_PARTS_PER_MILLION, \ - UNIT_PARTS_PER_BILLION, CONF_TEMPERATURE, CONF_HUMIDITY, ICON_PERIODIC_TABLE_CO2 + UNIT_PARTS_PER_BILLION, CONF_TEMPERATURE, CONF_HUMIDITY, ICON_MOLECULE_CO2 DEPENDENCIES = ['i2c'] @@ -15,7 +15,7 @@ CONF_BASELINE = 'baseline' CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(CCS811Component), - cv.Required(CONF_ECO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_PERIODIC_TABLE_CO2, + cv.Required(CONF_ECO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0), cv.Required(CONF_TVOC): sensor.sensor_schema(UNIT_PARTS_PER_BILLION, ICON_RADIATOR, 0), diff --git a/esphome/components/light/automation.h b/esphome/components/light/automation.h index dfab780658..b240f84e8f 100644 --- a/esphome/components/light/automation.h +++ b/esphome/components/light/automation.h @@ -103,10 +103,14 @@ class LightTurnOnTrigger : public Trigger<> { LightTurnOnTrigger(LightState *a_light) { a_light->add_new_remote_values_callback([this, a_light]() { auto is_on = a_light->current_values.is_on(); - if (is_on && !last_on_) { + // only trigger when going from off to on + auto should_trigger = is_on && !last_on_; + // Set new state immediately so that trigger() doesn't devolve + // into infinite loop + last_on_ = is_on; + if (should_trigger) { this->trigger(); } - last_on_ = is_on; }); last_on_ = a_light->current_values.is_on(); } @@ -120,10 +124,14 @@ class LightTurnOffTrigger : public Trigger<> { LightTurnOffTrigger(LightState *a_light) { a_light->add_new_remote_values_callback([this, a_light]() { auto is_on = a_light->current_values.is_on(); - if (!is_on && last_on_) { + // only trigger when going from on to off + auto should_trigger = !is_on && last_on_; + // Set new state immediately so that trigger() doesn't devolve + // into infinite loop + last_on_ = is_on; + if (should_trigger) { this->trigger(); } - last_on_ = is_on; }); last_on_ = a_light->current_values.is_on(); } diff --git a/esphome/components/mhz19/sensor.py b/esphome/components/mhz19/sensor.py index bdcecf12cb..d0a7caee84 100644 --- a/esphome/components/mhz19/sensor.py +++ b/esphome/components/mhz19/sensor.py @@ -3,7 +3,7 @@ import esphome.config_validation as cv from esphome import automation from esphome.automation import maybe_simple_id from esphome.components import sensor, uart -from esphome.const import CONF_CO2, CONF_ID, CONF_TEMPERATURE, ICON_PERIODIC_TABLE_CO2, \ +from esphome.const import CONF_CO2, CONF_ID, CONF_TEMPERATURE, ICON_MOLECULE_CO2, \ UNIT_PARTS_PER_MILLION, UNIT_CELSIUS, ICON_THERMOMETER DEPENDENCIES = ['uart'] @@ -18,7 +18,7 @@ MHZ19ABCDisableAction = mhz19_ns.class_('MHZ19ABCDisableAction', automation.Acti CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(MHZ19Component), - cv.Required(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_PERIODIC_TABLE_CO2, 0), + cv.Required(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0), cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 0), cv.Optional(CONF_AUTOMATIC_BASELINE_CALIBRATION): cv.boolean, }).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA) diff --git a/esphome/components/scd30/sensor.py b/esphome/components/scd30/sensor.py index b3de1b214a..ca1a254e05 100644 --- a/esphome/components/scd30/sensor.py +++ b/esphome/components/scd30/sensor.py @@ -3,7 +3,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_ID, UNIT_PARTS_PER_MILLION, \ - CONF_HUMIDITY, CONF_TEMPERATURE, ICON_PERIODIC_TABLE_CO2, \ + CONF_HUMIDITY, CONF_TEMPERATURE, ICON_MOLECULE_CO2, \ UNIT_CELSIUS, ICON_THERMOMETER, ICON_WATER_PERCENT, UNIT_PERCENT, CONF_CO2 DEPENDENCIES = ['i2c'] @@ -22,7 +22,7 @@ def remove_altitude_suffix(value): CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(SCD30Component), cv.Required(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, - ICON_PERIODIC_TABLE_CO2, 0), + ICON_MOLECULE_CO2, 0), cv.Required(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1), cv.Required(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1), cv.Optional(CONF_AUTOMATIC_SELF_CALIBRATION, default=True): cv.boolean, diff --git a/esphome/components/senseair/sensor.py b/esphome/components/senseair/sensor.py index 393bfd5182..c015871156 100644 --- a/esphome/components/senseair/sensor.py +++ b/esphome/components/senseair/sensor.py @@ -1,7 +1,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import sensor, uart -from esphome.const import CONF_CO2, CONF_ID, ICON_PERIODIC_TABLE_CO2, UNIT_PARTS_PER_MILLION +from esphome.const import CONF_CO2, CONF_ID, ICON_MOLECULE_CO2, UNIT_PARTS_PER_MILLION DEPENDENCIES = ['uart'] @@ -10,7 +10,7 @@ SenseAirComponent = senseair_ns.class_('SenseAirComponent', cg.PollingComponent, CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(SenseAirComponent), - cv.Required(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_PERIODIC_TABLE_CO2, 0), + cv.Required(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0), }).extend(cv.polling_component_schema('60s')).extend(uart.UART_DEVICE_SCHEMA) diff --git a/esphome/components/sgp30/sensor.py b/esphome/components/sgp30/sensor.py index a52811eb34..a8963fef7b 100644 --- a/esphome/components/sgp30/sensor.py +++ b/esphome/components/sgp30/sensor.py @@ -2,7 +2,7 @@ import esphome.codegen as cg import esphome.config_validation as cv from esphome.components import i2c, sensor from esphome.const import CONF_ID, ICON_RADIATOR, UNIT_PARTS_PER_MILLION, \ - UNIT_PARTS_PER_BILLION, ICON_PERIODIC_TABLE_CO2 + UNIT_PARTS_PER_BILLION, ICON_MOLECULE_CO2 DEPENDENCIES = ['i2c'] @@ -22,7 +22,7 @@ CONF_TEMPERATURE_SOURCE = 'temperature_source' CONFIG_SCHEMA = cv.Schema({ cv.GenerateID(): cv.declare_id(SGP30Component), cv.Required(CONF_ECO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, - ICON_PERIODIC_TABLE_CO2, 0), + ICON_MOLECULE_CO2, 0), cv.Required(CONF_TVOC): sensor.sensor_schema(UNIT_PARTS_PER_BILLION, ICON_RADIATOR, 0), cv.Optional(CONF_BASELINE): cv.Schema({ cv.Required(CONF_ECO2_BASELINE): cv.hex_uint16_t, diff --git a/esphome/components/web_server_base/__init__.py b/esphome/components/web_server_base/__init__.py index f7b2238552..05f4a4a4c6 100644 --- a/esphome/components/web_server_base/__init__.py +++ b/esphome/components/web_server_base/__init__.py @@ -24,4 +24,4 @@ def to_code(config): if CORE.is_esp32: cg.add_library('FS', None) # https://github.com/OttoWinter/ESPAsyncWebServer/blob/master/library.json - cg.add_library('ESPAsyncWebServer-esphome', '1.2.6') + cg.add_library('ESPAsyncWebServer-esphome', '1.2.7') diff --git a/esphome/components/wifi/wifi_component.cpp b/esphome/components/wifi/wifi_component.cpp index 5c1c533c5d..df80c5b109 100644 --- a/esphome/components/wifi/wifi_component.cpp +++ b/esphome/components/wifi/wifi_component.cpp @@ -201,7 +201,26 @@ void WiFiComponent::start_connecting(const WiFiAP &ap, bool two) { } else { ESP_LOGV(TAG, " BSSID: Not Set"); } - ESP_LOGV(TAG, " Password: " LOG_SECRET("'%s'"), ap.get_password().c_str()); + +#ifdef ESPHOME_WIFI_WPA2_EAP + if (ap.get_eap().has_value()) { + ESP_LOGV(TAG, " WPA2 Enterprise authentication configured:"); + EAPAuth eap_config = ap.get_eap().value(); + ESP_LOGV(TAG, " Identity: " LOG_SECRET("'%s'"), eap_config.identity.c_str()); + ESP_LOGV(TAG, " Username: " LOG_SECRET("'%s'"), eap_config.username.c_str()); + ESP_LOGV(TAG, " Password: " LOG_SECRET("'%s'"), eap_config.password.c_str()); + bool ca_cert_present = eap_config.ca_cert != nullptr && strlen(eap_config.ca_cert); + bool client_cert_present = eap_config.client_cert != nullptr && strlen(eap_config.client_cert); + bool client_key_present = eap_config.client_key != nullptr && strlen(eap_config.client_key); + ESP_LOGV(TAG, " CA Cert: %s", ca_cert_present ? "present" : "not present"); + ESP_LOGV(TAG, " Client Cert: %s", client_cert_present ? "present" : "not present"); + ESP_LOGV(TAG, " Client Key: %s", client_key_present ? "present" : "not present"); + } else { +#endif + ESP_LOGV(TAG, " Password: " LOG_SECRET("'%s'"), ap.get_password().c_str()); +#ifdef ESPHOME_WIFI_WPA2_EAP + } +#endif if (ap.get_channel().has_value()) { ESP_LOGV(TAG, " Channel: %u", *ap.get_channel()); } else { @@ -400,9 +419,17 @@ void WiFiComponent::check_scanning_finished() { connect_params.set_channel(scan_res.get_channel()); connect_params.set_bssid(scan_res.get_bssid()); } - // set manual IP+password (if any) + // copy manual IP (if set) connect_params.set_manual_ip(config.get_manual_ip()); + +#ifdef ESPHOME_WIFI_WPA2_EAP + // copy EAP parameters (if set) + connect_params.set_eap(config.get_eap()); +#endif + + // copy password (if set) connect_params.set_password(config.get_password()); + break; } @@ -576,9 +603,21 @@ bool WiFiScanResult::matches(const WiFiAP &config) { // If BSSID configured, only match for correct BSSIDs if (config.get_bssid().has_value() && *config.get_bssid() != this->bssid_) return false; - // If PW given, only match for networks with auth (and vice versa) + +#ifdef ESPHOME_WIFI_WPA2_EAP + // BSSID requires auth but no PSK or EAP credentials given + if (this->with_auth_ && (config.get_password().empty() && !config.get_eap().has_value())) + return false; + + // BSSID does not require auth, but PSK or EAP credentials given + if (!this->with_auth_ && (!config.get_password().empty() || config.get_eap().has_value())) + return false; +#else + // If PSK given, only match for networks with auth (and vice versa) if (config.get_password().empty() == this->with_auth_) return false; +#endif + // If channel configured, only match networks on that channel. if (config.get_channel().has_value() && *config.get_channel() != this->channel_) { return false; diff --git a/esphome/components/zyaura/sensor.py b/esphome/components/zyaura/sensor.py index 649b80b444..df263974e8 100644 --- a/esphome/components/zyaura/sensor.py +++ b/esphome/components/zyaura/sensor.py @@ -5,7 +5,7 @@ from esphome.components import sensor from esphome.const import CONF_ID, CONF_CLOCK_PIN, CONF_DATA_PIN, \ CONF_CO2, CONF_TEMPERATURE, CONF_HUMIDITY, \ UNIT_PARTS_PER_MILLION, UNIT_CELSIUS, UNIT_PERCENT, \ - ICON_PERIODIC_TABLE_CO2, ICON_THERMOMETER, ICON_WATER_PERCENT + ICON_MOLECULE_CO2, ICON_THERMOMETER, ICON_WATER_PERCENT from esphome.cpp_helpers import gpio_pin_expression zyaura_ns = cg.esphome_ns.namespace('zyaura') @@ -17,7 +17,7 @@ CONFIG_SCHEMA = cv.Schema({ pins.validate_has_interrupt), cv.Required(CONF_DATA_PIN): cv.All(pins.internal_gpio_input_pin_schema, pins.validate_has_interrupt), - cv.Optional(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_PERIODIC_TABLE_CO2, 0), + cv.Optional(CONF_CO2): sensor.sensor_schema(UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0), 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')) diff --git a/esphome/const.py b/esphome/const.py index 87d67f38bc..e1862d893b 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -2,7 +2,7 @@ MAJOR_VERSION = 1 MINOR_VERSION = 15 -PATCH_VERSION = '0b2' +PATCH_VERSION = '0b3' __short_version__ = f'{MAJOR_VERSION}.{MINOR_VERSION}' __version__ = f'{__short_version__}.{PATCH_VERSION}' @@ -587,10 +587,10 @@ ICON_GAS_CYLINDER = 'mdi:gas-cylinder' ICON_GAUGE = 'mdi:gauge' ICON_LIGHTBULB = 'mdi:lightbulb' ICON_MAGNET = 'mdi:magnet' +ICON_MOLECULE_CO2 = 'mdi:molecule-co2' ICON_MOTION_SENSOR = 'mdi:motion-sensor' ICON_NEW_BOX = 'mdi:new-box' ICON_PERCENT = 'mdi:percent' -ICON_PERIODIC_TABLE_CO2 = 'mdi:periodic-table-co2' ICON_POWER = 'mdi:power' ICON_PULSE = 'mdi:pulse' ICON_RADIATOR = 'mdi:radiator' diff --git a/platformio.ini b/platformio.ini index c43a25d29b..e3f3de808c 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,7 +13,7 @@ lib_deps = AsyncTCP-esphome@1.1.1 AsyncMqttClient-esphome@0.8.4 ArduinoJson-esphomelib@5.13.3 - ESPAsyncWebServer-esphome@1.2.6 + ESPAsyncWebServer-esphome@1.2.7 FastLED@3.3.2 NeoPixelBus-esphome@2.5.7 ESPAsyncTCP-esphome@1.2.2