Merge remote-tracking branch 'upstream/dev' into neutral-ble-client

# Conflicts:
#	esphome/components/ble_client/__init__.py
This commit is contained in:
J. Nick Koston
2026-10-10 09:25:46 -10:00
2360 changed files with 19556 additions and 6491 deletions
+13
View File
@@ -0,0 +1,13 @@
adalight:
light:
- platform: neopixelbus
id: adalight_light
name: Neopixelbus Light
method: esp8266_uart
num_leds: 5
pin: 2
type: GRBW
variant: SK6812
effects:
- adalight:
@@ -0,0 +1,3 @@
packages:
uart_115200: !include ../../test_build_components/common/uart_115200/esp8266-ard.yaml
adalight: !include common.yaml
@@ -1,23 +0,0 @@
sensor:
- platform: adc
pin: VDDHDIV5
name: "VDDH Voltage"
update_interval: 5sec
filters:
- multiply: 5
- platform: adc
pin: VDD
name: "VDD Voltage"
update_interval: 5sec
- platform: adc
pin: AIN0
name: "AIN0 Voltage"
update_interval: 5sec
- platform: adc
pin: P0.03
name: "AIN1 Voltage"
update_interval: 5sec
- platform: adc
name: "AIN2 Voltage"
update_interval: 5sec
pin: 4
@@ -1,3 +1,5 @@
# Config only: every LibreTiny chip compiles the same ADC code, covered by the
# ln882x-ard build; this checks the BK72xx analog pin names.
sensor:
- id: adc_my_sensor
platform: adc
@@ -1,3 +1,5 @@
# Config only: the P4 compiles the same ADC code as the esp32-s3-idf build
# (curve fitting calibration), only the pin table differs.
sensor:
- id: adc_my_sensor
platform: adc
@@ -1,3 +1,6 @@
# Config only: the S2 shares the C2's line fitting path (no default_vref) and
# the original ESP32's ADC init (no clk_src); both are compiled by those
# builds, only the S2 pin table differs.
sensor:
- id: adc_my_sensor
platform: adc
+1
View File
@@ -1,6 +1,7 @@
sensor:
- platform: ags10
id: ags10_1
i2c_id: i2c_bus
tvoc:
name: AGS10 TVOC
update_interval: 60s
+20
View File
@@ -1,6 +1,12 @@
from pathlib import Path
import esphome.codegen as cg
from esphome.const import CONF_ESPHOME, CONF_INCLUDES
from esphome.core import CORE
from tests.testing_helpers import ComponentManifestOverride
TESTS_DIR = Path(__file__).parent
def override_manifest(manifest: ComponentManifestOverride) -> None:
# USE_API compiles every api source, so emit what they need. No socket
@@ -8,6 +14,20 @@ def override_manifest(manifest: ComponentManifestOverride) -> None:
async def to_code_testing(config):
cg.add_define("USE_API")
cg.add_define("USE_API_PLAINTEXT")
# Linked wizard inputs only exist next to homeassistant entities, which need this
cg.add_define("USE_API_HOMEASSISTANT_STATES")
# test_wizard.cpp supplies the tables that codegen emits for a real build;
# it is only compiled when api's own tests are, not when api is a dependency
if any(Path(p) == TESTS_DIR for p in CORE.config[CONF_ESPHOME][CONF_INCLUDES]):
for define in (
"USE_API_WIZARD",
"USE_API_WIZARD_INPUTS",
"USE_API_WIZARD_LINKED_INPUTS",
"USE_API_WIZARD_STANDALONE_INPUTS",
):
cg.add_define(define)
cg.add_define("API_WIZARD_DATA_SIZE", 200)
cg.add_define("API_WIZARD_INPUT_COUNT", 2)
cg.add_define("API_MAX_SEND_QUEUE", 8)
cg.add_define("MAX_API_CONNECTIONS", 1)
cg.add_define("USE_SOCKET_IMPL_BSD_SOCKETS")
+108
View File
@@ -0,0 +1,108 @@
api:
homeassistant_services: true
wizard:
pages:
- title: Features
description: Choose which features to enable
entities:
- id: feature_enable_switch
description: "[%key:component::example::feature::enable_switch%]"
- id: guest_mode_switch
description: Guest Mode
- id: plain_switch
- title: Sources
inputs:
- entity: outdoor_temp
description: Outdoor temperature
target:
entity:
- integration: met
domain: sensor
device_class: [temperature, humidity]
supported_features: weather.WeatherEntityFeature.FORECAST_DAILY
- domain: [sensor, number]
- entity: default_temp
- entity: door_open
- entity: house_mode
- entity: target_temp
- entity: ha_light_switch
target:
entity:
- domain: light
- entity: ha_text
- entity: ha_select
target:
entity:
- domain: input_select
- entity: ha_button
- id: weather_input
description: Weather source
target:
entity:
- domain: weather
- id: unset_input
switch:
- platform: template
id: feature_enable_switch
name: Feature Enable
optimistic: true
- platform: template
id: guest_mode_switch
name: Guest Mode
optimistic: true
- platform: template
id: plain_switch
name: Plain Switch
optimistic: true
- platform: homeassistant
id: ha_light_switch
sensor:
- platform: homeassistant
id: outdoor_temp
- platform: homeassistant
id: default_temp
- platform: homeassistant
id: fixed_temp
entity_id: sensor.fixed_temperature
binary_sensor:
- platform: homeassistant
id: door_open
text_sensor:
- platform: homeassistant
id: house_mode
number:
- platform: homeassistant
id: target_temp
text:
- platform: homeassistant
id: ha_text
select:
- platform: homeassistant
id: ha_select
button:
- platform: homeassistant
id: ha_button
- platform: homeassistant
id: fixed_button
entity_id: button.restart
interval:
- interval: 1h
then:
- if:
condition:
api.wizard.input_is_set: weather_input
then:
- homeassistant.action:
action: weather.get_forecasts
data:
type: daily
entity_id: !lambda 'return id(weather_input).entity_id().c_str();'
@@ -0,0 +1,6 @@
packages:
wizard: !include common-wizard.yaml
wifi:
ssid: MySSID
password: password1
@@ -0,0 +1,6 @@
packages:
wizard: !include common-wizard.yaml
wifi:
ssid: MySSID
password: password1
@@ -0,0 +1,4 @@
packages:
wizard: !include common-wizard.yaml
network:
@@ -0,0 +1,88 @@
#include <gtest/gtest.h>
#include <cstdint>
#include <vector>
#include "esphome/components/api/api_buffer.h"
#include "esphome/components/api/proto.h"
namespace esphome::api::testing {
// Sub-message whose body is raw bytes, so the body size is set directly by the test.
struct BlobMessage {
const uint8_t *data;
uint32_t len;
static uint8_t *encode_msg(const void *self, uint8_t *pos PROTO_ENCODE_DEBUG_PARAM) {
const auto &msg = *static_cast<const BlobMessage *>(self);
// An empty vector's data() may be null, and memcpy needs a valid source even for zero bytes
if (msg.len == 0)
return pos;
return ProtoEncode::encode_raw(pos PROTO_ENCODE_DEBUG_ARG, msg.data, msg.len);
}
static uint32_t calc_size_msg(const void *self) { return static_cast<const BlobMessage *>(self)->len; }
};
static void append_varint(std::vector<uint8_t> &out, uint32_t value) {
while (value > 0x7F) {
out.push_back(static_cast<uint8_t>(value | 0x80));
value >>= 7;
}
out.push_back(static_cast<uint8_t>(value));
}
static std::vector<uint8_t> make_body(uint32_t len) {
std::vector<uint8_t> body(len);
for (uint32_t i = 0; i < len; i++)
body[i] = static_cast<uint8_t>(i * 7 + 1);
return body;
}
static std::vector<uint8_t> expected_field(uint32_t field_id, const std::vector<uint8_t> &body) {
std::vector<uint8_t> out;
append_varint(out, (field_id << 3) | 2);
append_varint(out, body.size());
out.insert(out.end(), body.begin(), body.end());
return out;
}
// Encodes into a buffer of exactly the expected size, so any overrun trips ASan or the debug bounds check.
template<bool OPTIONAL> static void verify(uint32_t field_id, uint32_t body_len) {
std::vector<uint8_t> body = make_body(body_len);
std::vector<uint8_t> expected = expected_field(field_id, body);
if (OPTIONAL && body_len == 0)
expected.clear();
BlobMessage msg{body.data(), body_len};
APIBuffer buf;
ASSERT_TRUE(buf.resize(expected.empty() ? 1 : expected.size()));
uint8_t *pos = buf.data();
#ifdef ESPHOME_DEBUG_API
uint8_t *proto_debug_end_ = buf.data() + buf.size();
#endif
uint8_t *end;
if constexpr (OPTIONAL) {
end = ProtoEncode::encode_optional_sub_message(pos PROTO_ENCODE_DEBUG_ARG, field_id, msg);
} else {
end = ProtoEncode::encode_sub_message(pos PROTO_ENCODE_DEBUG_ARG, field_id, msg);
}
ASSERT_EQ(static_cast<size_t>(end - buf.data()), expected.size()) << "field " << field_id << " body " << body_len;
EXPECT_EQ(std::vector<uint8_t>(buf.data(), end), expected) << "field " << field_id << " body " << body_len;
}
TEST(ProtoSubMessage, OneByteTag) { verify<false>(4, 10); }
TEST(ProtoSubMessage, TwoByteTag) { verify<false>(20, 10); }
TEST(ProtoSubMessage, EmptyBody) { verify<false>(20, 0); }
TEST(ProtoSubMessage, LongestOneByteLength) { verify<false>(20, 127); }
// The length outgrows its reserved byte, so the body is moved forward
TEST(ProtoSubMessage, TwoByteLength) {
verify<false>(20, 128);
verify<false>(25, 200);
}
TEST(ProtoSubMessage, ThreeByteLength) { verify<false>(4, 20000); }
TEST(ProtoOptionalSubMessage, EmptyIsSkipped) { verify<true>(22, 0); }
TEST(ProtoOptionalSubMessage, OneByteTag) { verify<true>(1, 10); }
TEST(ProtoOptionalSubMessage, TwoByteTagAndLength) { verify<true>(22, 200); }
} // namespace esphome::api::testing
+148
View File
@@ -0,0 +1,148 @@
#ifdef USE_HOST
#include <gtest/gtest.h>
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>
#include "esphome/components/api/api_buffer.h"
#include "esphome/components/api/api_pb2.h"
#include "esphome/components/api/api_wizard.h"
#include "esphome/components/api/proto.h"
namespace esphome::api {
// RAM buffers of the inputs, as codegen defines them
static char *wizard_input_weather() {
static char buffer[WIZARD_ENTITY_ID_BUFFER_SIZE] = "sensor.default";
return buffer;
}
static char *wizard_input_other() {
static char buffer[WIZARD_ENTITY_ID_BUFFER_SIZE] = "";
return buffer;
}
static constexpr uint32_t WEATHER_KEY = 0x0a0b0c0d;
static constexpr uint32_t OTHER_KEY = 0x11223344;
// The same shapes the generated code emits. The data is 200 bytes (API_WIZARD_DATA_SIZE), so its length takes two
// bytes as a varint.
const uint8_t API_WIZARD_DATA[API_WIZARD_DATA_SIZE] = {
0x03, 0x0a, 0x11, 0x18, 0x1f, 0x26, 0x2d, 0x34, 0x3b, 0x42, 0x49, 0x50, 0x57, 0x5e, 0x65, 0x6c, 0x73, 0x7a, 0x81,
0x88, 0x8f, 0x96, 0x9d, 0xa4, 0xab, 0xb2, 0xb9, 0xc0, 0xc7, 0xce, 0xd5, 0xdc, 0xe3, 0xea, 0xf1, 0xf8, 0xff, 0x06,
0x0d, 0x14, 0x1b, 0x22, 0x29, 0x30, 0x37, 0x3e, 0x45, 0x4c, 0x53, 0x5a, 0x61, 0x68, 0x6f, 0x76, 0x7d, 0x84, 0x8b,
0x92, 0x99, 0xa0, 0xa7, 0xae, 0xb5, 0xbc, 0xc3, 0xca, 0xd1, 0xd8, 0xdf, 0xe6, 0xed, 0xf4, 0xfb, 0x02, 0x09, 0x10,
0x17, 0x1e, 0x25, 0x2c, 0x33, 0x3a, 0x41, 0x48, 0x4f, 0x56, 0x5d, 0x64, 0x6b, 0x72, 0x79, 0x80, 0x87, 0x8e, 0x95,
0x9c, 0xa3, 0xaa, 0xb1, 0xb8, 0xbf, 0xc6, 0xcd, 0xd4, 0xdb, 0xe2, 0xe9, 0xf0, 0xf7, 0xfe, 0x05, 0x0c, 0x13, 0x1a,
0x21, 0x28, 0x2f, 0x36, 0x3d, 0x44, 0x4b, 0x52, 0x59, 0x60, 0x67, 0x6e, 0x75, 0x7c, 0x83, 0x8a, 0x91, 0x98, 0x9f,
0xa6, 0xad, 0xb4, 0xbb, 0xc2, 0xc9, 0xd0, 0xd7, 0xde, 0xe5, 0xec, 0xf3, 0xfa, 0x01, 0x08, 0x0f, 0x16, 0x1d, 0x24,
0x2b, 0x32, 0x39, 0x40, 0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x7f, 0x86, 0x8d, 0x94, 0x9b, 0xa2, 0xa9,
0xb0, 0xb7, 0xbe, 0xc5, 0xcc, 0xd3, 0xda, 0xe1, 0xe8, 0xef, 0xf6, 0xfd, 0x04, 0x0b, 0x12, 0x19, 0x20, 0x27, 0x2e,
0x35, 0x3c, 0x43, 0x4a, 0x51, 0x58, 0x5f, 0x66, 0x6d, 0x74};
const WizardInputEntry API_WIZARD_INPUTS[API_WIZARD_INPUT_COUNT] = {
{WEATHER_KEY, wizard_input_weather()},
{OTHER_KEY, wizard_input_other()},
};
using Bytes = std::vector<uint8_t>;
static Bytes encode(const ProtoMessage &msg, uint32_t (*calc)(const void *), ProtoEncodeFn enc) {
APIBuffer buffer;
uint32_t size = calc(&msg);
EXPECT_TRUE(buffer.resize(size));
#ifdef ESPHOME_DEBUG_API
uint8_t *proto_debug_end_ = buffer.data() + buffer.size();
#endif
uint8_t *end = enc(&msg, buffer.data() PROTO_ENCODE_DEBUG_ARG);
EXPECT_EQ(static_cast<size_t>(end - buffer.data()), size);
return Bytes(buffer.data(), buffer.data() + size);
}
TEST(DeviceWizard, ResponseSendsTheDataUnchanged) {
DeviceWizardResponse resp;
resp.data = API_WIZARD_DATA;
resp.data_len = API_WIZARD_DATA_SIZE;
// Field 1, length delimited, then the 200 byte length as a two byte varint, then the data
Bytes expected{0x0a, 0xc8, 0x01};
expected.insert(expected.end(), API_WIZARD_DATA, API_WIZARD_DATA + API_WIZARD_DATA_SIZE);
EXPECT_EQ(encode(resp, &DeviceWizardResponse::calc_size_msg, &DeviceWizardResponse::encode_msg), expected);
}
TEST(DeviceWizard, ShortDataHasAOneByteLength) {
DeviceWizardResponse resp;
resp.data = API_WIZARD_DATA;
resp.data_len = 3;
Bytes expected{0x0a, 0x03, API_WIZARD_DATA[0], API_WIZARD_DATA[1], API_WIZARD_DATA[2]};
EXPECT_EQ(encode(resp, &DeviceWizardResponse::calc_size_msg, &DeviceWizardResponse::encode_msg), expected);
}
TEST(DeviceWizard, NoDataEncodesNothing) {
DeviceWizardResponse resp;
EXPECT_EQ(resp.calculate_size(), 0u);
EXPECT_TRUE(encode(resp, &DeviceWizardResponse::calc_size_msg, &DeviceWizardResponse::encode_msg).empty());
}
TEST(DeviceWizard, CapabilitiesAnnounceTheWizard) {
DeviceCapabilitiesResponse resp;
EXPECT_TRUE(
encode(resp, &DeviceCapabilitiesResponse::calc_size_msg, &DeviceCapabilitiesResponse::encode_msg).empty());
resp.wizard.configured = true;
// Field 5 (the wizard), length delimited, holding field 1 (configured) set to 1
EXPECT_EQ(encode(resp, &DeviceCapabilitiesResponse::calc_size_msg, &DeviceCapabilitiesResponse::encode_msg),
(Bytes{0x2a, 0x02, 0x08, 0x01}));
}
// A request for the input with the given key and entity id
static const char *set_input(uint32_t key, const char *entity_id) {
WizardInputSetRequest request;
request.key = key;
request.entity_id = StringRef(entity_id);
return wizard_set_input(request);
}
TEST(DeviceWizard, SetInputStoresTheEntityIdInTheBuffer) {
EXPECT_EQ(set_input(OTHER_KEY, "sensor.outdoor"), wizard_input_other());
EXPECT_STREQ(wizard_input_other(), "sensor.outdoor");
// A shorter id replaces a longer one completely
EXPECT_EQ(set_input(OTHER_KEY, "light.a"), wizard_input_other());
EXPECT_STREQ(wizard_input_other(), "light.a");
// The longest valid id fills the buffer
std::string longest = "sensor." + std::string(WIZARD_ENTITY_ID_BUFFER_SIZE - 1 - 7, 'x');
EXPECT_EQ(set_input(OTHER_KEY, longest.c_str()), wizard_input_other());
EXPECT_EQ(std::string(wizard_input_other()), longest);
}
TEST(DeviceWizard, SetInputFindsTheInputByItsKey) {
EXPECT_EQ(set_input(WEATHER_KEY, "weather.home"), wizard_input_weather());
EXPECT_STREQ(wizard_input_weather(), "weather.home");
EXPECT_EQ(set_input(OTHER_KEY, "weather.away"), wizard_input_other());
EXPECT_STREQ(wizard_input_weather(), "weather.home");
}
TEST(DeviceWizard, SetInputIgnoresWhatIsNotAnEntityId) {
std::string before = wizard_input_weather();
EXPECT_EQ(set_input(WEATHER_KEY, ""), nullptr);
EXPECT_EQ(set_input(WEATHER_KEY, "nodot"), nullptr);
std::string too_long = "sensor." + std::string(WIZARD_ENTITY_ID_BUFFER_SIZE, 'x');
EXPECT_EQ(set_input(WEATHER_KEY, too_long.c_str()), nullptr);
EXPECT_EQ(set_input(0xdeadbeef, "sensor.a"), nullptr);
EXPECT_EQ(std::string(wizard_input_weather()), before);
}
TEST(DeviceWizard, StandaloneInputReadsTheBufferTheWizardWrites) {
strcpy(wizard_input_other(), "");
WizardInput input(wizard_input_other());
EXPECT_FALSE(input.has_entity_id());
EXPECT_TRUE(input.entity_id().empty());
ASSERT_EQ(set_input(OTHER_KEY, "weather.home"), wizard_input_other());
EXPECT_TRUE(input.has_entity_id());
EXPECT_EQ(input.entity_id(), "weather.home");
}
} // namespace esphome::api
#endif // USE_HOST
@@ -0,0 +1,6 @@
media_source:
- platform: audio_http
id: audio_http_ca_source
ca_certificate_path: $component_dir/test_ca.pem
<<: !include common.yaml
@@ -0,0 +1,13 @@
# http_request requires a network component; audio_http's common.yaml declares none.
wifi:
ssid: MySSID
password: password1
http_request:
ca_certificate_path: $component_dir/test_ca.pem
media_source:
- platform: audio_http
id: audio_http_inherit_ca_source
<<: !include common.yaml
+10
View File
@@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBkTCB+wIJAKHBfpegPjMCMA0GCSqGSIb3DQEBCwUAMBExDzANBgNVBAMMBnVu
dXNlZDAeFw0yNDAxMDEwMDAwMDBaFw0yNTAxMDEwMDAwMDBaMBExDzANBgNVBAMM
BnVudXNlZDBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQC5mMUB1hOgLmlnXtsvcGMP
XkhAqZaR0dDPW5OS8VEopWLJCX9Y0cvNCqiDI8cnP8pP8XJGU1hGLvA5PJzWnWZz
AgMBAAGjUzBRMB0GA1UdDgQWBBR5oQ9KqFeZOdBuAJrXxEP0dqzPtTAfBgNVHSME
GDAWgBR5oQ9KqFeZOdBuAJrXxEP0dqzPtTAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
SIb3DQEBCwUAA0EAKqZFf6+f8FPDbKyPCpssquojgn7fEXqr/I/yz0R5CowGdMms
H3WH3aKP4lLSHdPTBtfIoJi3gEIZjFxp3S1TWw==
-----END CERTIFICATE-----
+1
View File
@@ -61,6 +61,7 @@ sensor:
motion:
- platform: bmi270
id: bmi270_motion
i2c_id: i2c_bus
# Accelerometer full-scale range: 2G | 4G | 8G | 16G
accelerometer_range: 4G
@@ -0,0 +1,34 @@
#ifdef USE_HOST
#include <gtest/gtest.h>
#include <cstdint>
#include <vector>
#include "esphome/components/bytebuffer/bytebuffer.h"
namespace esphome::bytebuffer {
// The expressions esp32_ble_server emits for typed constant values, and the bytes they produce.
// Doubles are written as float literals, so they carry float precision.
TEST(ByteBufferWrapValues, MatchesGeneratedConstants) {
EXPECT_EQ(ByteBuffer::wrap(uint8_t(18), LITTLE).get_data(), (std::vector<uint8_t>{0x12}));
EXPECT_EQ(ByteBuffer::wrap(uint16_t(18), LITTLE).get_data(), (std::vector<uint8_t>{0x12, 0x00}));
EXPECT_EQ(ByteBuffer::wrap(uint16_t(4660), BIG).get_data(), (std::vector<uint8_t>{0x12, 0x34}));
EXPECT_EQ(ByteBuffer::wrap(uint32_t(305419896), LITTLE).get_data(), (std::vector<uint8_t>{0x78, 0x56, 0x34, 0x12}));
EXPECT_EQ(ByteBuffer::wrap(uint64_t(1311768467294899695ULL), BIG).get_data(),
(std::vector<uint8_t>{0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef}));
EXPECT_EQ(ByteBuffer::wrap(int8_t(-5), LITTLE).get_data(), (std::vector<uint8_t>{0xfb}));
EXPECT_EQ(ByteBuffer::wrap(int16_t(-2), BIG).get_data(), (std::vector<uint8_t>{0xff, 0xfe}));
EXPECT_EQ(ByteBuffer::wrap(int32_t(-2), LITTLE).get_data(), (std::vector<uint8_t>{0xfe, 0xff, 0xff, 0xff}));
EXPECT_EQ(ByteBuffer::wrap(int64_t(-2), LITTLE).get_data(),
(std::vector<uint8_t>{0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}));
EXPECT_EQ(ByteBuffer::wrap(float(123.1f), BIG).get_data(), (std::vector<uint8_t>{0x42, 0xf6, 0x33, 0x33}));
EXPECT_EQ(ByteBuffer::wrap(float(0.1f), LITTLE).get_data(), (std::vector<uint8_t>{0xcd, 0xcc, 0xcc, 0x3d}));
EXPECT_EQ(ByteBuffer::wrap(double(0.1f), LITTLE).get_data(),
(std::vector<uint8_t>{0x00, 0x00, 0x00, 0xa0, 0x99, 0x99, 0xb9, 0x3f}));
EXPECT_EQ(ByteBuffer::wrap(double(2.5f), BIG).get_data(),
(std::vector<uint8_t>{0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}));
}
} // namespace esphome::bytebuffer
#endif
+4
View File
@@ -18,6 +18,7 @@ canbus:
lambda: "return x[0] == 0x11;"
then:
logger.log: Truth
- canbus.send: !lambda return x;
- can_id: 0b00000000000000000000001000000
can_id_mask: 0b11111000000000011111111000000
use_extended_id: true
@@ -59,6 +60,9 @@ button:
data: [0, 1, 2]
- canbus.send: "abc"
- canbus.send: [0, 1, 2]
# Identical on purpose: both sends share one constant payload
- canbus.send: [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]
- canbus.send: [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]
- canbus.send: !lambda return {0, 1, 2};
# Test canbus.send with lambda that references a component (function pointer)
- canbus.send: !lambda |-
+4
View File
@@ -30,6 +30,8 @@ cc1101:
then:
- lambda: |-
ESP_LOGD("cc1101", "packet %s freq_offset %.0f Hz rssi %.1f dBm lqi %u", format_hex(x).c_str(), freq_offset, rssi, lqi);
- cc1101.send_packet: !lambda |-
return x;
button:
- platform: template
@@ -42,6 +44,8 @@ button:
- cc1101.reset: transceiver
- cc1101.send_packet:
data: [0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef]
- cc1101.send_packet: [0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef]
- cc1101.send_packet: "ABCDEFGH"
- cc1101.send_packet: !lambda |-
return {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
+8
View File
@@ -1,5 +1,13 @@
sensor:
- platform: cm1106
id: cm1106_sensor
co2:
name: CM1106 CO2 Value
on_value:
then:
- cm1106.abc_enable: cm1106_sensor
- cm1106.abc_disable: cm1106_sensor
update_interval: 15s
automatic_baseline_calibration:
cycle: 7
baseline: 420
@@ -0,0 +1,15 @@
sensor:
- platform: template
id: template_temperature1
lambda: return 0.6;
- platform: template
id: template_temperature2
lambda: return 0.8;
- platform: combination
type: linear
name: Linearly combined temperatures
sources:
- source: template_temperature1
coeffecient: 0.5
- source: template_temperature2
coefficient: 1.5
@@ -0,0 +1,43 @@
#include <gtest/gtest.h>
#include <vector>
#include "esphome/core/automation.h"
namespace esphome::testing {
static const uint8_t PAYLOAD[] = {1, 2, 3};
static std::vector<uint8_t> repeat(int count) { return std::vector<uint8_t>(count, 7); }
TEST(TemplatableBytesTest, VisitStaticTable) {
TemplatableBytes<> bytes;
bytes.set_static(PAYLOAD, sizeof(PAYLOAD));
std::vector<uint8_t> seen;
bytes.visit([&](const uint8_t *data, size_t len) { seen.assign(data, data + len); });
EXPECT_EQ(seen, (std::vector<uint8_t>{1, 2, 3}));
}
TEST(TemplatableBytesTest, VisitWithLargerStackBuffer) {
TemplatableBytes<> bytes;
bytes.set_static(PAYLOAD, sizeof(PAYLOAD));
size_t seen = 0;
bytes.visit<256>([&](const uint8_t *, size_t len) { seen = len; });
EXPECT_EQ(seen, sizeof(PAYLOAD));
}
TEST(TemplatableBytesTest, VisitEmptyStaticTable) {
TemplatableBytes<> bytes;
bytes.set_static(nullptr, 0);
size_t seen = 1;
bytes.visit([&](const uint8_t *, size_t len) { seen = len; });
EXPECT_EQ(seen, 0u);
}
TEST(TemplatableBytesTest, VisitLambdaWithArgument) {
TemplatableBytes<int> bytes;
bytes.set_template(repeat);
std::vector<uint8_t> seen;
bytes.visit([&](const uint8_t *data, size_t len) { seen.assign(data, data + len); }, 4);
EXPECT_EQ(seen, (std::vector<uint8_t>(4, 7)));
}
} // namespace esphome::testing
@@ -0,0 +1,10 @@
datetime:
- platform: template
name: Datetime Trigger Test
id: datetime_trigger_test
type: datetime
optimistic: true
on_value:
- logger.log:
format: "New value %04d-%02d-%02d"
args: [x.year, x.month, x.day_of_month]
@@ -0,0 +1,3 @@
packages:
datetime: !include common.yaml
triggers: !include common-triggers.yaml
+2 -1
View File
@@ -1,4 +1,5 @@
<<: !include common.yaml
packages:
debug: !include common.yaml
sensor:
- platform: debug
+2 -1
View File
@@ -1,4 +1,5 @@
<<: !include common.yaml
packages:
debug: !include common.yaml
esp32:
cpu_frequency: 240MHz
+2 -3
View File
@@ -1,12 +1,11 @@
<<: !include common.yaml
packages:
debug: !include common.yaml
esp32:
cpu_frequency: 240MHz
sensor:
- platform: debug
free:
name: "Heap Free"
psram:
name: "Free PSRAM"
fragmentation:
@@ -1,8 +0,0 @@
<<: !include common.yaml
sensor:
- platform: debug
fragmentation:
name: "Heap Fragmentation"
min_free:
name: "Heap Min Free"
+2 -1
View File
@@ -1,4 +1,5 @@
<<: !include common.yaml
packages:
debug: !include common.yaml
sensor:
- platform: debug
+2 -1
View File
@@ -1 +1,2 @@
<<: !include common.yaml
packages:
debug: !include common.yaml
@@ -1,6 +0,0 @@
<<: !include common.yaml
sensor:
- platform: debug
min_free:
name: "Heap Min Free"
@@ -1,4 +1,5 @@
<<: !include common.yaml
packages:
debug: !include common.yaml
nrf52:
reg0:
@@ -1 +1,2 @@
<<: !include common.yaml
packages:
debug: !include common.yaml
@@ -1 +0,0 @@
<<: !include common.yaml
+2 -1
View File
@@ -1 +1,2 @@
<<: !include common.yaml
packages:
debug: !include common.yaml
@@ -1,6 +0,0 @@
<<: !include common.yaml
sensor:
- platform: debug
min_free:
name: "Heap Min Free"
@@ -0,0 +1,4 @@
# No pin anywhere in the config: the pin class methods deep_sleep calls must still link
deep_sleep:
run_duration: 10s
sleep_duration: 50s
@@ -1,5 +1,27 @@
substitutions:
wakeup_pin: GPIO4
<<: !include common.yaml
<<: !include common-esp32-all.yaml
packages:
deep_sleep: !include common.yaml
deep_sleep_esp32: !include common-esp32-all.yaml
power_supply:
- id: power_supply_ds_esp32
pin:
number: GPIO5
hold_state: true
output:
- platform: gpio
pin:
number: GPIO22
hold_state: true
id: gpio_output_ds_esp32
power_supply: power_supply_ds_esp32
switch:
- platform: gpio
pin:
number: GPIO23
hold_state: true
id: gpio_switch_ds_esp32
@@ -0,0 +1,26 @@
display:
- platform: ssd1306_i2c
i2c_id: i2c_bus
model: SSD1306_128X64
reset_pin: ${reset_pin}
id: display_page_change_display
pages:
- id: display_page_change_page1
lambda: |-
it.rectangle(0, 0, it.get_width(), it.get_height());
- id: display_page_change_page2
lambda: |-
it.rectangle(0, 0, 10, 10);
on_page_change:
- then:
lambda: 'ESP_LOGD("display", "any change, %p %p", from, to);'
- from: display_page_change_page1
then:
lambda: 'ESP_LOGD("display", "left page 1, %p %p", from, to);'
- to: display_page_change_page1
then:
lambda: 'ESP_LOGD("display", "entered page 1, %p %p", from, to);'
- from: display_page_change_page1
to: display_page_change_page2
then:
lambda: 'ESP_LOGD("display", "1 -> 2, %p %p", from, to);'
@@ -0,0 +1,6 @@
substitutions:
reset_pin: GPIO15
packages:
i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml
display: !include common-page-change.yaml
+20 -36
View File
@@ -4,63 +4,47 @@ dlms_meter:
decryption_key: "36C66639E48A8CA4D6BC8B282A793BBB"
auth_key: "11223344556677889900AABBCCDDEEFF"
skip_crc: true
provider: "netznoe"
custom_patterns:
- "custom_pattern_1"
- "custom_pattern_2"
- pattern: "L, TSTR"
name: "MeterID"
default_obis: "0.0.96.1.0.255"
- pattern: "F, TDTM"
name: "DateTime"
default_obis: "0.0.1.0.0.255"
sensor:
# Old Schema tests
- platform: dlms_meter
voltage_l1:
name: "Voltage L1"
voltage_l2:
name: "Voltage L2"
voltage_l3:
name: "Voltage L3"
current_l1:
name: "Current L1"
current_l2:
name: "Current L2"
current_l3:
name: "Current L3"
active_power_plus:
name: "Active power taken from grid"
active_power_minus:
name: "Active power put into grid"
active_energy_plus:
name: "Active energy taken from grid"
active_energy_minus:
name: "Active energy put into grid"
reactive_energy_plus:
name: "Reactive energy taken from grid"
reactive_energy_minus:
name: "Reactive energy put into grid"
power_factor:
name: "Power factor"
obis_code: "1.0.32.7.0.255"
name: "Voltage L1"
unit_of_measurement: V
accuracy_decimals: 1
device_class: voltage
state_class: measurement
- platform: dlms_meter
obis_code: "1.0.1.8.0.255"
name: "Active energy taken from grid"
unit_of_measurement: Wh
device_class: energy
state_class: total_increasing
# Dynamic Schema tests
- platform: dlms_meter
dlms_meter_id: dlms_meter_hub
obis_code: "1-0:99.99.9"
name: "Custom Dynamic Sensor"
text_sensor:
# Old Schema tests
- platform: dlms_meter
timestamp:
name: "timestamp"
meternumber:
name: "Meter Number"
obis_code: "0.0.1.0.0.255"
name: "timestamp"
# Dynamic Schema tests
- platform: dlms_meter
dlms_meter_id: dlms_meter_hub
obis_code: "0-0:99.99.9"
name: "Custom Dynamic Text Sensor"
binary_sensor:
# Dynamic Schema tests (Binary sensors only use the dynamic schema)
- platform: dlms_meter
dlms_meter_id: dlms_meter_hub
obis_code: "0-1:2.3.4"
+4
View File
@@ -5,10 +5,12 @@
# - DS2484: adjustable 1-Wire timing + RWPU pullup resistor selection
ds248x:
- id: ds2482_100
i2c_id: i2c_bus
address: 0x18
type: ds2482-100
active_pullup: true
- id: ds2482_101
i2c_id: i2c_bus
address: 0x19
type: ds2482-101
active_pullup: true
@@ -18,10 +20,12 @@ ds248x:
bus_sleep: true
hub_sleep: true
- id: ds2482_800
i2c_id: i2c_bus
address: 0x1a
type: ds2482-800
active_pullup: true
- id: ds2484_hub
i2c_id: i2c_bus
address: 0x1b
type: ds2484
active_pullup: true
@@ -196,6 +196,21 @@ TEST(EPaperSSD1677, RequestedFullUpdateAppliesWhenTheNextUpdateStarts) {
EXPECT_FALSE(display.run_update_state()) << "request was applied more than once";
}
/// The display reports updating from the start of an update until it is back to idle.
TEST(EPaperSSD1677, IsUpdatingUntilBackToIdle) {
TestableSSD1677 display(16, 2);
RecordingDelegate bus(&display.dc);
display.install(&bus, 5);
EXPECT_FALSE(display.is_updating());
display.request_full_update();
EXPECT_TRUE(display.run_update_state());
EXPECT_TRUE(display.is_updating());
EXPECT_FALSE(display.run_update_state()) << "an update with nothing drawn should not push";
EXPECT_FALSE(display.is_updating());
}
/// Nothing a partial needs lives in controller RAM any more, so a partial push skips the reset
/// altogether; a full one still gets the hardware pulse and the software reset.
TEST(EPaperSSD1677, PartialPushSkipsTheResetAndAFullPushKeepsIt) {
@@ -8,6 +8,11 @@ esphome:
on_boot:
then:
- epaper_spi.full_update_next: epaper_partial
- if:
condition:
epaper_spi.is_updating: epaper_partial
then:
- component.update: epaper_partial
display:
- platform: epaper_spi
@@ -77,6 +77,46 @@ esp32_ble_server:
id: test_change_descriptor
value:
data: [0x01, 0x02, 0x03]
# Identical constants, strings and typed numbers
- ble_server.characteristic.set_value:
id: test_change_characteristic
value:
data: [0xfc, 0xef, 0xfe, 0x86]
- ble_server.characteristic.set_value:
id: test_change_characteristic
value: "Changed"
- ble_server.characteristic.set_value:
id: test_change_characteristic
value:
data: 4660
type: uint16_t
endianness: BIG
- ble_server.characteristic.set_value:
id: test_change_characteristic
value:
data: -2
type: int32_t
- ble_server.characteristic.set_value:
id: test_change_characteristic
value:
data: 0.1
type: double
- ble_server.descriptor.set_value:
id: test_change_descriptor
value:
data: 123.1
type: float
endianness: BIG
- ble_server.descriptor.set_value:
id: test_change_descriptor
value: "Changed"
# Lambdas that read the trigger argument
- ble_server.characteristic.set_value:
id: test_change_characteristic
value: !lambda return x;
- ble_server.descriptor.set_value:
id: test_change_descriptor
value: !lambda return x;
# Regression test for #17142: the set_value action used from a trigger that passes
# its argument by reference (climate on_control supplies ClimateCall&) previously
+3
View File
@@ -36,6 +36,9 @@ espnow:
data: [0x01, 0x02, 0x03, 0x04, 0x05]
- espnow.broadcast:
data: !lambda 'return {0x01, 0x02, 0x03, 0x04, 0x05};'
- espnow.send:
address: 11:22:33:44:55:66
data: !lambda 'return std::vector<uint8_t>(data, data + size);'
- espnow.peer.add:
address: 11:22:33:44:55:66
- espnow.peer.add:
@@ -1,17 +0,0 @@
ethernet:
type: W5500
spi_id: spi_bus
cs_pin: 5
interrupt_pin: 36
reset_pin: 22
clock_speed: 10Mhz
manual_ip:
static_ip: 192.168.178.56
gateway: 192.168.178.1
subnet: 255.255.255.0
domain: .local
mac_address: "02:AA:BB:CC:DD:01"
on_connect:
- logger.log: "Ethernet connected!"
on_disconnect:
- logger.log: "Ethernet disconnected!"
@@ -1 +1,6 @@
<<: !include common-ip101.yaml
packages:
ethernet: !include common-ip101.yaml
# IPv6 on the internal EMAC: compiles the IPv6 event handler and multicast filter
network:
enable_ipv6: true
@@ -1,12 +0,0 @@
network:
enable_ipv6: true
ethernet:
type: IP101
mdc_pin: 23
mdio_pin: 18
clk:
pin: 0
mode: CLK_EXT_IN
phy_addr: 1
power_pin: 5
@@ -1,39 +0,0 @@
ethernet:
id: eth
type: W5500
clk_pin: 19
mosi_pin: 21
miso_pin: 23
cs_pin: 18
interrupt_pin: 36
reset_pin: 22
enable_on_boot: false
manual_ip:
static_ip: 192.168.178.56
gateway: 192.168.178.1
subnet: 255.255.255.0
mac_address: "02:AA:BB:CC:DD:01"
interface: spi2
esphome:
on_boot:
priority: 200
then:
- if:
condition:
not:
ethernet.enabled:
then:
- ethernet.enable:
button:
- platform: template
name: "Disable Ethernet"
on_press:
- ethernet.disable:
binary_sensor:
- platform: template
name: "Ethernet Connected"
lambda: |-
return id(eth).is_connected();
@@ -1,3 +0,0 @@
packages:
spi: !include ../../test_build_components/common/spi/esp32-idf.yaml
ethernet: !include common-w5500-spi-id.yaml
@@ -1,15 +0,0 @@
# W5500 next to wifi with PSRAM: compiles the input path that moves received frames to PSRAM
packages:
ethernet: !include common-w5500.yaml
psram:
mode: quad
wifi:
ssid: MySSID
password: password1
network:
priority:
- ethernet
- wifi
@@ -1,20 +1,47 @@
# W5500 on a shared SPI bus, started from an automation instead of on boot.
# Covers the spi_id attach path and the enable/disable lifecycle in one build.
packages:
spi: !include ../../test_build_components/common/spi/esp32-idf.yaml
ethernet:
id: eth
type: W5500
clk_pin: 19
mosi_pin: 21
miso_pin: 23
cs_pin: 18
spi_id: spi_bus
cs_pin: 5
interrupt_pin: 36
reset_pin: 22
clock_speed: 10Mhz
enable_on_boot: false
manual_ip:
static_ip: 192.168.178.56
gateway: 192.168.178.1
subnet: 255.255.255.0
domain: .local
mac_address: "02:AA:BB:CC:DD:01"
interface: spi2
on_connect:
- logger.log: "Ethernet connected!"
on_disconnect:
- logger.log: "Ethernet disconnected!"
esphome:
on_boot:
priority: 200
then:
- if:
condition:
not:
ethernet.enabled:
then:
- ethernet.enable:
button:
- platform: template
name: "Disable Ethernet"
on_press:
- ethernet.disable:
binary_sensor:
- platform: template
name: "Ethernet Connected"
lambda: |-
return id(eth).is_connected();
@@ -1 +1,15 @@
<<: !include common-w5500.yaml
# W5500 next to wifi with PSRAM: compiles the input path that moves received frames to PSRAM
packages:
ethernet: !include common-w5500.yaml
psram:
mode: quad
wifi:
ssid: MySSID
password: password1
network:
priority:
- ethernet
- wifi
@@ -0,0 +1,12 @@
# Config only: a user supplied SPI interface on dedicated pins exercises the
# interface option in validation.
ethernet:
type: W5500
interface: spi3
clk_pin: 19
mosi_pin: 21
miso_pin: 17
cs_pin: 18
interrupt_pin: 36
reset_pin: 12
clock_speed: 10Mhz
+16 -1
View File
@@ -6,4 +6,19 @@ substitutions:
switch_pin_3: GPIO6
switch_pin_4: GPIO7
<<: !include common.yaml
packages:
gpio: !include common.yaml
binary_sensor:
- platform: gpio
pin:
number: GPIO8
hold_state: true
id: gpio_binary_sensor_esp32
output:
- platform: gpio
pin:
number: GPIO9
hold_state: true
id: gpio_output_esp32
+23 -1
View File
@@ -6,4 +6,26 @@ substitutions:
switch_pin_3: GPIO16
switch_pin_4: GPIO17
<<: !include common.yaml
packages:
gpio: !include common.yaml
binary_sensor:
- platform: gpio
pin:
number: GPIO18
hold_state: true
id: gpio_binary_sensor_esp32
output:
- platform: gpio
pin:
number: GPIO19
hold_state: true
id: gpio_output_esp32
switch:
- platform: gpio
pin:
number: GPIO21
hold_state: true
id: gpio_switch_esp32
@@ -0,0 +1,24 @@
display:
- platform: ssd1306_i2c
i2c_id: i2c_bus
id: graphical_display_menu_triggers_display
model: SSD1306_128X64
reset_pin: ${reset_pin}
lambda: |-
it.rectangle(0, 0, it.get_width(), it.get_height());
font:
- file: "gfonts://Roboto"
id: graphical_display_menu_triggers_font
size: 20
graphical_display_menu:
id: graphical_display_menu_triggers_menu
display: graphical_display_menu_triggers_display
font: graphical_display_menu_triggers_font
on_redraw:
then:
lambda: 'ESP_LOGI("graphical_display_menu", "redraw, active: %d", it->is_active());'
items:
- type: label
text: "Label"
@@ -0,0 +1,6 @@
substitutions:
reset_pin: GPIO15
packages:
i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml
graphical_display_menu: !include common-triggers.yaml
@@ -71,6 +71,8 @@ esphome:
# encode_json_ receives const copies of non-const reference args.
- http_request.post:
url: https://esphome.io
request_headers:
X-Body-Length: !lambda 'return body.empty() ? "0" : "1";'
json:
status: "ok"
# Same with json: lambda variant, exercises json_func_ path
@@ -102,3 +104,23 @@ script:
then:
- http_request.get:
url: "http://google.com"
- id: send_with_headers
parameters:
token: string
then:
- http_request.get:
url: https://esphome.io
request_headers:
Accept: application/json
X-Device: esphome
- http_request.get:
url: https://esphome.io/other
request_headers:
Accept: application/json
X-Device: esphome
- http_request.get:
url: https://esphome.io
request_headers:
Accept: application/json
X-Token-Set: !lambda 'return token.empty() ? "no" : "yes";'
@@ -0,0 +1,21 @@
wifi:
ssid: MySSID
password: password1
binary_sensor:
- platform: gpio
pin: 0
id: improv_trigger_authorizer
improv_ble:
authorizer: improv_trigger_authorizer
on_start:
- logger.log: Improv started
on_provisioning:
- logger.log: Provisioning
on_provisioned:
- logger.log: Provisioned
on_stop:
- logger.log: Improv stopped
on_state:
- lambda: ESP_LOGD("improv", "State %d error %d", (int) state, (int) error);
@@ -1,8 +0,0 @@
wifi:
ssid: MySSID
password: password1
logger:
hardware_uart: USB_SERIAL_JTAG
improv_serial:
@@ -1,3 +0,0 @@
packages:
uart: !include ../../test_build_components/common/uart/esp8266-ard.yaml
improv_serial: !include common-uart-bus.yaml
@@ -1 +0,0 @@
<<: !include common-uart0.yaml
@@ -1 +0,0 @@
<<: !include common-uart0.yaml
@@ -1 +0,0 @@
<<: !include common-uart0.yaml
@@ -1 +0,0 @@
<<: !include common-uart0.yaml
@@ -1 +0,0 @@
<<: !include common-uart0.yaml
@@ -1 +0,0 @@
<<: !include common-uart0.yaml
@@ -1 +1,2 @@
<<: !include common-usb_cdc.yaml
packages:
improv_serial: !include common-usb_cdc.yaml
@@ -1 +0,0 @@
<<: !include common-usb_cdc.yaml
@@ -1 +0,0 @@
<<: !include common-usb_serial_jtag.yaml
@@ -1 +0,0 @@
<<: !include common-usb_serial_jtag.yaml
@@ -0,0 +1,2 @@
packages:
improv_serial: !include common.yaml
@@ -0,0 +1,2 @@
packages:
improv_serial: !include common.yaml
@@ -0,0 +1,2 @@
packages:
improv_serial: !include common.yaml
@@ -0,0 +1,2 @@
packages:
improv_serial: !include common.yaml
@@ -1,9 +0,0 @@
substitutions:
cs_pin: GPIO5
gdo0_pin: GPIO4
gdo2_pin: GPIO16
packages:
common: !include common.yaml
spi: !include ../../test_build_components/common/spi/esp8266-ard.yaml
cc1101: !include common-cc1101.yaml
@@ -1,7 +0,0 @@
substitutions:
tx_pin: GPIO4
rx_pin: GPIO5
packages:
common: !include common.yaml
rx: !include common-rx.yaml
@@ -1,7 +0,0 @@
substitutions:
tx_pin: GPIO4
rx_pin: GPIO5
packages:
common: !include common.yaml
rx: !include common-rx.yaml
@@ -1,7 +0,0 @@
substitutions:
tx_pin: GPIO4
rx_pin: GPIO5
packages:
common: !include common.yaml
tx: !include common-tx.yaml
@@ -1,7 +0,0 @@
substitutions:
tx_pin: GPIO4
rx_pin: GPIO5
packages:
common: !include common.yaml
tx: !include common-tx.yaml
@@ -1,7 +0,0 @@
substitutions:
tx_pin: GPIO4
rx_pin: GPIO5
packages:
common: !include common.yaml
tx: !include common-tx.yaml
+2 -1
View File
@@ -32,6 +32,7 @@ esphome:
- lambda: |-
const auto &effects = id(test_monochromatic_light).get_effects();
auto &same = id(test_monochromatic_light).get_effects();
const auto copy = id(test_monochromatic_light).get_effects(); // the view is copyable
uint32_t total = effects.size();
for (auto *effect : effects) {
ESP_LOGD("test", "Effect %s", effect->get_name().c_str());
@@ -39,7 +40,7 @@ esphome:
if (total > 0) {
ESP_LOGD("test", "First %s", effects.at(0)->get_name().c_str());
// Raw pointer iterators on purpose: external code binds std::find's result to `const auto *`
auto *it = std::find(same.begin(), same.end(), effects[0]);
auto *it = std::find(same.begin(), same.end(), copy[0]);
ESP_LOGD("test", "Index %d", (int) (it - same.begin()));
}
@@ -1,6 +1,5 @@
#include <gtest/gtest.h>
#include "esphome/components/light/light_effect.h"
#include "esphome/components/light/light_output.h"
#include "esphome/components/light/light_state.h"
@@ -18,36 +17,8 @@ class BrightnessOutput : public LightOutput {
void write_state(LightState *state) override {}
};
class NoopEffect : public LightEffect {
public:
using LightEffect::LightEffect;
void apply() override {}
};
// start_effect_() is where the uint32_t index is narrowed to the stored uint16_t.
class TestableLightState : public LightState {
public:
using LightState::LightState;
using LightState::start_effect_;
};
} // namespace
// add_effects() is public, so an external component can exceed the codegen cap on effect count;
// an index the uint16_t can't hold must be ignored rather than wrap onto another effect.
TEST(LightStateEffect, IndexAboveUint16IsIgnoredAndKeepsTheActiveEffect) {
BrightnessOutput output;
TestableLightState state(&output);
NoopEffect effect("Noop");
state.add_effects({&effect});
state.start_effect_(1);
ASSERT_EQ(state.get_current_effect_index(), 1u);
state.start_effect_(0x10000u); // unchecked narrowing wraps this to 0, which stops the effect
EXPECT_EQ(state.get_current_effect_index(), 1u);
}
// get_gamma_correct() reads the gamma codegen stores after the lookup table, rounded to two decimals.
TEST(LightStateGamma, ReadsTheGammaStoredWithTheTable) {
static constexpr GammaTable TABLE{{}, 280};
+31
View File
@@ -0,0 +1,31 @@
#include <gtest/gtest.h>
#include <map>
#include <string>
#include "esphome/components/logger/log_buffer.h"
#include "esphome/components/logger/logger.h"
#include "esphome/core/log.h"
namespace esphome::logger::testing {
ESPHOME_LOG_TAG(TEST_TAG, "test.tag");
TEST(LogTagTest, HeaderContainsTagAndLine) {
char data[256];
LogBuffer buf{data, sizeof(data)};
buf.write_header(5, TEST_TAG, 42, nullptr);
const std::string header(data, buf.pos);
EXPECT_NE(header.find("[D][test.tag:042]"), std::string::npos) << header;
}
TEST(LogTagTest, PerTagLevelLookupMatchesByContent) {
std::map<const char *, uint8_t, CStrCompare> levels{{"api", 1}, {"sensor", 2}, {"wifi", 3}};
// Copies, so a match must compare content rather than pointers
char api[] = "api", sensor[] = "sensor", wifi[] = "wifi", missing[] = "switch";
EXPECT_EQ(levels.find(FlashTag{api})->second, 1);
EXPECT_EQ(levels.find(FlashTag{sensor})->second, 2);
EXPECT_EQ(levels.find(FlashTag{wifi})->second, 3);
EXPECT_EQ(levels.find(FlashTag{missing}), levels.end());
}
} // namespace esphome::logger::testing
@@ -1,5 +0,0 @@
<<: !include common-default_uart.yaml
logger:
id: logger_id
task_log_buffer_size: 1024B # Set a custom buffer size
@@ -1,5 +0,0 @@
<<: !include common-default_uart.yaml
logger:
id: logger_id
task_log_buffer_size: 0
@@ -1,2 +0,0 @@
logger:
hardware_uart: UART0
@@ -1 +0,0 @@
<<: !include common-usb_cdc.yaml
@@ -1 +0,0 @@
<<: !include common-usb_cdc.yaml
@@ -1 +0,0 @@
<<: !include common-usb_serial_jtag.yaml
@@ -1 +0,0 @@
<<: !include common-usb_serial_jtag.yaml
@@ -1 +1,5 @@
<<: !include common-default_uart.yaml
logger:
id: logger_id
task_log_buffer_size: 0

Some files were not shown because too many files have changed in this diff Show More