Merge branch 'dev' into optolink

This commit is contained in:
j0ta29 2023-11-25 16:38:33 +01:00 committed by GitHub
commit 88d282200a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 523 additions and 29 deletions

View file

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check for needs-docs label - name: Check for needs-docs label
uses: actions/github-script@v6.4.1 uses: actions/github-script@v7.0.1
with: with:
script: | script: |
const { data: labels } = await github.rest.issues.listLabelsOnIssue({ const { data: labels } = await github.rest.issues.listLabelsOnIssue({

View file

@ -203,7 +203,7 @@ jobs:
needs: [deploy-manifest] needs: [deploy-manifest]
steps: steps:
- name: Trigger Workflow - name: Trigger Workflow
uses: actions/github-script@v6.4.1 uses: actions/github-script@v7.0.1
with: with:
github-token: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }} github-token: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
script: | script: |

View file

@ -90,6 +90,7 @@ esphome/components/duty_time/* @dudanov
esphome/components/ee895/* @Stock-M esphome/components/ee895/* @Stock-M
esphome/components/ektf2232/* @jesserockz esphome/components/ektf2232/* @jesserockz
esphome/components/emc2101/* @ellull esphome/components/emc2101/* @ellull
esphome/components/ens160/* @vincentscode
esphome/components/ens210/* @itn3rd77 esphome/components/ens210/* @itn3rd77
esphome/components/esp32/* @esphome/core esphome/components/esp32/* @esphome/core
esphome/components/esp32_ble/* @Rapsssito @jesserockz esphome/components/esp32_ble/* @Rapsssito @jesserockz

View file

@ -0,0 +1 @@
CODEOWNERS = ["@vincentscode"]

View file

@ -0,0 +1,321 @@
// ENS160 sensor with I2C interface from ScioSense
//
// Datasheet: https://www.sciosense.com/wp-content/uploads/documents/SC-001224-DS-7-ENS160-Datasheet.pdf
//
// Implementation based on:
// https://github.com/sciosense/ENS160_driver
#include "ens160.h"
#include "esphome/core/log.h"
#include "esphome/core/hal.h"
namespace esphome {
namespace ens160 {
static const char *const TAG = "ens160";
static const uint8_t ENS160_BOOTING = 10;
static const uint16_t ENS160_PART_ID = 0x0160;
static const uint8_t ENS160_REG_PART_ID = 0x00;
static const uint8_t ENS160_REG_OPMODE = 0x10;
static const uint8_t ENS160_REG_CONFIG = 0x11;
static const uint8_t ENS160_REG_COMMAND = 0x12;
static const uint8_t ENS160_REG_TEMP_IN = 0x13;
static const uint8_t ENS160_REG_DATA_STATUS = 0x20;
static const uint8_t ENS160_REG_DATA_AQI = 0x21;
static const uint8_t ENS160_REG_DATA_TVOC = 0x22;
static const uint8_t ENS160_REG_DATA_ECO2 = 0x24;
static const uint8_t ENS160_REG_GPR_READ_0 = 0x48;
static const uint8_t ENS160_REG_GPR_READ_4 = ENS160_REG_GPR_READ_0 + 4;
static const uint8_t ENS160_COMMAND_NOP = 0x00;
static const uint8_t ENS160_COMMAND_CLRGPR = 0xCC;
static const uint8_t ENS160_COMMAND_GET_APPVER = 0x0E;
static const uint8_t ENS160_OPMODE_RESET = 0xF0;
static const uint8_t ENS160_OPMODE_IDLE = 0x01;
static const uint8_t ENS160_OPMODE_STD = 0x02;
static const uint8_t ENS160_DATA_STATUS_STATAS = 0x80;
static const uint8_t ENS160_DATA_STATUS_STATER = 0x40;
static const uint8_t ENS160_DATA_STATUS_VALIDITY = 0x0C;
static const uint8_t ENS160_DATA_STATUS_NEWDAT = 0x02;
static const uint8_t ENS160_DATA_STATUS_NEWGPR = 0x01;
// helps remove reserved bits in aqi data register
static const uint8_t ENS160_DATA_AQI = 0x07;
void ENS160Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up ENS160...");
// check part_id
uint16_t part_id;
if (!this->read_bytes(ENS160_REG_PART_ID, reinterpret_cast<uint8_t *>(&part_id), 2)) {
this->error_code_ = COMMUNICATION_FAILED;
this->mark_failed();
return;
}
if (part_id != ENS160_PART_ID) {
this->error_code_ = INVALID_ID;
this->mark_failed();
return;
}
// set mode to reset
if (!this->write_byte(ENS160_REG_OPMODE, ENS160_OPMODE_RESET)) {
this->error_code_ = WRITE_FAILED;
this->mark_failed();
return;
}
delay(ENS160_BOOTING);
// check status
uint8_t status_value;
if (!this->read_byte(ENS160_REG_DATA_STATUS, &status_value)) {
this->error_code_ = READ_FAILED;
this->mark_failed();
return;
}
this->validity_flag_ = static_cast<ValidityFlag>((ENS160_DATA_STATUS_VALIDITY & status_value) >> 2);
if (this->validity_flag_ == INVALID_OUTPUT) {
this->error_code_ = VALIDITY_INVALID;
this->mark_failed();
return;
}
// set mode to idle
if (!this->write_byte(ENS160_REG_OPMODE, ENS160_OPMODE_IDLE)) {
this->error_code_ = WRITE_FAILED;
this->mark_failed();
return;
}
// clear command
if (!this->write_byte(ENS160_REG_COMMAND, ENS160_COMMAND_NOP)) {
this->error_code_ = WRITE_FAILED;
this->mark_failed();
return;
}
if (!this->write_byte(ENS160_REG_COMMAND, ENS160_COMMAND_CLRGPR)) {
this->error_code_ = WRITE_FAILED;
this->mark_failed();
return;
}
// read firmware version
if (!this->write_byte(ENS160_REG_COMMAND, ENS160_COMMAND_GET_APPVER)) {
this->error_code_ = WRITE_FAILED;
this->mark_failed();
return;
}
uint8_t version_data[3];
if (!this->read_bytes(ENS160_REG_GPR_READ_4, version_data, 3)) {
this->error_code_ = READ_FAILED;
this->mark_failed();
return;
}
this->firmware_ver_major_ = version_data[0];
this->firmware_ver_minor_ = version_data[1];
this->firmware_ver_build_ = version_data[2];
// set mode to standard
if (!this->write_byte(ENS160_REG_OPMODE, ENS160_OPMODE_STD)) {
this->error_code_ = WRITE_FAILED;
this->mark_failed();
return;
}
// read opmode and check standard mode is achieved before finishing Setup
uint8_t op_mode;
if (!this->read_byte(ENS160_REG_OPMODE, &op_mode)) {
this->error_code_ = READ_FAILED;
this->mark_failed();
return;
}
if (op_mode != ENS160_OPMODE_STD) {
this->error_code_ = STD_OPMODE_FAILED;
this->mark_failed();
return;
}
}
void ENS160Component::update() {
uint8_t status_value, data_ready;
if (!this->read_byte(ENS160_REG_DATA_STATUS, &status_value)) {
ESP_LOGW(TAG, "Error reading status register");
this->status_set_warning();
return;
}
// verbose status logging
ESP_LOGV(TAG, "Status: ENS160 STATAS bit 0x%x",
(ENS160_DATA_STATUS_STATAS & (status_value)) == ENS160_DATA_STATUS_STATAS);
ESP_LOGV(TAG, "Status: ENS160 STATER bit 0x%x",
(ENS160_DATA_STATUS_STATER & (status_value)) == ENS160_DATA_STATUS_STATER);
ESP_LOGV(TAG, "Status: ENS160 VALIDITY FLAG 0x%02x", (ENS160_DATA_STATUS_VALIDITY & status_value) >> 2);
ESP_LOGV(TAG, "Status: ENS160 NEWDAT bit 0x%x",
(ENS160_DATA_STATUS_NEWDAT & (status_value)) == ENS160_DATA_STATUS_NEWDAT);
ESP_LOGV(TAG, "Status: ENS160 NEWGPR bit 0x%x",
(ENS160_DATA_STATUS_NEWGPR & (status_value)) == ENS160_DATA_STATUS_NEWGPR);
data_ready = ENS160_DATA_STATUS_NEWDAT & status_value;
this->validity_flag_ = static_cast<ValidityFlag>((ENS160_DATA_STATUS_VALIDITY & status_value) >> 2);
switch (validity_flag_) {
case NORMAL_OPERATION:
if (data_ready != ENS160_DATA_STATUS_NEWDAT) {
ESP_LOGD(TAG, "ENS160 readings unavailable - Normal Operation but readings not ready");
return;
}
break;
case INITIAL_STARTUP:
if (!this->initial_startup_) {
this->initial_startup_ = true;
ESP_LOGI(TAG, "ENS160 readings unavailable - 1 hour startup required after first power on");
}
return;
case WARMING_UP:
if (!this->warming_up_) {
this->warming_up_ = true;
ESP_LOGI(TAG, "ENS160 readings not available yet - Warming up requires 3 minutes");
this->send_env_data_();
}
return;
case INVALID_OUTPUT:
ESP_LOGE(TAG, "ENS160 Invalid Status - No Invalid Output");
this->status_set_warning();
return;
}
// read new data
uint16_t data_eco2;
if (!this->read_bytes(ENS160_REG_DATA_ECO2, reinterpret_cast<uint8_t *>(&data_eco2), 2)) {
ESP_LOGW(TAG, "Error reading eCO2 data register");
this->status_set_warning();
return;
}
if (this->co2_ != nullptr) {
this->co2_->publish_state(data_eco2);
}
uint16_t data_tvoc;
if (!this->read_bytes(ENS160_REG_DATA_TVOC, reinterpret_cast<uint8_t *>(&data_tvoc), 2)) {
ESP_LOGW(TAG, "Error reading TVOC data register");
this->status_set_warning();
return;
}
if (this->tvoc_ != nullptr) {
this->tvoc_->publish_state(data_tvoc);
}
uint8_t data_aqi;
if (!this->read_byte(ENS160_REG_DATA_AQI, &data_aqi)) {
ESP_LOGW(TAG, "Error reading AQI data register");
this->status_set_warning();
return;
}
if (this->aqi_ != nullptr) {
// remove reserved bits, just in case they are used in future
data_aqi = ENS160_DATA_AQI & data_aqi;
this->aqi_->publish_state(data_aqi);
}
this->status_clear_warning();
// set temperature and humidity compensation data
this->send_env_data_();
}
void ENS160Component::send_env_data_() {
if (this->temperature_ == nullptr && this->humidity_ == nullptr)
return;
float temperature = NAN;
if (this->temperature_ != nullptr)
temperature = this->temperature_->state;
if (std::isnan(temperature) || temperature < -40.0f || temperature > 85.0f) {
ESP_LOGW(TAG, "Invalid external temperature - compensation values not updated");
return;
} else {
ESP_LOGV(TAG, "External temperature compensation: %.1f°C", temperature);
}
float humidity = NAN;
if (this->humidity_ != nullptr)
humidity = this->humidity_->state;
if (std::isnan(humidity) || humidity < 0.0f || humidity > 100.0f) {
ESP_LOGW(TAG, "Invalid external humidity - compensation values not updated");
return;
} else {
ESP_LOGV(TAG, "External humidity compensation: %.1f%%", humidity);
}
uint16_t t = (uint16_t) ((temperature + 273.15f) * 64.0f);
uint16_t h = (uint16_t) (humidity * 512.0f);
uint8_t data[4];
data[0] = t & 0xff;
data[1] = (t >> 8) & 0xff;
data[2] = h & 0xff;
data[3] = (h >> 8) & 0xff;
if (!this->write_bytes(ENS160_REG_TEMP_IN, data, 4)) {
ESP_LOGE(TAG, "Error writing compensation values");
this->status_set_warning();
return;
}
}
void ENS160Component::dump_config() {
ESP_LOGCONFIG(TAG, "ENS160:");
switch (this->error_code_) {
case COMMUNICATION_FAILED:
ESP_LOGE(TAG, "Communication failed! Is the sensor connected?");
break;
case READ_FAILED:
ESP_LOGE(TAG, "Error reading from register");
break;
case WRITE_FAILED:
ESP_LOGE(TAG, "Error writing to register");
break;
case INVALID_ID:
ESP_LOGE(TAG, "Sensor reported an invalid ID. Is this a ENS160?");
break;
case VALIDITY_INVALID:
ESP_LOGE(TAG, "Invalid Device Status - No valid output");
break;
case STD_OPMODE_FAILED:
ESP_LOGE(TAG, "Device failed to achieve Standard Operating Mode");
break;
case NONE:
ESP_LOGD(TAG, "Setup successful");
break;
}
ESP_LOGI(TAG, "Firmware Version: %d.%d.%d", this->firmware_ver_major_, this->firmware_ver_minor_,
this->firmware_ver_build_);
LOG_I2C_DEVICE(this);
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "CO2 Sensor:", this->co2_);
LOG_SENSOR(" ", "TVOC Sensor:", this->tvoc_);
LOG_SENSOR(" ", "AQI Sensor:", this->aqi_);
if (this->temperature_ != nullptr && this->humidity_ != nullptr) {
LOG_SENSOR(" ", " Temperature Compensation:", this->temperature_);
LOG_SENSOR(" ", " Humidity Compensation:", this->humidity_);
} else {
ESP_LOGCONFIG(TAG, " Compensation: Not configured");
}
}
} // namespace ens160
} // namespace esphome

View file

@ -0,0 +1,60 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/i2c/i2c.h"
namespace esphome {
namespace ens160 {
class ENS160Component : public PollingComponent, public i2c::I2CDevice, public sensor::Sensor {
public:
void set_co2(sensor::Sensor *co2) { co2_ = co2; }
void set_tvoc(sensor::Sensor *tvoc) { tvoc_ = tvoc; }
void set_aqi(sensor::Sensor *aqi) { aqi_ = aqi; }
void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; }
void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; }
void setup() override;
void update() override;
void dump_config() override;
float get_setup_priority() const override { return setup_priority::DATA; }
protected:
void send_env_data_();
enum ErrorCode {
NONE = 0,
COMMUNICATION_FAILED,
INVALID_ID,
VALIDITY_INVALID,
READ_FAILED,
WRITE_FAILED,
STD_OPMODE_FAILED,
} error_code_{NONE};
enum ValidityFlag {
NORMAL_OPERATION = 0,
WARMING_UP,
INITIAL_STARTUP,
INVALID_OUTPUT,
} validity_flag_;
bool warming_up_{false};
bool initial_startup_{false};
uint8_t firmware_ver_major_{0};
uint8_t firmware_ver_minor_{0};
uint8_t firmware_ver_build_{0};
sensor::Sensor *co2_{nullptr};
sensor::Sensor *tvoc_{nullptr};
sensor::Sensor *aqi_{nullptr};
sensor::Sensor *humidity_{nullptr};
sensor::Sensor *temperature_{nullptr};
};
} // namespace ens160
} // namespace esphome

View file

@ -0,0 +1,88 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import i2c, sensor
from esphome.const import (
CONF_ECO2,
CONF_HUMIDITY,
CONF_ID,
CONF_TEMPERATURE,
CONF_TVOC,
DEVICE_CLASS_AQI,
DEVICE_CLASS_CARBON_DIOXIDE,
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
ICON_CHEMICAL_WEAPON,
ICON_MOLECULE_CO2,
ICON_RADIATOR,
STATE_CLASS_MEASUREMENT,
UNIT_PARTS_PER_BILLION,
UNIT_PARTS_PER_MILLION,
)
CODEOWNERS = ["@vincentscode"]
DEPENDENCIES = ["i2c"]
ens160_ns = cg.esphome_ns.namespace("ens160")
ENS160Component = ens160_ns.class_(
"ENS160Component", cg.PollingComponent, i2c.I2CDevice, sensor.Sensor
)
CONF_AQI = "aqi"
CONF_COMPENSATION = "compensation"
UNIT_INDEX = "index"
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(ENS160Component),
cv.Required(CONF_ECO2): sensor.sensor_schema(
unit_of_measurement=UNIT_PARTS_PER_MILLION,
icon=ICON_MOLECULE_CO2,
accuracy_decimals=0,
device_class=DEVICE_CLASS_CARBON_DIOXIDE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Required(CONF_TVOC): sensor.sensor_schema(
unit_of_measurement=UNIT_PARTS_PER_BILLION,
icon=ICON_RADIATOR,
accuracy_decimals=0,
device_class=DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Required(CONF_AQI): sensor.sensor_schema(
unit_of_measurement=UNIT_INDEX,
icon=ICON_CHEMICAL_WEAPON,
accuracy_decimals=0,
device_class=DEVICE_CLASS_AQI,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_COMPENSATION): cv.Schema(
{
cv.Required(CONF_TEMPERATURE): cv.use_id(sensor.Sensor),
cv.Required(CONF_HUMIDITY): cv.use_id(sensor.Sensor),
}
),
}
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x53))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await i2c.register_i2c_device(var, config)
sens = await sensor.new_sensor(config[CONF_ECO2])
cg.add(var.set_co2(sens))
sens = await sensor.new_sensor(config[CONF_TVOC])
cg.add(var.set_tvoc(sens))
sens = await sensor.new_sensor(config[CONF_AQI])
cg.add(var.set_aqi(sens))
if CONF_COMPENSATION in config:
compensation_config = config[CONF_COMPENSATION]
sens = await cg.get_variable(compensation_config[CONF_TEMPERATURE])
cg.add(var.set_temperature(sens))
sens = await cg.get_variable(compensation_config[CONF_HUMIDITY])
cg.add(var.set_humidity(sens))

View file

@ -93,7 +93,8 @@ bool Nextion::check_connect_() {
connect_info.push_back(response.substr(start, end - start)); connect_info.push_back(response.substr(start, end - start));
} }
if (connect_info.size() == 7) { this->is_detected_ = (connect_info.size() == 7);
if (this->is_detected_) {
ESP_LOGN(TAG, "Received connect_info %zu", connect_info.size()); ESP_LOGN(TAG, "Received connect_info %zu", connect_info.size());
this->device_model_ = connect_info[2]; this->device_model_ = connect_info[2];

View file

@ -48,10 +48,12 @@ class NextionBase {
bool is_sleeping() { return this->is_sleeping_; } bool is_sleeping() { return this->is_sleeping_; }
bool is_setup() { return this->is_setup_; } bool is_setup() { return this->is_setup_; }
bool is_detected() { return this->is_detected_; }
protected: protected:
bool is_setup_ = false; bool is_setup_ = false;
bool is_sleeping_ = false; bool is_sleeping_ = false;
bool is_detected_ = false;
}; };
} // namespace nextion } // namespace nextion

View file

@ -75,12 +75,13 @@ def validate_rx_pin(value):
def validate_invert_esp32(config): def validate_invert_esp32(config):
if ( if (
CORE.is_esp32 CORE.is_esp32
and CORE.using_arduino
and CONF_TX_PIN in config and CONF_TX_PIN in config
and CONF_RX_PIN in config and CONF_RX_PIN in config
and config[CONF_TX_PIN][CONF_INVERTED] != config[CONF_RX_PIN][CONF_INVERTED] and config[CONF_TX_PIN][CONF_INVERTED] != config[CONF_RX_PIN][CONF_INVERTED]
): ):
raise cv.Invalid( raise cv.Invalid(
"Different invert values for TX and RX pin are not (yet) supported for ESP32." "Different invert values for TX and RX pin are not supported for ESP32 when using Arduino."
) )
return config return config

View file

@ -278,10 +278,13 @@ std::string str_snake_case(const std::string &str) {
return result; return result;
} }
std::string str_sanitize(const std::string &str) { std::string str_sanitize(const std::string &str) {
std::string out; std::string out = str;
std::copy_if(str.begin(), str.end(), std::back_inserter(out), [](const char &c) { std::replace_if(
return c == '-' || c == '_' || (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); out.begin(), out.end(),
}); [](const char &c) {
return !(c == '-' || c == '_' || (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
},
'_');
return out; return out;
} }
std::string str_snprintf(const char *fmt, size_t len, ...) { std::string str_snprintf(const char *fmt, size_t len, ...) {

View file

@ -27,6 +27,7 @@ import tornado.process
import tornado.queues import tornado.queues
import tornado.web import tornado.web
import tornado.websocket import tornado.websocket
import tornado.httputil
import yaml import yaml
from tornado.log import access_log from tornado.log import access_log
@ -136,7 +137,15 @@ def websocket_method(name):
@websocket_class @websocket_class
class EsphomeCommandWebSocket(tornado.websocket.WebSocketHandler): class EsphomeCommandWebSocket(tornado.websocket.WebSocketHandler):
def __init__(self, application, request, **kwargs): """Base class for ESPHome websocket commands."""
def __init__(
self,
application: tornado.web.Application,
request: tornado.httputil.HTTPServerRequest,
**kwargs: Any,
) -> None:
"""Initialize the websocket."""
super().__init__(application, request, **kwargs) super().__init__(application, request, **kwargs)
self._proc = None self._proc = None
self._queue = None self._queue = None
@ -145,6 +154,12 @@ class EsphomeCommandWebSocket(tornado.websocket.WebSocketHandler):
# use Popen() with a reading thread instead # use Popen() with a reading thread instead
self._use_popen = os.name == "nt" self._use_popen = os.name == "nt"
def open(self, *args: str, **kwargs: str) -> None:
"""Handle new WebSocket connection."""
# Ensure messages from the subprocess are sent immediately
# to avoid a 200-500ms delay when nodelay is not set.
self.set_nodelay(True)
@authenticated @authenticated
async def on_message( # pylint: disable=invalid-overridden-method async def on_message( # pylint: disable=invalid-overridden-method
self, message: str self, message: str

View file

@ -357,6 +357,9 @@ def snake_case(value):
return value.replace(" ", "_").lower() return value.replace(" ", "_").lower()
_DISALLOWED_CHARS = re.compile(r"[^a-zA-Z0-9_]")
def sanitize(value): def sanitize(value):
"""Same behaviour as `helpers.cpp` method `str_sanitize`.""" """Same behaviour as `helpers.cpp` method `str_sanitize`."""
return re.sub("[^-_0-9a-zA-Z]", r"", value) return _DISALLOWED_CHARS.sub("_", value)

View file

@ -10,7 +10,7 @@ platformio==6.1.11 # When updating platformio, also update Dockerfile
esptool==4.6.2 esptool==4.6.2
click==8.1.7 click==8.1.7
esphome-dashboard==20231107.0 esphome-dashboard==20231107.0
aioesphomeapi==18.5.5 aioesphomeapi==18.5.9
zeroconf==0.127.0 zeroconf==0.127.0
python-magic==0.4.27 python-magic==0.4.27

View file

@ -6,12 +6,6 @@ cd "$(dirname "$0")/.."
set -x set -x
esphome compile tests/test1.yaml for f in ./tests/test*.yaml; do
esphome compile tests/test2.yaml esphome compile $f
esphome compile tests/test3.yaml done
esphome compile tests/test3.1.yaml
esphome compile tests/test4.yaml
esphome compile tests/test5.yaml
esphome compile tests/test6.yaml
esphome compile tests/test7.yaml
esphome compile tests/test8.yaml

View file

@ -225,6 +225,13 @@ sensor:
name: "ADE7953 Reactive Power B" name: "ADE7953 Reactive Power B"
update_interval: 1s update_interval: 1s
- platform: ens160
eco2:
name: "ENS160 eCO2"
tvoc:
name: "ENS160 Total Volatile Organic Compounds"
aqi:
name: "ENS160 Air Quality Index"
- platform: tmp102 - platform: tmp102
name: TMP102 Temperature name: TMP102 Temperature
- platform: hm3301 - platform: hm3301
@ -424,7 +431,6 @@ switch:
direction: BACKWARD direction: BACKWARD
id: test_motor id: test_motor
custom_component: custom_component:
lambda: |- lambda: |-
auto s = new CustomComponent(); auto s = new CustomComponent();
@ -613,7 +619,6 @@ mcp23017:
mcp23008: mcp23008:
id: mcp23008_hub id: mcp23008_hub
light: light:
- platform: hbridge - platform: hbridge
name: Icicle Lights name: Icicle Lights
@ -633,7 +638,6 @@ ttp229_bsf:
sdo_pin: D2 sdo_pin: D2
scl_pin: D1 scl_pin: D1
display: display:
- platform: max7219digit - platform: max7219digit
cs_pin: GPIO15 cs_pin: GPIO15
@ -645,7 +649,6 @@ display:
lambda: |- lambda: |-
it.printdigit("hello"); it.printdigit("hello");
http_request: http_request:
useragent: esphome/device useragent: esphome/device
timeout: 10s timeout: 10s

View file

@ -480,7 +480,6 @@ sensor:
name: PZEMDC Power name: PZEMDC Power
energy: energy:
name: PZEMDC Energy name: PZEMDC Energy
- platform: pmsx003 - platform: pmsx003
uart_id: uart_9 uart_id: uart_9
type: PMSX003 type: PMSX003

View file

@ -41,7 +41,9 @@ uart:
rx_pin: 3 rx_pin: 3
baud_rate: 9600 baud_rate: 9600
- id: uart_2 - id: uart_2
tx_pin: 17 tx_pin:
number: 17
inverted: true
rx_pin: 16 rx_pin: 16
baud_rate: 19200 baud_rate: 19200

View file

@ -258,9 +258,9 @@ def test_snake_case(text, expected):
"text, expected", "text, expected",
( (
("foo_bar", "foo_bar"), ("foo_bar", "foo_bar"),
('!"§$%&/()=?foo_bar', "foo_bar"), ('!"§$%&/()=?foo_bar', "___________foo_bar"),
('foo_!"§$%&/()=?bar', "foo_bar"), ('foo_!"§$%&/()=?bar', "foo____________bar"),
('foo_bar!"§$%&/()=?', "foo_bar"), ('foo_bar!"§$%&/()=?', "foo_bar___________"),
), ),
) )
def test_sanitize(text, expected): def test_sanitize(text, expected):