mirror of
https://github.com/esphome/esphome.git
synced 2025-08-10 12:27:46 +00:00
Merge branch 'dev' into esp32_touch_isr
This commit is contained in:
2
.github/workflows/ci-docker.yml
vendored
2
.github/workflows/ci-docker.yml
vendored
@@ -49,7 +49,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: "3.10"
|
python-version: "3.10"
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.10.0
|
uses: docker/setup-buildx-action@v3.11.1
|
||||||
|
|
||||||
- name: Set TAG
|
- name: Set TAG
|
||||||
run: |
|
run: |
|
||||||
|
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -99,7 +99,7 @@ jobs:
|
|||||||
python-version: "3.10"
|
python-version: "3.10"
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.10.0
|
uses: docker/setup-buildx-action@v3.11.1
|
||||||
|
|
||||||
- name: Log in to docker hub
|
- name: Log in to docker hub
|
||||||
uses: docker/login-action@v3.4.0
|
uses: docker/login-action@v3.4.0
|
||||||
@@ -178,7 +178,7 @@ jobs:
|
|||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.10.0
|
uses: docker/setup-buildx-action@v3.11.1
|
||||||
|
|
||||||
- name: Log in to docker hub
|
- name: Log in to docker hub
|
||||||
if: matrix.registry == 'dockerhub'
|
if: matrix.registry == 'dockerhub'
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
# Ruff version.
|
# Ruff version.
|
||||||
rev: v0.11.10
|
rev: v0.12.0
|
||||||
hooks:
|
hooks:
|
||||||
# Run the linter.
|
# Run the linter.
|
||||||
- id: ruff
|
- id: ruff
|
||||||
|
@@ -520,6 +520,7 @@ esphome/components/xiaomi_lywsd03mmc/* @ahpohl
|
|||||||
esphome/components/xiaomi_mhoc303/* @drug123
|
esphome/components/xiaomi_mhoc303/* @drug123
|
||||||
esphome/components/xiaomi_mhoc401/* @vevsvevs
|
esphome/components/xiaomi_mhoc401/* @vevsvevs
|
||||||
esphome/components/xiaomi_rtcgq02lm/* @jesserockz
|
esphome/components/xiaomi_rtcgq02lm/* @jesserockz
|
||||||
|
esphome/components/xiaomi_xmwsdj04mmc/* @medusalix
|
||||||
esphome/components/xl9535/* @mreditor97
|
esphome/components/xl9535/* @mreditor97
|
||||||
esphome/components/xpt2046/touchscreen/* @nielsnl68 @numo68
|
esphome/components/xpt2046/touchscreen/* @nielsnl68 @numo68
|
||||||
esphome/components/xxtea/* @clydebarrow
|
esphome/components/xxtea/* @clydebarrow
|
||||||
|
@@ -22,6 +22,7 @@ from esphome.cpp_generator import ( # noqa: F401
|
|||||||
TemplateArguments,
|
TemplateArguments,
|
||||||
add,
|
add,
|
||||||
add_build_flag,
|
add_build_flag,
|
||||||
|
add_build_unflag,
|
||||||
add_define,
|
add_define,
|
||||||
add_global,
|
add_global,
|
||||||
add_library,
|
add_library,
|
||||||
@@ -34,6 +35,7 @@ from esphome.cpp_generator import ( # noqa: F401
|
|||||||
process_lambda,
|
process_lambda,
|
||||||
progmem_array,
|
progmem_array,
|
||||||
safe_exp,
|
safe_exp,
|
||||||
|
set_cpp_standard,
|
||||||
statement,
|
statement,
|
||||||
static_const_array,
|
static_const_array,
|
||||||
templatable,
|
templatable,
|
||||||
|
@@ -193,14 +193,13 @@ void AcDimmer::setup() {
|
|||||||
setTimer1Callback(&timer_interrupt);
|
setTimer1Callback(&timer_interrupt);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
// 80 Divider -> 1 count=1µs
|
// timer frequency of 1mhz
|
||||||
dimmer_timer = timerBegin(0, 80, true);
|
dimmer_timer = timerBegin(1000000);
|
||||||
timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr, true);
|
timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr);
|
||||||
// For ESP32, we can't use dynamic interval calculation because the timerX functions
|
// For ESP32, we can't use dynamic interval calculation because the timerX functions
|
||||||
// are not callable from ISR (placed in flash storage).
|
// are not callable from ISR (placed in flash storage).
|
||||||
// Here we just use an interrupt firing every 50 µs.
|
// Here we just use an interrupt firing every 50 µs.
|
||||||
timerAlarmWrite(dimmer_timer, 50, true);
|
timerAlarm(dimmer_timer, 50, true, 0);
|
||||||
timerAlarmEnable(dimmer_timer);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
void AcDimmer::write_state(float state) {
|
void AcDimmer::write_state(float state) {
|
||||||
|
@@ -17,7 +17,11 @@ void Anova::setup() {
|
|||||||
this->current_request_ = 0;
|
this->current_request_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Anova::loop() {}
|
void Anova::loop() {
|
||||||
|
// Parent BLEClientNode has a loop() method, but this component uses
|
||||||
|
// polling via update() and BLE callbacks so loop isn't needed
|
||||||
|
this->disable_loop();
|
||||||
|
}
|
||||||
|
|
||||||
void Anova::control(const ClimateCall &call) {
|
void Anova::control(const ClimateCall &call) {
|
||||||
if (call.get_mode().has_value()) {
|
if (call.get_mode().has_value()) {
|
||||||
|
@@ -266,6 +266,7 @@ enum EntityCategory {
|
|||||||
// ==================== BINARY SENSOR ====================
|
// ==================== BINARY SENSOR ====================
|
||||||
message ListEntitiesBinarySensorResponse {
|
message ListEntitiesBinarySensorResponse {
|
||||||
option (id) = 12;
|
option (id) = 12;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_BINARY_SENSOR";
|
option (ifdef) = "USE_BINARY_SENSOR";
|
||||||
|
|
||||||
@@ -282,6 +283,7 @@ message ListEntitiesBinarySensorResponse {
|
|||||||
}
|
}
|
||||||
message BinarySensorStateResponse {
|
message BinarySensorStateResponse {
|
||||||
option (id) = 21;
|
option (id) = 21;
|
||||||
|
option (base_class) = "StateResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_BINARY_SENSOR";
|
option (ifdef) = "USE_BINARY_SENSOR";
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
@@ -296,6 +298,7 @@ message BinarySensorStateResponse {
|
|||||||
// ==================== COVER ====================
|
// ==================== COVER ====================
|
||||||
message ListEntitiesCoverResponse {
|
message ListEntitiesCoverResponse {
|
||||||
option (id) = 13;
|
option (id) = 13;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_COVER";
|
option (ifdef) = "USE_COVER";
|
||||||
|
|
||||||
@@ -325,6 +328,7 @@ enum CoverOperation {
|
|||||||
}
|
}
|
||||||
message CoverStateResponse {
|
message CoverStateResponse {
|
||||||
option (id) = 22;
|
option (id) = 22;
|
||||||
|
option (base_class) = "StateResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_COVER";
|
option (ifdef) = "USE_COVER";
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
@@ -367,6 +371,7 @@ message CoverCommandRequest {
|
|||||||
// ==================== FAN ====================
|
// ==================== FAN ====================
|
||||||
message ListEntitiesFanResponse {
|
message ListEntitiesFanResponse {
|
||||||
option (id) = 14;
|
option (id) = 14;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_FAN";
|
option (ifdef) = "USE_FAN";
|
||||||
|
|
||||||
@@ -395,6 +400,7 @@ enum FanDirection {
|
|||||||
}
|
}
|
||||||
message FanStateResponse {
|
message FanStateResponse {
|
||||||
option (id) = 23;
|
option (id) = 23;
|
||||||
|
option (base_class) = "StateResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_FAN";
|
option (ifdef) = "USE_FAN";
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
@@ -444,6 +450,7 @@ enum ColorMode {
|
|||||||
}
|
}
|
||||||
message ListEntitiesLightResponse {
|
message ListEntitiesLightResponse {
|
||||||
option (id) = 15;
|
option (id) = 15;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_LIGHT";
|
option (ifdef) = "USE_LIGHT";
|
||||||
|
|
||||||
@@ -467,6 +474,7 @@ message ListEntitiesLightResponse {
|
|||||||
}
|
}
|
||||||
message LightStateResponse {
|
message LightStateResponse {
|
||||||
option (id) = 24;
|
option (id) = 24;
|
||||||
|
option (base_class) = "StateResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_LIGHT";
|
option (ifdef) = "USE_LIGHT";
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
@@ -536,6 +544,7 @@ enum SensorLastResetType {
|
|||||||
|
|
||||||
message ListEntitiesSensorResponse {
|
message ListEntitiesSensorResponse {
|
||||||
option (id) = 16;
|
option (id) = 16;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_SENSOR";
|
option (ifdef) = "USE_SENSOR";
|
||||||
|
|
||||||
@@ -557,6 +566,7 @@ message ListEntitiesSensorResponse {
|
|||||||
}
|
}
|
||||||
message SensorStateResponse {
|
message SensorStateResponse {
|
||||||
option (id) = 25;
|
option (id) = 25;
|
||||||
|
option (base_class) = "StateResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_SENSOR";
|
option (ifdef) = "USE_SENSOR";
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
@@ -571,6 +581,7 @@ message SensorStateResponse {
|
|||||||
// ==================== SWITCH ====================
|
// ==================== SWITCH ====================
|
||||||
message ListEntitiesSwitchResponse {
|
message ListEntitiesSwitchResponse {
|
||||||
option (id) = 17;
|
option (id) = 17;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_SWITCH";
|
option (ifdef) = "USE_SWITCH";
|
||||||
|
|
||||||
@@ -587,6 +598,7 @@ message ListEntitiesSwitchResponse {
|
|||||||
}
|
}
|
||||||
message SwitchStateResponse {
|
message SwitchStateResponse {
|
||||||
option (id) = 26;
|
option (id) = 26;
|
||||||
|
option (base_class) = "StateResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_SWITCH";
|
option (ifdef) = "USE_SWITCH";
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
@@ -607,6 +619,7 @@ message SwitchCommandRequest {
|
|||||||
// ==================== TEXT SENSOR ====================
|
// ==================== TEXT SENSOR ====================
|
||||||
message ListEntitiesTextSensorResponse {
|
message ListEntitiesTextSensorResponse {
|
||||||
option (id) = 18;
|
option (id) = 18;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_TEXT_SENSOR";
|
option (ifdef) = "USE_TEXT_SENSOR";
|
||||||
|
|
||||||
@@ -622,6 +635,7 @@ message ListEntitiesTextSensorResponse {
|
|||||||
}
|
}
|
||||||
message TextSensorStateResponse {
|
message TextSensorStateResponse {
|
||||||
option (id) = 27;
|
option (id) = 27;
|
||||||
|
option (base_class) = "StateResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_TEXT_SENSOR";
|
option (ifdef) = "USE_TEXT_SENSOR";
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
@@ -789,6 +803,7 @@ message ExecuteServiceRequest {
|
|||||||
// ==================== CAMERA ====================
|
// ==================== CAMERA ====================
|
||||||
message ListEntitiesCameraResponse {
|
message ListEntitiesCameraResponse {
|
||||||
option (id) = 43;
|
option (id) = 43;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_ESP32_CAMERA";
|
option (ifdef) = "USE_ESP32_CAMERA";
|
||||||
|
|
||||||
@@ -869,6 +884,7 @@ enum ClimatePreset {
|
|||||||
}
|
}
|
||||||
message ListEntitiesClimateResponse {
|
message ListEntitiesClimateResponse {
|
||||||
option (id) = 46;
|
option (id) = 46;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_CLIMATE";
|
option (ifdef) = "USE_CLIMATE";
|
||||||
|
|
||||||
@@ -903,6 +919,7 @@ message ListEntitiesClimateResponse {
|
|||||||
}
|
}
|
||||||
message ClimateStateResponse {
|
message ClimateStateResponse {
|
||||||
option (id) = 47;
|
option (id) = 47;
|
||||||
|
option (base_class) = "StateResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_CLIMATE";
|
option (ifdef) = "USE_CLIMATE";
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
@@ -964,6 +981,7 @@ enum NumberMode {
|
|||||||
}
|
}
|
||||||
message ListEntitiesNumberResponse {
|
message ListEntitiesNumberResponse {
|
||||||
option (id) = 49;
|
option (id) = 49;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_NUMBER";
|
option (ifdef) = "USE_NUMBER";
|
||||||
|
|
||||||
@@ -984,6 +1002,7 @@ message ListEntitiesNumberResponse {
|
|||||||
}
|
}
|
||||||
message NumberStateResponse {
|
message NumberStateResponse {
|
||||||
option (id) = 50;
|
option (id) = 50;
|
||||||
|
option (base_class) = "StateResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_NUMBER";
|
option (ifdef) = "USE_NUMBER";
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
@@ -1007,6 +1026,7 @@ message NumberCommandRequest {
|
|||||||
// ==================== SELECT ====================
|
// ==================== SELECT ====================
|
||||||
message ListEntitiesSelectResponse {
|
message ListEntitiesSelectResponse {
|
||||||
option (id) = 52;
|
option (id) = 52;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_SELECT";
|
option (ifdef) = "USE_SELECT";
|
||||||
|
|
||||||
@@ -1022,6 +1042,7 @@ message ListEntitiesSelectResponse {
|
|||||||
}
|
}
|
||||||
message SelectStateResponse {
|
message SelectStateResponse {
|
||||||
option (id) = 53;
|
option (id) = 53;
|
||||||
|
option (base_class) = "StateResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_SELECT";
|
option (ifdef) = "USE_SELECT";
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
@@ -1045,6 +1066,7 @@ message SelectCommandRequest {
|
|||||||
// ==================== SIREN ====================
|
// ==================== SIREN ====================
|
||||||
message ListEntitiesSirenResponse {
|
message ListEntitiesSirenResponse {
|
||||||
option (id) = 55;
|
option (id) = 55;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_SIREN";
|
option (ifdef) = "USE_SIREN";
|
||||||
|
|
||||||
@@ -1062,6 +1084,7 @@ message ListEntitiesSirenResponse {
|
|||||||
}
|
}
|
||||||
message SirenStateResponse {
|
message SirenStateResponse {
|
||||||
option (id) = 56;
|
option (id) = 56;
|
||||||
|
option (base_class) = "StateResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_SIREN";
|
option (ifdef) = "USE_SIREN";
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
@@ -1102,6 +1125,7 @@ enum LockCommand {
|
|||||||
}
|
}
|
||||||
message ListEntitiesLockResponse {
|
message ListEntitiesLockResponse {
|
||||||
option (id) = 58;
|
option (id) = 58;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_LOCK";
|
option (ifdef) = "USE_LOCK";
|
||||||
|
|
||||||
@@ -1123,6 +1147,7 @@ message ListEntitiesLockResponse {
|
|||||||
}
|
}
|
||||||
message LockStateResponse {
|
message LockStateResponse {
|
||||||
option (id) = 59;
|
option (id) = 59;
|
||||||
|
option (base_class) = "StateResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_LOCK";
|
option (ifdef) = "USE_LOCK";
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
@@ -1145,6 +1170,7 @@ message LockCommandRequest {
|
|||||||
// ==================== BUTTON ====================
|
// ==================== BUTTON ====================
|
||||||
message ListEntitiesButtonResponse {
|
message ListEntitiesButtonResponse {
|
||||||
option (id) = 61;
|
option (id) = 61;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_BUTTON";
|
option (ifdef) = "USE_BUTTON";
|
||||||
|
|
||||||
@@ -1196,6 +1222,7 @@ message MediaPlayerSupportedFormat {
|
|||||||
}
|
}
|
||||||
message ListEntitiesMediaPlayerResponse {
|
message ListEntitiesMediaPlayerResponse {
|
||||||
option (id) = 63;
|
option (id) = 63;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_MEDIA_PLAYER";
|
option (ifdef) = "USE_MEDIA_PLAYER";
|
||||||
|
|
||||||
@@ -1214,6 +1241,7 @@ message ListEntitiesMediaPlayerResponse {
|
|||||||
}
|
}
|
||||||
message MediaPlayerStateResponse {
|
message MediaPlayerStateResponse {
|
||||||
option (id) = 64;
|
option (id) = 64;
|
||||||
|
option (base_class) = "StateResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_MEDIA_PLAYER";
|
option (ifdef) = "USE_MEDIA_PLAYER";
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
@@ -1615,6 +1643,7 @@ enum VoiceAssistantEvent {
|
|||||||
VOICE_ASSISTANT_STT_VAD_END = 12;
|
VOICE_ASSISTANT_STT_VAD_END = 12;
|
||||||
VOICE_ASSISTANT_TTS_STREAM_START = 98;
|
VOICE_ASSISTANT_TTS_STREAM_START = 98;
|
||||||
VOICE_ASSISTANT_TTS_STREAM_END = 99;
|
VOICE_ASSISTANT_TTS_STREAM_END = 99;
|
||||||
|
VOICE_ASSISTANT_INTENT_PROGRESS = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
message VoiceAssistantEventData {
|
message VoiceAssistantEventData {
|
||||||
@@ -1735,6 +1764,7 @@ enum AlarmControlPanelStateCommand {
|
|||||||
|
|
||||||
message ListEntitiesAlarmControlPanelResponse {
|
message ListEntitiesAlarmControlPanelResponse {
|
||||||
option (id) = 94;
|
option (id) = 94;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_ALARM_CONTROL_PANEL";
|
option (ifdef) = "USE_ALARM_CONTROL_PANEL";
|
||||||
|
|
||||||
@@ -1752,6 +1782,7 @@ message ListEntitiesAlarmControlPanelResponse {
|
|||||||
|
|
||||||
message AlarmControlPanelStateResponse {
|
message AlarmControlPanelStateResponse {
|
||||||
option (id) = 95;
|
option (id) = 95;
|
||||||
|
option (base_class) = "StateResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_ALARM_CONTROL_PANEL";
|
option (ifdef) = "USE_ALARM_CONTROL_PANEL";
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
@@ -1776,6 +1807,7 @@ enum TextMode {
|
|||||||
}
|
}
|
||||||
message ListEntitiesTextResponse {
|
message ListEntitiesTextResponse {
|
||||||
option (id) = 97;
|
option (id) = 97;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_TEXT";
|
option (ifdef) = "USE_TEXT";
|
||||||
|
|
||||||
@@ -1794,6 +1826,7 @@ message ListEntitiesTextResponse {
|
|||||||
}
|
}
|
||||||
message TextStateResponse {
|
message TextStateResponse {
|
||||||
option (id) = 98;
|
option (id) = 98;
|
||||||
|
option (base_class) = "StateResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_TEXT";
|
option (ifdef) = "USE_TEXT";
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
@@ -1818,6 +1851,7 @@ message TextCommandRequest {
|
|||||||
// ==================== DATETIME DATE ====================
|
// ==================== DATETIME DATE ====================
|
||||||
message ListEntitiesDateResponse {
|
message ListEntitiesDateResponse {
|
||||||
option (id) = 100;
|
option (id) = 100;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_DATETIME_DATE";
|
option (ifdef) = "USE_DATETIME_DATE";
|
||||||
|
|
||||||
@@ -1832,6 +1866,7 @@ message ListEntitiesDateResponse {
|
|||||||
}
|
}
|
||||||
message DateStateResponse {
|
message DateStateResponse {
|
||||||
option (id) = 101;
|
option (id) = 101;
|
||||||
|
option (base_class) = "StateResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_DATETIME_DATE";
|
option (ifdef) = "USE_DATETIME_DATE";
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
@@ -1859,6 +1894,7 @@ message DateCommandRequest {
|
|||||||
// ==================== DATETIME TIME ====================
|
// ==================== DATETIME TIME ====================
|
||||||
message ListEntitiesTimeResponse {
|
message ListEntitiesTimeResponse {
|
||||||
option (id) = 103;
|
option (id) = 103;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_DATETIME_TIME";
|
option (ifdef) = "USE_DATETIME_TIME";
|
||||||
|
|
||||||
@@ -1873,6 +1909,7 @@ message ListEntitiesTimeResponse {
|
|||||||
}
|
}
|
||||||
message TimeStateResponse {
|
message TimeStateResponse {
|
||||||
option (id) = 104;
|
option (id) = 104;
|
||||||
|
option (base_class) = "StateResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_DATETIME_TIME";
|
option (ifdef) = "USE_DATETIME_TIME";
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
@@ -1900,6 +1937,7 @@ message TimeCommandRequest {
|
|||||||
// ==================== EVENT ====================
|
// ==================== EVENT ====================
|
||||||
message ListEntitiesEventResponse {
|
message ListEntitiesEventResponse {
|
||||||
option (id) = 107;
|
option (id) = 107;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_EVENT";
|
option (ifdef) = "USE_EVENT";
|
||||||
|
|
||||||
@@ -1917,6 +1955,7 @@ message ListEntitiesEventResponse {
|
|||||||
}
|
}
|
||||||
message EventResponse {
|
message EventResponse {
|
||||||
option (id) = 108;
|
option (id) = 108;
|
||||||
|
option (base_class) = "StateResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_EVENT";
|
option (ifdef) = "USE_EVENT";
|
||||||
|
|
||||||
@@ -1927,6 +1966,7 @@ message EventResponse {
|
|||||||
// ==================== VALVE ====================
|
// ==================== VALVE ====================
|
||||||
message ListEntitiesValveResponse {
|
message ListEntitiesValveResponse {
|
||||||
option (id) = 109;
|
option (id) = 109;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_VALVE";
|
option (ifdef) = "USE_VALVE";
|
||||||
|
|
||||||
@@ -1952,6 +1992,7 @@ enum ValveOperation {
|
|||||||
}
|
}
|
||||||
message ValveStateResponse {
|
message ValveStateResponse {
|
||||||
option (id) = 110;
|
option (id) = 110;
|
||||||
|
option (base_class) = "StateResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_VALVE";
|
option (ifdef) = "USE_VALVE";
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
@@ -1976,6 +2017,7 @@ message ValveCommandRequest {
|
|||||||
// ==================== DATETIME DATETIME ====================
|
// ==================== DATETIME DATETIME ====================
|
||||||
message ListEntitiesDateTimeResponse {
|
message ListEntitiesDateTimeResponse {
|
||||||
option (id) = 112;
|
option (id) = 112;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_DATETIME_DATETIME";
|
option (ifdef) = "USE_DATETIME_DATETIME";
|
||||||
|
|
||||||
@@ -1990,6 +2032,7 @@ message ListEntitiesDateTimeResponse {
|
|||||||
}
|
}
|
||||||
message DateTimeStateResponse {
|
message DateTimeStateResponse {
|
||||||
option (id) = 113;
|
option (id) = 113;
|
||||||
|
option (base_class) = "StateResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_DATETIME_DATETIME";
|
option (ifdef) = "USE_DATETIME_DATETIME";
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
@@ -2013,6 +2056,7 @@ message DateTimeCommandRequest {
|
|||||||
// ==================== UPDATE ====================
|
// ==================== UPDATE ====================
|
||||||
message ListEntitiesUpdateResponse {
|
message ListEntitiesUpdateResponse {
|
||||||
option (id) = 116;
|
option (id) = 116;
|
||||||
|
option (base_class) = "InfoResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_UPDATE";
|
option (ifdef) = "USE_UPDATE";
|
||||||
|
|
||||||
@@ -2028,6 +2072,7 @@ message ListEntitiesUpdateResponse {
|
|||||||
}
|
}
|
||||||
message UpdateStateResponse {
|
message UpdateStateResponse {
|
||||||
option (id) = 117;
|
option (id) = 117;
|
||||||
|
option (base_class) = "StateResponseProtoMessage";
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
option (ifdef) = "USE_UPDATE";
|
option (ifdef) = "USE_UPDATE";
|
||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
|
@@ -61,8 +61,8 @@ void APIConnection::start() {
|
|||||||
APIError err = this->helper_->init();
|
APIError err = this->helper_->init();
|
||||||
if (err != APIError::OK) {
|
if (err != APIError::OK) {
|
||||||
on_fatal_error();
|
on_fatal_error();
|
||||||
ESP_LOGW(TAG, "%s: Helper init failed: %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
|
ESP_LOGW(TAG, "%s: Helper init failed: %s errno=%d", this->get_client_combined_info().c_str(),
|
||||||
errno);
|
api_error_to_str(err), errno);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->client_info_ = helper_->getpeername();
|
this->client_info_ = helper_->getpeername();
|
||||||
@@ -91,7 +91,7 @@ void APIConnection::loop() {
|
|||||||
// when network is disconnected force disconnect immediately
|
// when network is disconnected force disconnect immediately
|
||||||
// don't wait for timeout
|
// don't wait for timeout
|
||||||
this->on_fatal_error();
|
this->on_fatal_error();
|
||||||
ESP_LOGW(TAG, "%s: Network unavailable; disconnecting", this->client_combined_info_.c_str());
|
ESP_LOGW(TAG, "%s: Network unavailable; disconnecting", this->get_client_combined_info().c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this->next_close_) {
|
if (this->next_close_) {
|
||||||
@@ -104,7 +104,7 @@ void APIConnection::loop() {
|
|||||||
APIError err = this->helper_->loop();
|
APIError err = this->helper_->loop();
|
||||||
if (err != APIError::OK) {
|
if (err != APIError::OK) {
|
||||||
on_fatal_error();
|
on_fatal_error();
|
||||||
ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->client_combined_info_.c_str(),
|
ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->get_client_combined_info().c_str(),
|
||||||
api_error_to_str(err), errno);
|
api_error_to_str(err), errno);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -118,12 +118,12 @@ void APIConnection::loop() {
|
|||||||
} else if (err != APIError::OK) {
|
} else if (err != APIError::OK) {
|
||||||
on_fatal_error();
|
on_fatal_error();
|
||||||
if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) {
|
if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) {
|
||||||
ESP_LOGW(TAG, "%s: Connection reset", this->client_combined_info_.c_str());
|
ESP_LOGW(TAG, "%s: Connection reset", this->get_client_combined_info().c_str());
|
||||||
} else if (err == APIError::CONNECTION_CLOSED) {
|
} else if (err == APIError::CONNECTION_CLOSED) {
|
||||||
ESP_LOGW(TAG, "%s: Connection closed", this->client_combined_info_.c_str());
|
ESP_LOGW(TAG, "%s: Connection closed", this->get_client_combined_info().c_str());
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
|
ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", this->get_client_combined_info().c_str(),
|
||||||
errno);
|
api_error_to_str(err), errno);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
@@ -157,7 +157,7 @@ void APIConnection::loop() {
|
|||||||
// Disconnect if not responded within 2.5*keepalive
|
// Disconnect if not responded within 2.5*keepalive
|
||||||
if (now - this->last_traffic_ > (KEEPALIVE_TIMEOUT_MS * 5) / 2) {
|
if (now - this->last_traffic_ > (KEEPALIVE_TIMEOUT_MS * 5) / 2) {
|
||||||
on_fatal_error();
|
on_fatal_error();
|
||||||
ESP_LOGW(TAG, "%s is unresponsive; disconnecting", this->client_combined_info_.c_str());
|
ESP_LOGW(TAG, "%s is unresponsive; disconnecting", this->get_client_combined_info().c_str());
|
||||||
}
|
}
|
||||||
} else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS && now > this->next_ping_retry_) {
|
} else if (now - this->last_traffic_ > KEEPALIVE_TIMEOUT_MS && now > this->next_ping_retry_) {
|
||||||
ESP_LOGVV(TAG, "Sending keepalive PING");
|
ESP_LOGVV(TAG, "Sending keepalive PING");
|
||||||
@@ -166,7 +166,7 @@ void APIConnection::loop() {
|
|||||||
this->next_ping_retry_ = now + ping_retry_interval;
|
this->next_ping_retry_ = now + ping_retry_interval;
|
||||||
this->ping_retries_++;
|
this->ping_retries_++;
|
||||||
std::string warn_str = str_sprintf("%s: Sending keepalive failed %u time(s);",
|
std::string warn_str = str_sprintf("%s: Sending keepalive failed %u time(s);",
|
||||||
this->client_combined_info_.c_str(), this->ping_retries_);
|
this->get_client_combined_info().c_str(), this->ping_retries_);
|
||||||
if (this->ping_retries_ >= max_ping_retries) {
|
if (this->ping_retries_ >= max_ping_retries) {
|
||||||
on_fatal_error();
|
on_fatal_error();
|
||||||
ESP_LOGE(TAG, "%s disconnecting", warn_str.c_str());
|
ESP_LOGE(TAG, "%s disconnecting", warn_str.c_str());
|
||||||
@@ -233,7 +233,7 @@ DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
|
|||||||
// remote initiated disconnect_client
|
// remote initiated disconnect_client
|
||||||
// don't close yet, we still need to send the disconnect response
|
// don't close yet, we still need to send the disconnect response
|
||||||
// close will happen on next loop
|
// close will happen on next loop
|
||||||
ESP_LOGD(TAG, "%s disconnected", this->client_combined_info_.c_str());
|
ESP_LOGD(TAG, "%s disconnected", this->get_client_combined_info().c_str());
|
||||||
this->next_close_ = true;
|
this->next_close_ = true;
|
||||||
DisconnectResponse resp;
|
DisconnectResponse resp;
|
||||||
return resp;
|
return resp;
|
||||||
@@ -248,25 +248,41 @@ void APIConnection::on_disconnect_response(const DisconnectResponse &value) {
|
|||||||
uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint16_t message_type, APIConnection *conn,
|
uint16_t APIConnection::encode_message_to_buffer(ProtoMessage &msg, uint16_t message_type, APIConnection *conn,
|
||||||
uint32_t remaining_size, bool is_single) {
|
uint32_t remaining_size, bool is_single) {
|
||||||
// Calculate size
|
// Calculate size
|
||||||
uint32_t size = 0;
|
uint32_t calculated_size = 0;
|
||||||
msg.calculate_size(size);
|
msg.calculate_size(calculated_size);
|
||||||
|
|
||||||
|
// Cache frame sizes to avoid repeated virtual calls
|
||||||
|
const uint8_t header_padding = conn->helper_->frame_header_padding();
|
||||||
|
const uint8_t footer_size = conn->helper_->frame_footer_size();
|
||||||
|
|
||||||
// Calculate total size with padding for buffer allocation
|
// Calculate total size with padding for buffer allocation
|
||||||
uint16_t total_size =
|
size_t total_calculated_size = calculated_size + header_padding + footer_size;
|
||||||
static_cast<uint16_t>(size) + conn->helper_->frame_header_padding() + conn->helper_->frame_footer_size();
|
|
||||||
|
|
||||||
// Check if it fits
|
// Check if it fits
|
||||||
if (total_size > remaining_size) {
|
if (total_calculated_size > remaining_size) {
|
||||||
return 0; // Doesn't fit
|
return 0; // Doesn't fit
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate exact buffer space needed (just the payload, not the overhead)
|
// Allocate buffer space - pass payload size, allocation functions add header/footer space
|
||||||
ProtoWriteBuffer buffer =
|
ProtoWriteBuffer buffer = is_single ? conn->allocate_single_message_buffer(calculated_size)
|
||||||
is_single ? conn->allocate_single_message_buffer(size) : conn->allocate_batch_message_buffer(size);
|
: conn->allocate_batch_message_buffer(calculated_size);
|
||||||
|
|
||||||
|
// Get buffer size after allocation (which includes header padding)
|
||||||
|
std::vector<uint8_t> &shared_buf = conn->parent_->get_shared_buffer_ref();
|
||||||
|
size_t size_before_encode = shared_buf.size();
|
||||||
|
|
||||||
// Encode directly into buffer
|
// Encode directly into buffer
|
||||||
msg.encode(buffer);
|
msg.encode(buffer);
|
||||||
return total_size;
|
|
||||||
|
// Calculate actual encoded size (not including header that was already added)
|
||||||
|
size_t actual_payload_size = shared_buf.size() - size_before_encode;
|
||||||
|
|
||||||
|
// Return actual total size (header + actual payload + footer)
|
||||||
|
size_t actual_total_size = header_padding + actual_payload_size + footer_size;
|
||||||
|
|
||||||
|
// Verify that calculate_size() returned the correct value
|
||||||
|
assert(calculated_size == actual_payload_size);
|
||||||
|
return static_cast<uint16_t>(actual_total_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
@@ -285,7 +301,7 @@ uint16_t APIConnection::try_send_binary_sensor_state(EntityBase *entity, APIConn
|
|||||||
BinarySensorStateResponse resp;
|
BinarySensorStateResponse resp;
|
||||||
resp.state = binary_sensor->state;
|
resp.state = binary_sensor->state;
|
||||||
resp.missing_state = !binary_sensor->has_state();
|
resp.missing_state = !binary_sensor->has_state();
|
||||||
resp.key = binary_sensor->get_object_id_hash();
|
fill_entity_state_base(binary_sensor, resp);
|
||||||
return encode_message_to_buffer(resp, BinarySensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return encode_message_to_buffer(resp, BinarySensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -319,7 +335,7 @@ uint16_t APIConnection::try_send_cover_state(EntityBase *entity, APIConnection *
|
|||||||
if (traits.get_supports_tilt())
|
if (traits.get_supports_tilt())
|
||||||
msg.tilt = cover->tilt;
|
msg.tilt = cover->tilt;
|
||||||
msg.current_operation = static_cast<enums::CoverOperation>(cover->current_operation);
|
msg.current_operation = static_cast<enums::CoverOperation>(cover->current_operation);
|
||||||
msg.key = cover->get_object_id_hash();
|
fill_entity_state_base(cover, msg);
|
||||||
return encode_message_to_buffer(msg, CoverStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return encode_message_to_buffer(msg, CoverStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_cover_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_cover_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
@@ -387,7 +403,7 @@ uint16_t APIConnection::try_send_fan_state(EntityBase *entity, APIConnection *co
|
|||||||
msg.direction = static_cast<enums::FanDirection>(fan->direction);
|
msg.direction = static_cast<enums::FanDirection>(fan->direction);
|
||||||
if (traits.supports_preset_modes())
|
if (traits.supports_preset_modes())
|
||||||
msg.preset_mode = fan->preset_mode;
|
msg.preset_mode = fan->preset_mode;
|
||||||
msg.key = fan->get_object_id_hash();
|
fill_entity_state_base(fan, msg);
|
||||||
return encode_message_to_buffer(msg, FanStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return encode_message_to_buffer(msg, FanStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_fan_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
@@ -454,7 +470,7 @@ uint16_t APIConnection::try_send_light_state(EntityBase *entity, APIConnection *
|
|||||||
resp.warm_white = values.get_warm_white();
|
resp.warm_white = values.get_warm_white();
|
||||||
if (light->supports_effects())
|
if (light->supports_effects())
|
||||||
resp.effect = light->get_effect_name();
|
resp.effect = light->get_effect_name();
|
||||||
resp.key = light->get_object_id_hash();
|
fill_entity_state_base(light, resp);
|
||||||
return encode_message_to_buffer(resp, LightStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return encode_message_to_buffer(resp, LightStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_light_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
@@ -536,7 +552,7 @@ uint16_t APIConnection::try_send_sensor_state(EntityBase *entity, APIConnection
|
|||||||
SensorStateResponse resp;
|
SensorStateResponse resp;
|
||||||
resp.state = sensor->state;
|
resp.state = sensor->state;
|
||||||
resp.missing_state = !sensor->has_state();
|
resp.missing_state = !sensor->has_state();
|
||||||
resp.key = sensor->get_object_id_hash();
|
fill_entity_state_base(sensor, resp);
|
||||||
return encode_message_to_buffer(resp, SensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return encode_message_to_buffer(resp, SensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -570,7 +586,7 @@ uint16_t APIConnection::try_send_switch_state(EntityBase *entity, APIConnection
|
|||||||
auto *a_switch = static_cast<switch_::Switch *>(entity);
|
auto *a_switch = static_cast<switch_::Switch *>(entity);
|
||||||
SwitchStateResponse resp;
|
SwitchStateResponse resp;
|
||||||
resp.state = a_switch->state;
|
resp.state = a_switch->state;
|
||||||
resp.key = a_switch->get_object_id_hash();
|
fill_entity_state_base(a_switch, resp);
|
||||||
return encode_message_to_buffer(resp, SwitchStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return encode_message_to_buffer(resp, SwitchStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -613,7 +629,7 @@ uint16_t APIConnection::try_send_text_sensor_state(EntityBase *entity, APIConnec
|
|||||||
TextSensorStateResponse resp;
|
TextSensorStateResponse resp;
|
||||||
resp.state = text_sensor->state;
|
resp.state = text_sensor->state;
|
||||||
resp.missing_state = !text_sensor->has_state();
|
resp.missing_state = !text_sensor->has_state();
|
||||||
resp.key = text_sensor->get_object_id_hash();
|
fill_entity_state_base(text_sensor, resp);
|
||||||
return encode_message_to_buffer(resp, TextSensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return encode_message_to_buffer(resp, TextSensorStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
uint16_t APIConnection::try_send_text_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
uint16_t APIConnection::try_send_text_sensor_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||||
@@ -637,7 +653,7 @@ uint16_t APIConnection::try_send_climate_state(EntityBase *entity, APIConnection
|
|||||||
bool is_single) {
|
bool is_single) {
|
||||||
auto *climate = static_cast<climate::Climate *>(entity);
|
auto *climate = static_cast<climate::Climate *>(entity);
|
||||||
ClimateStateResponse resp;
|
ClimateStateResponse resp;
|
||||||
resp.key = climate->get_object_id_hash();
|
fill_entity_state_base(climate, resp);
|
||||||
auto traits = climate->get_traits();
|
auto traits = climate->get_traits();
|
||||||
resp.mode = static_cast<enums::ClimateMode>(climate->mode);
|
resp.mode = static_cast<enums::ClimateMode>(climate->mode);
|
||||||
resp.action = static_cast<enums::ClimateAction>(climate->action);
|
resp.action = static_cast<enums::ClimateAction>(climate->action);
|
||||||
@@ -746,7 +762,7 @@ uint16_t APIConnection::try_send_number_state(EntityBase *entity, APIConnection
|
|||||||
NumberStateResponse resp;
|
NumberStateResponse resp;
|
||||||
resp.state = number->state;
|
resp.state = number->state;
|
||||||
resp.missing_state = !number->has_state();
|
resp.missing_state = !number->has_state();
|
||||||
resp.key = number->get_object_id_hash();
|
fill_entity_state_base(number, resp);
|
||||||
return encode_message_to_buffer(resp, NumberStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return encode_message_to_buffer(resp, NumberStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -787,7 +803,7 @@ uint16_t APIConnection::try_send_date_state(EntityBase *entity, APIConnection *c
|
|||||||
resp.year = date->year;
|
resp.year = date->year;
|
||||||
resp.month = date->month;
|
resp.month = date->month;
|
||||||
resp.day = date->day;
|
resp.day = date->day;
|
||||||
resp.key = date->get_object_id_hash();
|
fill_entity_state_base(date, resp);
|
||||||
return encode_message_to_buffer(resp, DateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return encode_message_to_buffer(resp, DateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::send_date_info(datetime::DateEntity *date) {
|
void APIConnection::send_date_info(datetime::DateEntity *date) {
|
||||||
@@ -824,7 +840,7 @@ uint16_t APIConnection::try_send_time_state(EntityBase *entity, APIConnection *c
|
|||||||
resp.hour = time->hour;
|
resp.hour = time->hour;
|
||||||
resp.minute = time->minute;
|
resp.minute = time->minute;
|
||||||
resp.second = time->second;
|
resp.second = time->second;
|
||||||
resp.key = time->get_object_id_hash();
|
fill_entity_state_base(time, resp);
|
||||||
return encode_message_to_buffer(resp, TimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return encode_message_to_buffer(resp, TimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::send_time_info(datetime::TimeEntity *time) {
|
void APIConnection::send_time_info(datetime::TimeEntity *time) {
|
||||||
@@ -863,7 +879,7 @@ uint16_t APIConnection::try_send_datetime_state(EntityBase *entity, APIConnectio
|
|||||||
ESPTime state = datetime->state_as_esptime();
|
ESPTime state = datetime->state_as_esptime();
|
||||||
resp.epoch_seconds = state.timestamp;
|
resp.epoch_seconds = state.timestamp;
|
||||||
}
|
}
|
||||||
resp.key = datetime->get_object_id_hash();
|
fill_entity_state_base(datetime, resp);
|
||||||
return encode_message_to_buffer(resp, DateTimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return encode_message_to_buffer(resp, DateTimeStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::send_datetime_info(datetime::DateTimeEntity *datetime) {
|
void APIConnection::send_datetime_info(datetime::DateTimeEntity *datetime) {
|
||||||
@@ -902,7 +918,7 @@ uint16_t APIConnection::try_send_text_state(EntityBase *entity, APIConnection *c
|
|||||||
TextStateResponse resp;
|
TextStateResponse resp;
|
||||||
resp.state = text->state;
|
resp.state = text->state;
|
||||||
resp.missing_state = !text->has_state();
|
resp.missing_state = !text->has_state();
|
||||||
resp.key = text->get_object_id_hash();
|
fill_entity_state_base(text, resp);
|
||||||
return encode_message_to_buffer(resp, TextStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return encode_message_to_buffer(resp, TextStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -943,7 +959,7 @@ uint16_t APIConnection::try_send_select_state(EntityBase *entity, APIConnection
|
|||||||
SelectStateResponse resp;
|
SelectStateResponse resp;
|
||||||
resp.state = select->state;
|
resp.state = select->state;
|
||||||
resp.missing_state = !select->has_state();
|
resp.missing_state = !select->has_state();
|
||||||
resp.key = select->get_object_id_hash();
|
fill_entity_state_base(select, resp);
|
||||||
return encode_message_to_buffer(resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return encode_message_to_buffer(resp, SelectStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1003,7 +1019,7 @@ uint16_t APIConnection::try_send_lock_state(EntityBase *entity, APIConnection *c
|
|||||||
auto *a_lock = static_cast<lock::Lock *>(entity);
|
auto *a_lock = static_cast<lock::Lock *>(entity);
|
||||||
LockStateResponse resp;
|
LockStateResponse resp;
|
||||||
resp.state = static_cast<enums::LockState>(a_lock->state);
|
resp.state = static_cast<enums::LockState>(a_lock->state);
|
||||||
resp.key = a_lock->get_object_id_hash();
|
fill_entity_state_base(a_lock, resp);
|
||||||
return encode_message_to_buffer(resp, LockStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return encode_message_to_buffer(resp, LockStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1047,7 +1063,7 @@ uint16_t APIConnection::try_send_valve_state(EntityBase *entity, APIConnection *
|
|||||||
ValveStateResponse resp;
|
ValveStateResponse resp;
|
||||||
resp.position = valve->position;
|
resp.position = valve->position;
|
||||||
resp.current_operation = static_cast<enums::ValveOperation>(valve->current_operation);
|
resp.current_operation = static_cast<enums::ValveOperation>(valve->current_operation);
|
||||||
resp.key = valve->get_object_id_hash();
|
fill_entity_state_base(valve, resp);
|
||||||
return encode_message_to_buffer(resp, ValveStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return encode_message_to_buffer(resp, ValveStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::send_valve_info(valve::Valve *valve) {
|
void APIConnection::send_valve_info(valve::Valve *valve) {
|
||||||
@@ -1095,7 +1111,7 @@ uint16_t APIConnection::try_send_media_player_state(EntityBase *entity, APIConne
|
|||||||
resp.state = static_cast<enums::MediaPlayerState>(report_state);
|
resp.state = static_cast<enums::MediaPlayerState>(report_state);
|
||||||
resp.volume = media_player->volume;
|
resp.volume = media_player->volume;
|
||||||
resp.muted = media_player->is_muted();
|
resp.muted = media_player->is_muted();
|
||||||
resp.key = media_player->get_object_id_hash();
|
fill_entity_state_base(media_player, resp);
|
||||||
return encode_message_to_buffer(resp, MediaPlayerStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return encode_message_to_buffer(resp, MediaPlayerStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::send_media_player_info(media_player::MediaPlayer *media_player) {
|
void APIConnection::send_media_player_info(media_player::MediaPlayer *media_player) {
|
||||||
@@ -1359,7 +1375,7 @@ uint16_t APIConnection::try_send_alarm_control_panel_state(EntityBase *entity, A
|
|||||||
auto *a_alarm_control_panel = static_cast<alarm_control_panel::AlarmControlPanel *>(entity);
|
auto *a_alarm_control_panel = static_cast<alarm_control_panel::AlarmControlPanel *>(entity);
|
||||||
AlarmControlPanelStateResponse resp;
|
AlarmControlPanelStateResponse resp;
|
||||||
resp.state = static_cast<enums::AlarmControlPanelState>(a_alarm_control_panel->get_state());
|
resp.state = static_cast<enums::AlarmControlPanelState>(a_alarm_control_panel->get_state());
|
||||||
resp.key = a_alarm_control_panel->get_object_id_hash();
|
fill_entity_state_base(a_alarm_control_panel, resp);
|
||||||
return encode_message_to_buffer(resp, AlarmControlPanelStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return encode_message_to_buffer(resp, AlarmControlPanelStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
|
void APIConnection::send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
|
||||||
@@ -1423,7 +1439,7 @@ uint16_t APIConnection::try_send_event_response(event::Event *event, const std::
|
|||||||
uint32_t remaining_size, bool is_single) {
|
uint32_t remaining_size, bool is_single) {
|
||||||
EventResponse resp;
|
EventResponse resp;
|
||||||
resp.event_type = event_type;
|
resp.event_type = event_type;
|
||||||
resp.key = event->get_object_id_hash();
|
fill_entity_state_base(event, resp);
|
||||||
return encode_message_to_buffer(resp, EventResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return encode_message_to_buffer(resp, EventResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1461,7 +1477,7 @@ uint16_t APIConnection::try_send_update_state(EntityBase *entity, APIConnection
|
|||||||
resp.release_summary = update->update_info.summary;
|
resp.release_summary = update->update_info.summary;
|
||||||
resp.release_url = update->update_info.release_url;
|
resp.release_url = update->update_info.release_url;
|
||||||
}
|
}
|
||||||
resp.key = update->get_object_id_hash();
|
fill_entity_state_base(update, resp);
|
||||||
return encode_message_to_buffer(resp, UpdateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
return encode_message_to_buffer(resp, UpdateStateResponse::MESSAGE_TYPE, conn, remaining_size, is_single);
|
||||||
}
|
}
|
||||||
void APIConnection::send_update_info(update::UpdateEntity *update) {
|
void APIConnection::send_update_info(update::UpdateEntity *update) {
|
||||||
@@ -1522,14 +1538,13 @@ bool APIConnection::try_send_log_message(int level, const char *tag, const char
|
|||||||
buffer.encode_string(3, line, line_length); // string message = 3
|
buffer.encode_string(3, line, line_length); // string message = 3
|
||||||
|
|
||||||
// SubscribeLogsResponse - 29
|
// SubscribeLogsResponse - 29
|
||||||
return this->send_buffer(buffer, 29);
|
return this->send_buffer(buffer, SubscribeLogsResponse::MESSAGE_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
HelloResponse APIConnection::hello(const HelloRequest &msg) {
|
HelloResponse APIConnection::hello(const HelloRequest &msg) {
|
||||||
this->client_info_ = msg.client_info;
|
this->client_info_ = msg.client_info;
|
||||||
this->client_peername_ = this->helper_->getpeername();
|
this->client_peername_ = this->helper_->getpeername();
|
||||||
this->client_combined_info_ = this->client_info_ + " (" + this->client_peername_ + ")";
|
this->helper_->set_log_info(this->get_client_combined_info());
|
||||||
this->helper_->set_log_info(this->client_combined_info_);
|
|
||||||
this->client_api_version_major_ = msg.api_version_major;
|
this->client_api_version_major_ = msg.api_version_major;
|
||||||
this->client_api_version_minor_ = msg.api_version_minor;
|
this->client_api_version_minor_ = msg.api_version_minor;
|
||||||
ESP_LOGV(TAG, "Hello from client: '%s' | %s | API Version %" PRIu32 ".%" PRIu32, this->client_info_.c_str(),
|
ESP_LOGV(TAG, "Hello from client: '%s' | %s | API Version %" PRIu32 ".%" PRIu32, this->client_info_.c_str(),
|
||||||
@@ -1551,7 +1566,7 @@ ConnectResponse APIConnection::connect(const ConnectRequest &msg) {
|
|||||||
// bool invalid_password = 1;
|
// bool invalid_password = 1;
|
||||||
resp.invalid_password = !correct;
|
resp.invalid_password = !correct;
|
||||||
if (correct) {
|
if (correct) {
|
||||||
ESP_LOGD(TAG, "%s connected", this->client_combined_info_.c_str());
|
ESP_LOGD(TAG, "%s connected", this->get_client_combined_info().c_str());
|
||||||
this->connection_state_ = ConnectionState::AUTHENTICATED;
|
this->connection_state_ = ConnectionState::AUTHENTICATED;
|
||||||
this->parent_->get_client_connected_trigger()->trigger(this->client_info_, this->client_peername_);
|
this->parent_->get_client_connected_trigger()->trigger(this->client_info_, this->client_peername_);
|
||||||
#ifdef USE_HOMEASSISTANT_TIME
|
#ifdef USE_HOMEASSISTANT_TIME
|
||||||
@@ -1657,7 +1672,7 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
|
|||||||
APIError err = this->helper_->loop();
|
APIError err = this->helper_->loop();
|
||||||
if (err != APIError::OK) {
|
if (err != APIError::OK) {
|
||||||
on_fatal_error();
|
on_fatal_error();
|
||||||
ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->client_combined_info_.c_str(),
|
ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", this->get_client_combined_info().c_str(),
|
||||||
api_error_to_str(err), errno);
|
api_error_to_str(err), errno);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1669,7 +1684,7 @@ bool APIConnection::try_to_clear_buffer(bool log_out_of_space) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) {
|
bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) {
|
||||||
if (!this->try_to_clear_buffer(message_type != 29)) { // SubscribeLogsResponse
|
if (!this->try_to_clear_buffer(message_type != SubscribeLogsResponse::MESSAGE_TYPE)) { // SubscribeLogsResponse
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1679,10 +1694,10 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint16_t message_type)
|
|||||||
if (err != APIError::OK) {
|
if (err != APIError::OK) {
|
||||||
on_fatal_error();
|
on_fatal_error();
|
||||||
if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
|
if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
|
||||||
ESP_LOGW(TAG, "%s: Connection reset", this->client_combined_info_.c_str());
|
ESP_LOGW(TAG, "%s: Connection reset", this->get_client_combined_info().c_str());
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGW(TAG, "%s: Packet write failed %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
|
ESP_LOGW(TAG, "%s: Packet write failed %s errno=%d", this->get_client_combined_info().c_str(),
|
||||||
errno);
|
api_error_to_str(err), errno);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1691,11 +1706,11 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint16_t message_type)
|
|||||||
}
|
}
|
||||||
void APIConnection::on_unauthenticated_access() {
|
void APIConnection::on_unauthenticated_access() {
|
||||||
this->on_fatal_error();
|
this->on_fatal_error();
|
||||||
ESP_LOGD(TAG, "%s requested access without authentication", this->client_combined_info_.c_str());
|
ESP_LOGD(TAG, "%s requested access without authentication", this->get_client_combined_info().c_str());
|
||||||
}
|
}
|
||||||
void APIConnection::on_no_setup_connection() {
|
void APIConnection::on_no_setup_connection() {
|
||||||
this->on_fatal_error();
|
this->on_fatal_error();
|
||||||
ESP_LOGD(TAG, "%s requested access without full connection", this->client_combined_info_.c_str());
|
ESP_LOGD(TAG, "%s requested access without full connection", this->get_client_combined_info().c_str());
|
||||||
}
|
}
|
||||||
void APIConnection::on_fatal_error() {
|
void APIConnection::on_fatal_error() {
|
||||||
this->helper_->close();
|
this->helper_->close();
|
||||||
@@ -1791,7 +1806,7 @@ void APIConnection::process_batch_() {
|
|||||||
this->batch_first_message_ = true;
|
this->batch_first_message_ = true;
|
||||||
|
|
||||||
size_t items_processed = 0;
|
size_t items_processed = 0;
|
||||||
uint32_t remaining_size = MAX_PACKET_SIZE;
|
uint16_t remaining_size = std::numeric_limits<uint16_t>::max();
|
||||||
|
|
||||||
// Track where each message's header padding begins in the buffer
|
// Track where each message's header padding begins in the buffer
|
||||||
// For plaintext: this is where the 6-byte header padding starts
|
// For plaintext: this is where the 6-byte header padding starts
|
||||||
@@ -1816,11 +1831,15 @@ void APIConnection::process_batch_() {
|
|||||||
packet_info.emplace_back(item.message_type, current_offset, proto_payload_size);
|
packet_info.emplace_back(item.message_type, current_offset, proto_payload_size);
|
||||||
|
|
||||||
// Update tracking variables
|
// Update tracking variables
|
||||||
|
items_processed++;
|
||||||
|
// After first message, set remaining size to MAX_PACKET_SIZE to avoid fragmentation
|
||||||
|
if (items_processed == 1) {
|
||||||
|
remaining_size = MAX_PACKET_SIZE;
|
||||||
|
}
|
||||||
remaining_size -= payload_size;
|
remaining_size -= payload_size;
|
||||||
// Calculate where the next message's header padding will start
|
// Calculate where the next message's header padding will start
|
||||||
// Current buffer size + footer space (that prepare_message_buffer will add for this message)
|
// Current buffer size + footer space (that prepare_message_buffer will add for this message)
|
||||||
current_offset = this->parent_->get_shared_buffer_ref().size() + footer_size;
|
current_offset = this->parent_->get_shared_buffer_ref().size() + footer_size;
|
||||||
items_processed++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (items_processed == 0) {
|
if (items_processed == 0) {
|
||||||
@@ -1840,10 +1859,10 @@ void APIConnection::process_batch_() {
|
|||||||
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
|
if (err != APIError::OK && err != APIError::WOULD_BLOCK) {
|
||||||
on_fatal_error();
|
on_fatal_error();
|
||||||
if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
|
if (err == APIError::SOCKET_WRITE_FAILED && errno == ECONNRESET) {
|
||||||
ESP_LOGW(TAG, "%s: Connection reset during batch write", this->client_combined_info_.c_str());
|
ESP_LOGW(TAG, "%s: Connection reset during batch write", this->get_client_combined_info().c_str());
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGW(TAG, "%s: Batch write failed %s errno=%d", this->client_combined_info_.c_str(), api_error_to_str(err),
|
ESP_LOGW(TAG, "%s: Batch write failed %s errno=%d", this->get_client_combined_info().c_str(),
|
||||||
errno);
|
api_error_to_str(err), errno);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -240,8 +240,8 @@ class APIConnection : public APIServerConnection {
|
|||||||
// - Header padding: space for protocol headers (7 bytes for Noise, 6 for Plaintext)
|
// - Header padding: space for protocol headers (7 bytes for Noise, 6 for Plaintext)
|
||||||
// - Footer: space for MAC (16 bytes for Noise, 0 for Plaintext)
|
// - Footer: space for MAC (16 bytes for Noise, 0 for Plaintext)
|
||||||
shared_buf.reserve(reserve_size + header_padding + this->helper_->frame_footer_size());
|
shared_buf.reserve(reserve_size + header_padding + this->helper_->frame_footer_size());
|
||||||
// Insert header padding bytes so message encoding starts at the correct position
|
// Resize to add header padding so message encoding starts at the correct position
|
||||||
shared_buf.insert(shared_buf.begin(), header_padding, 0);
|
shared_buf.resize(header_padding);
|
||||||
return {&shared_buf};
|
return {&shared_buf};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,47 +249,47 @@ class APIConnection : public APIServerConnection {
|
|||||||
ProtoWriteBuffer prepare_message_buffer(uint16_t message_size, bool is_first_message) {
|
ProtoWriteBuffer prepare_message_buffer(uint16_t message_size, bool is_first_message) {
|
||||||
// Get reference to shared buffer (it maintains state between batch messages)
|
// Get reference to shared buffer (it maintains state between batch messages)
|
||||||
std::vector<uint8_t> &shared_buf = this->parent_->get_shared_buffer_ref();
|
std::vector<uint8_t> &shared_buf = this->parent_->get_shared_buffer_ref();
|
||||||
size_t current_size = shared_buf.size();
|
|
||||||
|
|
||||||
if (is_first_message) {
|
if (is_first_message) {
|
||||||
// For first message, initialize buffer with header padding
|
|
||||||
uint8_t header_padding = this->helper_->frame_header_padding();
|
|
||||||
shared_buf.clear();
|
shared_buf.clear();
|
||||||
shared_buf.reserve(message_size + header_padding);
|
|
||||||
shared_buf.resize(header_padding);
|
|
||||||
// Fill header padding with zeros
|
|
||||||
std::fill(shared_buf.begin(), shared_buf.end(), 0);
|
|
||||||
} else {
|
|
||||||
// For subsequent messages, add footer space for previous message and header for this message
|
|
||||||
uint8_t footer_size = this->helper_->frame_footer_size();
|
|
||||||
uint8_t header_padding = this->helper_->frame_header_padding();
|
|
||||||
|
|
||||||
// Reserve additional space for everything
|
|
||||||
shared_buf.reserve(current_size + footer_size + header_padding + message_size);
|
|
||||||
|
|
||||||
// Single resize to add both footer and header padding
|
|
||||||
size_t new_size = current_size + footer_size + header_padding;
|
|
||||||
shared_buf.resize(new_size);
|
|
||||||
|
|
||||||
// Fill the newly added bytes with zeros (footer + header padding)
|
|
||||||
std::fill(shared_buf.begin() + current_size, shared_buf.end(), 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t current_size = shared_buf.size();
|
||||||
|
|
||||||
|
// Calculate padding to add:
|
||||||
|
// - First message: just header padding
|
||||||
|
// - Subsequent messages: footer for previous message + header padding for this message
|
||||||
|
size_t padding_to_add = is_first_message
|
||||||
|
? this->helper_->frame_header_padding()
|
||||||
|
: this->helper_->frame_header_padding() + this->helper_->frame_footer_size();
|
||||||
|
|
||||||
|
// Reserve space for padding + message
|
||||||
|
shared_buf.reserve(current_size + padding_to_add + message_size);
|
||||||
|
|
||||||
|
// Resize to add the padding bytes
|
||||||
|
shared_buf.resize(current_size + padding_to_add);
|
||||||
|
|
||||||
return {&shared_buf};
|
return {&shared_buf};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool try_to_clear_buffer(bool log_out_of_space);
|
bool try_to_clear_buffer(bool log_out_of_space);
|
||||||
bool send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) override;
|
bool send_buffer(ProtoWriteBuffer buffer, uint16_t message_type) override;
|
||||||
|
|
||||||
std::string get_client_combined_info() const { return this->client_combined_info_; }
|
std::string get_client_combined_info() const {
|
||||||
|
if (this->client_info_ == this->client_peername_) {
|
||||||
|
// Before Hello message, both are the same (just IP:port)
|
||||||
|
return this->client_info_;
|
||||||
|
}
|
||||||
|
return this->client_info_ + " (" + this->client_peername_ + ")";
|
||||||
|
}
|
||||||
|
|
||||||
// Buffer allocator methods for batch processing
|
// Buffer allocator methods for batch processing
|
||||||
ProtoWriteBuffer allocate_single_message_buffer(uint16_t size);
|
ProtoWriteBuffer allocate_single_message_buffer(uint16_t size);
|
||||||
ProtoWriteBuffer allocate_batch_message_buffer(uint16_t size);
|
ProtoWriteBuffer allocate_batch_message_buffer(uint16_t size);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Helper function to fill common entity fields
|
// Helper function to fill common entity info fields
|
||||||
template<typename ResponseT> static void fill_entity_info_base(esphome::EntityBase *entity, ResponseT &response) {
|
static void fill_entity_info_base(esphome::EntityBase *entity, InfoResponseProtoMessage &response) {
|
||||||
// Set common fields that are shared by all entity types
|
// Set common fields that are shared by all entity types
|
||||||
response.key = entity->get_object_id_hash();
|
response.key = entity->get_object_id_hash();
|
||||||
response.object_id = entity->get_object_id();
|
response.object_id = entity->get_object_id();
|
||||||
@@ -303,6 +303,11 @@ class APIConnection : public APIServerConnection {
|
|||||||
response.entity_category = static_cast<enums::EntityCategory>(entity->get_entity_category());
|
response.entity_category = static_cast<enums::EntityCategory>(entity->get_entity_category());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to fill common entity state fields
|
||||||
|
static void fill_entity_state_base(esphome::EntityBase *entity, StateResponseProtoMessage &response) {
|
||||||
|
response.key = entity->get_object_id_hash();
|
||||||
|
}
|
||||||
|
|
||||||
// Non-template helper to encode any ProtoMessage
|
// Non-template helper to encode any ProtoMessage
|
||||||
static uint16_t encode_message_to_buffer(ProtoMessage &msg, uint16_t message_type, APIConnection *conn,
|
static uint16_t encode_message_to_buffer(ProtoMessage &msg, uint16_t message_type, APIConnection *conn,
|
||||||
uint32_t remaining_size, bool is_single);
|
uint32_t remaining_size, bool is_single);
|
||||||
@@ -433,37 +438,44 @@ class APIConnection : public APIServerConnection {
|
|||||||
// Helper function to get estimated message size for buffer pre-allocation
|
// Helper function to get estimated message size for buffer pre-allocation
|
||||||
static uint16_t get_estimated_message_size(uint16_t message_type);
|
static uint16_t get_estimated_message_size(uint16_t message_type);
|
||||||
|
|
||||||
enum class ConnectionState {
|
// Pointers first (4 bytes each, naturally aligned)
|
||||||
|
std::unique_ptr<APIFrameHelper> helper_;
|
||||||
|
APIServer *parent_;
|
||||||
|
|
||||||
|
// 4-byte aligned types
|
||||||
|
uint32_t last_traffic_;
|
||||||
|
uint32_t next_ping_retry_{0};
|
||||||
|
int state_subs_at_ = -1;
|
||||||
|
|
||||||
|
// Strings (12 bytes each on 32-bit)
|
||||||
|
std::string client_info_;
|
||||||
|
std::string client_peername_;
|
||||||
|
|
||||||
|
// 2-byte aligned types
|
||||||
|
uint16_t client_api_version_major_{0};
|
||||||
|
uint16_t client_api_version_minor_{0};
|
||||||
|
|
||||||
|
// Group all 1-byte types together to minimize padding
|
||||||
|
enum class ConnectionState : uint8_t {
|
||||||
WAITING_FOR_HELLO,
|
WAITING_FOR_HELLO,
|
||||||
CONNECTED,
|
CONNECTED,
|
||||||
AUTHENTICATED,
|
AUTHENTICATED,
|
||||||
} connection_state_{ConnectionState::WAITING_FOR_HELLO};
|
} connection_state_{ConnectionState::WAITING_FOR_HELLO};
|
||||||
|
uint8_t log_subscription_{ESPHOME_LOG_LEVEL_NONE};
|
||||||
bool remove_{false};
|
bool remove_{false};
|
||||||
|
|
||||||
std::unique_ptr<APIFrameHelper> helper_;
|
|
||||||
|
|
||||||
std::string client_info_;
|
|
||||||
std::string client_peername_;
|
|
||||||
std::string client_combined_info_;
|
|
||||||
uint32_t client_api_version_major_{0};
|
|
||||||
uint32_t client_api_version_minor_{0};
|
|
||||||
#ifdef USE_ESP32_CAMERA
|
|
||||||
esp32_camera::CameraImageReader image_reader_;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool state_subscription_{false};
|
bool state_subscription_{false};
|
||||||
int log_subscription_{ESPHOME_LOG_LEVEL_NONE};
|
|
||||||
uint32_t last_traffic_;
|
|
||||||
uint32_t next_ping_retry_{0};
|
|
||||||
uint8_t ping_retries_{0};
|
|
||||||
bool sent_ping_{false};
|
bool sent_ping_{false};
|
||||||
bool service_call_subscription_{false};
|
bool service_call_subscription_{false};
|
||||||
bool next_close_ = false;
|
bool next_close_ = false;
|
||||||
APIServer *parent_;
|
uint8_t ping_retries_{0};
|
||||||
|
// 8 bytes used, no padding needed
|
||||||
|
|
||||||
|
// Larger objects at the end
|
||||||
InitialStateIterator initial_state_iterator_;
|
InitialStateIterator initial_state_iterator_;
|
||||||
ListEntitiesIterator list_entities_iterator_;
|
ListEntitiesIterator list_entities_iterator_;
|
||||||
int state_subs_at_ = -1;
|
#ifdef USE_ESP32_CAMERA
|
||||||
|
esp32_camera::CameraImageReader image_reader_;
|
||||||
|
#endif
|
||||||
|
|
||||||
// Function pointer type for message encoding
|
// Function pointer type for message encoding
|
||||||
using MessageCreatorPtr = uint16_t (*)(EntityBase *, APIConnection *, uint32_t remaining_size, bool is_single);
|
using MessageCreatorPtr = uint16_t (*)(EntityBase *, APIConnection *, uint32_t remaining_size, bool is_single);
|
||||||
|
@@ -125,38 +125,6 @@ class APIFrameHelper {
|
|||||||
const uint8_t *current_data() const { return data.data() + offset; }
|
const uint8_t *current_data() const { return data.data() + offset; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Queue of data buffers to be sent
|
|
||||||
std::deque<SendBuffer> tx_buf_;
|
|
||||||
|
|
||||||
// Common state enum for all frame helpers
|
|
||||||
// Note: Not all states are used by all implementations
|
|
||||||
// - INITIALIZE: Used by both Noise and Plaintext
|
|
||||||
// - CLIENT_HELLO, SERVER_HELLO, HANDSHAKE: Only used by Noise protocol
|
|
||||||
// - DATA: Used by both Noise and Plaintext
|
|
||||||
// - CLOSED: Used by both Noise and Plaintext
|
|
||||||
// - FAILED: Used by both Noise and Plaintext
|
|
||||||
// - EXPLICIT_REJECT: Only used by Noise protocol
|
|
||||||
enum class State {
|
|
||||||
INITIALIZE = 1,
|
|
||||||
CLIENT_HELLO = 2, // Noise only
|
|
||||||
SERVER_HELLO = 3, // Noise only
|
|
||||||
HANDSHAKE = 4, // Noise only
|
|
||||||
DATA = 5,
|
|
||||||
CLOSED = 6,
|
|
||||||
FAILED = 7,
|
|
||||||
EXPLICIT_REJECT = 8, // Noise only
|
|
||||||
};
|
|
||||||
|
|
||||||
// Current state of the frame helper
|
|
||||||
State state_{State::INITIALIZE};
|
|
||||||
|
|
||||||
// Helper name for logging
|
|
||||||
std::string info_;
|
|
||||||
|
|
||||||
// Socket for communication
|
|
||||||
socket::Socket *socket_{nullptr};
|
|
||||||
std::unique_ptr<socket::Socket> socket_owned_;
|
|
||||||
|
|
||||||
// Common implementation for writing raw data to socket
|
// Common implementation for writing raw data to socket
|
||||||
APIError write_raw_(const struct iovec *iov, int iovcnt);
|
APIError write_raw_(const struct iovec *iov, int iovcnt);
|
||||||
|
|
||||||
@@ -169,15 +137,41 @@ class APIFrameHelper {
|
|||||||
APIError write_raw_(const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector<uint8_t> &tx_buf,
|
APIError write_raw_(const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector<uint8_t> &tx_buf,
|
||||||
const std::string &info, StateEnum &state, StateEnum failed_state);
|
const std::string &info, StateEnum &state, StateEnum failed_state);
|
||||||
|
|
||||||
|
// Pointers first (4 bytes each)
|
||||||
|
socket::Socket *socket_{nullptr};
|
||||||
|
std::unique_ptr<socket::Socket> socket_owned_;
|
||||||
|
|
||||||
|
// Common state enum for all frame helpers
|
||||||
|
// Note: Not all states are used by all implementations
|
||||||
|
// - INITIALIZE: Used by both Noise and Plaintext
|
||||||
|
// - CLIENT_HELLO, SERVER_HELLO, HANDSHAKE: Only used by Noise protocol
|
||||||
|
// - DATA: Used by both Noise and Plaintext
|
||||||
|
// - CLOSED: Used by both Noise and Plaintext
|
||||||
|
// - FAILED: Used by both Noise and Plaintext
|
||||||
|
// - EXPLICIT_REJECT: Only used by Noise protocol
|
||||||
|
enum class State : uint8_t {
|
||||||
|
INITIALIZE = 1,
|
||||||
|
CLIENT_HELLO = 2, // Noise only
|
||||||
|
SERVER_HELLO = 3, // Noise only
|
||||||
|
HANDSHAKE = 4, // Noise only
|
||||||
|
DATA = 5,
|
||||||
|
CLOSED = 6,
|
||||||
|
FAILED = 7,
|
||||||
|
EXPLICIT_REJECT = 8, // Noise only
|
||||||
|
};
|
||||||
|
|
||||||
|
// Containers (size varies, but typically 12+ bytes on 32-bit)
|
||||||
|
std::deque<SendBuffer> tx_buf_;
|
||||||
|
std::string info_;
|
||||||
|
std::vector<struct iovec> reusable_iovs_;
|
||||||
|
std::vector<uint8_t> rx_buf_;
|
||||||
|
|
||||||
|
// Group smaller types together
|
||||||
|
uint16_t rx_buf_len_ = 0;
|
||||||
|
State state_{State::INITIALIZE};
|
||||||
uint8_t frame_header_padding_{0};
|
uint8_t frame_header_padding_{0};
|
||||||
uint8_t frame_footer_size_{0};
|
uint8_t frame_footer_size_{0};
|
||||||
|
// 5 bytes total, 3 bytes padding
|
||||||
// Reusable IOV array for write_protobuf_packets to avoid repeated allocations
|
|
||||||
std::vector<struct iovec> reusable_iovs_;
|
|
||||||
|
|
||||||
// Receive buffer for reading frame data
|
|
||||||
std::vector<uint8_t> rx_buf_;
|
|
||||||
uint16_t rx_buf_len_ = 0;
|
|
||||||
|
|
||||||
// Common initialization for both plaintext and noise protocols
|
// Common initialization for both plaintext and noise protocols
|
||||||
APIError init_common_();
|
APIError init_common_();
|
||||||
@@ -213,19 +207,28 @@ class APINoiseFrameHelper : public APIFrameHelper {
|
|||||||
APIError init_handshake_();
|
APIError init_handshake_();
|
||||||
APIError check_handshake_finished_();
|
APIError check_handshake_finished_();
|
||||||
void send_explicit_handshake_reject_(const std::string &reason);
|
void send_explicit_handshake_reject_(const std::string &reason);
|
||||||
|
|
||||||
|
// Pointers first (4 bytes each)
|
||||||
|
NoiseHandshakeState *handshake_{nullptr};
|
||||||
|
NoiseCipherState *send_cipher_{nullptr};
|
||||||
|
NoiseCipherState *recv_cipher_{nullptr};
|
||||||
|
|
||||||
|
// Shared pointer (8 bytes on 32-bit = 4 bytes control block pointer + 4 bytes object pointer)
|
||||||
|
std::shared_ptr<APINoiseContext> ctx_;
|
||||||
|
|
||||||
|
// Vector (12 bytes on 32-bit)
|
||||||
|
std::vector<uint8_t> prologue_;
|
||||||
|
|
||||||
|
// NoiseProtocolId (size depends on implementation)
|
||||||
|
NoiseProtocolId nid_;
|
||||||
|
|
||||||
|
// Group small types together
|
||||||
// Fixed-size header buffer for noise protocol:
|
// Fixed-size header buffer for noise protocol:
|
||||||
// 1 byte for indicator + 2 bytes for message size (16-bit value, not varint)
|
// 1 byte for indicator + 2 bytes for message size (16-bit value, not varint)
|
||||||
// Note: Maximum message size is UINT16_MAX (65535), with a limit of 128 bytes during handshake phase
|
// Note: Maximum message size is UINT16_MAX (65535), with a limit of 128 bytes during handshake phase
|
||||||
uint8_t rx_header_buf_[3];
|
uint8_t rx_header_buf_[3];
|
||||||
uint8_t rx_header_buf_len_ = 0;
|
uint8_t rx_header_buf_len_ = 0;
|
||||||
|
// 4 bytes total, no padding
|
||||||
std::vector<uint8_t> prologue_;
|
|
||||||
|
|
||||||
std::shared_ptr<APINoiseContext> ctx_;
|
|
||||||
NoiseHandshakeState *handshake_{nullptr};
|
|
||||||
NoiseCipherState *send_cipher_{nullptr};
|
|
||||||
NoiseCipherState *recv_cipher_{nullptr};
|
|
||||||
NoiseProtocolId nid_;
|
|
||||||
};
|
};
|
||||||
#endif // USE_API_NOISE
|
#endif // USE_API_NOISE
|
||||||
|
|
||||||
@@ -252,6 +255,12 @@ class APIPlaintextFrameHelper : public APIFrameHelper {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
APIError try_read_frame_(ParsedFrame *frame);
|
APIError try_read_frame_(ParsedFrame *frame);
|
||||||
|
|
||||||
|
// Group 2-byte aligned types
|
||||||
|
uint16_t rx_header_parsed_type_ = 0;
|
||||||
|
uint16_t rx_header_parsed_len_ = 0;
|
||||||
|
|
||||||
|
// Group 1-byte types together
|
||||||
// Fixed-size header buffer for plaintext protocol:
|
// Fixed-size header buffer for plaintext protocol:
|
||||||
// We now store the indicator byte + the two varints.
|
// We now store the indicator byte + the two varints.
|
||||||
// To match noise protocol's maximum message size (UINT16_MAX = 65535), we need:
|
// To match noise protocol's maximum message size (UINT16_MAX = 65535), we need:
|
||||||
@@ -263,8 +272,7 @@ class APIPlaintextFrameHelper : public APIFrameHelper {
|
|||||||
uint8_t rx_header_buf_[6]; // 1 byte indicator + 5 bytes for varints (3 for size + 2 for type)
|
uint8_t rx_header_buf_[6]; // 1 byte indicator + 5 bytes for varints (3 for size + 2 for type)
|
||||||
uint8_t rx_header_buf_pos_ = 0;
|
uint8_t rx_header_buf_pos_ = 0;
|
||||||
bool rx_header_parsed_ = false;
|
bool rx_header_parsed_ = false;
|
||||||
uint16_t rx_header_parsed_type_ = 0;
|
// 8 bytes total, no padding needed
|
||||||
uint16_t rx_header_parsed_len_ = 0;
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -21,4 +21,5 @@ extend google.protobuf.MessageOptions {
|
|||||||
optional string ifdef = 1038;
|
optional string ifdef = 1038;
|
||||||
optional bool log = 1039 [default=true];
|
optional bool log = 1039 [default=true];
|
||||||
optional bool no_delay = 1040 [default=false];
|
optional bool no_delay = 1040 [default=false];
|
||||||
|
optional string base_class = 1041;
|
||||||
}
|
}
|
||||||
|
@@ -516,6 +516,8 @@ template<> const char *proto_enum_to_string<enums::VoiceAssistantEvent>(enums::V
|
|||||||
return "VOICE_ASSISTANT_TTS_STREAM_START";
|
return "VOICE_ASSISTANT_TTS_STREAM_START";
|
||||||
case enums::VOICE_ASSISTANT_TTS_STREAM_END:
|
case enums::VOICE_ASSISTANT_TTS_STREAM_END:
|
||||||
return "VOICE_ASSISTANT_TTS_STREAM_END";
|
return "VOICE_ASSISTANT_TTS_STREAM_END";
|
||||||
|
case enums::VOICE_ASSISTANT_INTENT_PROGRESS:
|
||||||
|
return "VOICE_ASSISTANT_INTENT_PROGRESS";
|
||||||
default:
|
default:
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
@@ -628,6 +630,7 @@ template<> const char *proto_enum_to_string<enums::UpdateCommand>(enums::UpdateC
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool HelloRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
bool HelloRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
switch (field_id) {
|
switch (field_id) {
|
||||||
case 2: {
|
case 2: {
|
||||||
@@ -794,28 +797,18 @@ void ConnectResponse::dump_to(std::string &out) const {
|
|||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
void DisconnectRequest::encode(ProtoWriteBuffer buffer) const {}
|
|
||||||
void DisconnectRequest::calculate_size(uint32_t &total_size) const {}
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void DisconnectRequest::dump_to(std::string &out) const { out.append("DisconnectRequest {}"); }
|
void DisconnectRequest::dump_to(std::string &out) const { out.append("DisconnectRequest {}"); }
|
||||||
#endif
|
#endif
|
||||||
void DisconnectResponse::encode(ProtoWriteBuffer buffer) const {}
|
|
||||||
void DisconnectResponse::calculate_size(uint32_t &total_size) const {}
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void DisconnectResponse::dump_to(std::string &out) const { out.append("DisconnectResponse {}"); }
|
void DisconnectResponse::dump_to(std::string &out) const { out.append("DisconnectResponse {}"); }
|
||||||
#endif
|
#endif
|
||||||
void PingRequest::encode(ProtoWriteBuffer buffer) const {}
|
|
||||||
void PingRequest::calculate_size(uint32_t &total_size) const {}
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void PingRequest::dump_to(std::string &out) const { out.append("PingRequest {}"); }
|
void PingRequest::dump_to(std::string &out) const { out.append("PingRequest {}"); }
|
||||||
#endif
|
#endif
|
||||||
void PingResponse::encode(ProtoWriteBuffer buffer) const {}
|
|
||||||
void PingResponse::calculate_size(uint32_t &total_size) const {}
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void PingResponse::dump_to(std::string &out) const { out.append("PingResponse {}"); }
|
void PingResponse::dump_to(std::string &out) const { out.append("PingResponse {}"); }
|
||||||
#endif
|
#endif
|
||||||
void DeviceInfoRequest::encode(ProtoWriteBuffer buffer) const {}
|
|
||||||
void DeviceInfoRequest::calculate_size(uint32_t &total_size) const {}
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void DeviceInfoRequest::dump_to(std::string &out) const { out.append("DeviceInfoRequest {}"); }
|
void DeviceInfoRequest::dump_to(std::string &out) const { out.append("DeviceInfoRequest {}"); }
|
||||||
#endif
|
#endif
|
||||||
@@ -1036,18 +1029,12 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
|
|||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
void ListEntitiesRequest::encode(ProtoWriteBuffer buffer) const {}
|
|
||||||
void ListEntitiesRequest::calculate_size(uint32_t &total_size) const {}
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void ListEntitiesRequest::dump_to(std::string &out) const { out.append("ListEntitiesRequest {}"); }
|
void ListEntitiesRequest::dump_to(std::string &out) const { out.append("ListEntitiesRequest {}"); }
|
||||||
#endif
|
#endif
|
||||||
void ListEntitiesDoneResponse::encode(ProtoWriteBuffer buffer) const {}
|
|
||||||
void ListEntitiesDoneResponse::calculate_size(uint32_t &total_size) const {}
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void ListEntitiesDoneResponse::dump_to(std::string &out) const { out.append("ListEntitiesDoneResponse {}"); }
|
void ListEntitiesDoneResponse::dump_to(std::string &out) const { out.append("ListEntitiesDoneResponse {}"); }
|
||||||
#endif
|
#endif
|
||||||
void SubscribeStatesRequest::encode(ProtoWriteBuffer buffer) const {}
|
|
||||||
void SubscribeStatesRequest::calculate_size(uint32_t &total_size) const {}
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void SubscribeStatesRequest::dump_to(std::string &out) const { out.append("SubscribeStatesRequest {}"); }
|
void SubscribeStatesRequest::dump_to(std::string &out) const { out.append("SubscribeStatesRequest {}"); }
|
||||||
#endif
|
#endif
|
||||||
@@ -3368,8 +3355,6 @@ void NoiseEncryptionSetKeyResponse::dump_to(std::string &out) const {
|
|||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
void SubscribeHomeassistantServicesRequest::encode(ProtoWriteBuffer buffer) const {}
|
|
||||||
void SubscribeHomeassistantServicesRequest::calculate_size(uint32_t &total_size) const {}
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void SubscribeHomeassistantServicesRequest::dump_to(std::string &out) const {
|
void SubscribeHomeassistantServicesRequest::dump_to(std::string &out) const {
|
||||||
out.append("SubscribeHomeassistantServicesRequest {}");
|
out.append("SubscribeHomeassistantServicesRequest {}");
|
||||||
@@ -3495,8 +3480,6 @@ void HomeassistantServiceResponse::dump_to(std::string &out) const {
|
|||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
void SubscribeHomeAssistantStatesRequest::encode(ProtoWriteBuffer buffer) const {}
|
|
||||||
void SubscribeHomeAssistantStatesRequest::calculate_size(uint32_t &total_size) const {}
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void SubscribeHomeAssistantStatesRequest::dump_to(std::string &out) const {
|
void SubscribeHomeAssistantStatesRequest::dump_to(std::string &out) const {
|
||||||
out.append("SubscribeHomeAssistantStatesRequest {}");
|
out.append("SubscribeHomeAssistantStatesRequest {}");
|
||||||
@@ -3600,8 +3583,6 @@ void HomeAssistantStateResponse::dump_to(std::string &out) const {
|
|||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
void GetTimeRequest::encode(ProtoWriteBuffer buffer) const {}
|
|
||||||
void GetTimeRequest::calculate_size(uint32_t &total_size) const {}
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void GetTimeRequest::dump_to(std::string &out) const { out.append("GetTimeRequest {}"); }
|
void GetTimeRequest::dump_to(std::string &out) const { out.append("GetTimeRequest {}"); }
|
||||||
#endif
|
#endif
|
||||||
@@ -7496,8 +7477,6 @@ void BluetoothGATTNotifyDataResponse::dump_to(std::string &out) const {
|
|||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
void SubscribeBluetoothConnectionsFreeRequest::encode(ProtoWriteBuffer buffer) const {}
|
|
||||||
void SubscribeBluetoothConnectionsFreeRequest::calculate_size(uint32_t &total_size) const {}
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void SubscribeBluetoothConnectionsFreeRequest::dump_to(std::string &out) const {
|
void SubscribeBluetoothConnectionsFreeRequest::dump_to(std::string &out) const {
|
||||||
out.append("SubscribeBluetoothConnectionsFreeRequest {}");
|
out.append("SubscribeBluetoothConnectionsFreeRequest {}");
|
||||||
@@ -7781,8 +7760,6 @@ void BluetoothDeviceUnpairingResponse::dump_to(std::string &out) const {
|
|||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
void UnsubscribeBluetoothLEAdvertisementsRequest::encode(ProtoWriteBuffer buffer) const {}
|
|
||||||
void UnsubscribeBluetoothLEAdvertisementsRequest::calculate_size(uint32_t &total_size) const {}
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void UnsubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const {
|
void UnsubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const {
|
||||||
out.append("UnsubscribeBluetoothLEAdvertisementsRequest {}");
|
out.append("UnsubscribeBluetoothLEAdvertisementsRequest {}");
|
||||||
@@ -8448,8 +8425,6 @@ void VoiceAssistantWakeWord::dump_to(std::string &out) const {
|
|||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
void VoiceAssistantConfigurationRequest::encode(ProtoWriteBuffer buffer) const {}
|
|
||||||
void VoiceAssistantConfigurationRequest::calculate_size(uint32_t &total_size) const {}
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void VoiceAssistantConfigurationRequest::dump_to(std::string &out) const {
|
void VoiceAssistantConfigurationRequest::dump_to(std::string &out) const {
|
||||||
out.append("VoiceAssistantConfigurationRequest {}");
|
out.append("VoiceAssistantConfigurationRequest {}");
|
||||||
|
@@ -208,6 +208,7 @@ enum VoiceAssistantEvent : uint32_t {
|
|||||||
VOICE_ASSISTANT_STT_VAD_END = 12,
|
VOICE_ASSISTANT_STT_VAD_END = 12,
|
||||||
VOICE_ASSISTANT_TTS_STREAM_START = 98,
|
VOICE_ASSISTANT_TTS_STREAM_START = 98,
|
||||||
VOICE_ASSISTANT_TTS_STREAM_END = 99,
|
VOICE_ASSISTANT_TTS_STREAM_END = 99,
|
||||||
|
VOICE_ASSISTANT_INTENT_PROGRESS = 100,
|
||||||
};
|
};
|
||||||
enum VoiceAssistantTimerEvent : uint32_t {
|
enum VoiceAssistantTimerEvent : uint32_t {
|
||||||
VOICE_ASSISTANT_TIMER_STARTED = 0,
|
VOICE_ASSISTANT_TIMER_STARTED = 0,
|
||||||
@@ -253,6 +254,27 @@ enum UpdateCommand : uint32_t {
|
|||||||
|
|
||||||
} // namespace enums
|
} // namespace enums
|
||||||
|
|
||||||
|
class InfoResponseProtoMessage : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
~InfoResponseProtoMessage() override = default;
|
||||||
|
std::string object_id{};
|
||||||
|
uint32_t key{0};
|
||||||
|
std::string name{};
|
||||||
|
std::string unique_id{};
|
||||||
|
bool disabled_by_default{false};
|
||||||
|
std::string icon{};
|
||||||
|
enums::EntityCategory entity_category{};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
|
||||||
|
class StateResponseProtoMessage : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
~StateResponseProtoMessage() override = default;
|
||||||
|
uint32_t key{0};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
};
|
||||||
class HelloRequest : public ProtoMessage {
|
class HelloRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 1;
|
static constexpr uint16_t MESSAGE_TYPE = 1;
|
||||||
@@ -335,8 +357,6 @@ class DisconnectRequest : public ProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "disconnect_request"; }
|
static constexpr const char *message_name() { return "disconnect_request"; }
|
||||||
#endif
|
#endif
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -350,8 +370,6 @@ class DisconnectResponse : public ProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "disconnect_response"; }
|
static constexpr const char *message_name() { return "disconnect_response"; }
|
||||||
#endif
|
#endif
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -365,8 +383,6 @@ class PingRequest : public ProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "ping_request"; }
|
static constexpr const char *message_name() { return "ping_request"; }
|
||||||
#endif
|
#endif
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -380,8 +396,6 @@ class PingResponse : public ProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "ping_response"; }
|
static constexpr const char *message_name() { return "ping_response"; }
|
||||||
#endif
|
#endif
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -395,8 +409,6 @@ class DeviceInfoRequest : public ProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "device_info_request"; }
|
static constexpr const char *message_name() { return "device_info_request"; }
|
||||||
#endif
|
#endif
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -446,8 +458,6 @@ class ListEntitiesRequest : public ProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_request"; }
|
static constexpr const char *message_name() { return "list_entities_request"; }
|
||||||
#endif
|
#endif
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -461,8 +471,6 @@ class ListEntitiesDoneResponse : public ProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_done_response"; }
|
static constexpr const char *message_name() { return "list_entities_done_response"; }
|
||||||
#endif
|
#endif
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -476,30 +484,21 @@ class SubscribeStatesRequest : public ProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "subscribe_states_request"; }
|
static constexpr const char *message_name() { return "subscribe_states_request"; }
|
||||||
#endif
|
#endif
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
};
|
};
|
||||||
class ListEntitiesBinarySensorResponse : public ProtoMessage {
|
class ListEntitiesBinarySensorResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 12;
|
static constexpr uint16_t MESSAGE_TYPE = 12;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 56;
|
static constexpr uint16_t ESTIMATED_SIZE = 56;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_binary_sensor_response"; }
|
static constexpr const char *message_name() { return "list_entities_binary_sensor_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
std::string device_class{};
|
std::string device_class{};
|
||||||
bool is_status_binary_sensor{false};
|
bool is_status_binary_sensor{false};
|
||||||
bool disabled_by_default{false};
|
|
||||||
std::string icon{};
|
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@@ -511,14 +510,13 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class BinarySensorStateResponse : public ProtoMessage {
|
class BinarySensorStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 21;
|
static constexpr uint16_t MESSAGE_TYPE = 21;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 9;
|
static constexpr uint16_t ESTIMATED_SIZE = 9;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "binary_sensor_state_response"; }
|
static constexpr const char *message_name() { return "binary_sensor_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
uint32_t key{0};
|
|
||||||
bool state{false};
|
bool state{false};
|
||||||
bool missing_state{false};
|
bool missing_state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
@@ -531,24 +529,17 @@ class BinarySensorStateResponse : public ProtoMessage {
|
|||||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesCoverResponse : public ProtoMessage {
|
class ListEntitiesCoverResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 13;
|
static constexpr uint16_t MESSAGE_TYPE = 13;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 62;
|
static constexpr uint16_t ESTIMATED_SIZE = 62;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_cover_response"; }
|
static constexpr const char *message_name() { return "list_entities_cover_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
bool assumed_state{false};
|
bool assumed_state{false};
|
||||||
bool supports_position{false};
|
bool supports_position{false};
|
||||||
bool supports_tilt{false};
|
bool supports_tilt{false};
|
||||||
std::string device_class{};
|
std::string device_class{};
|
||||||
bool disabled_by_default{false};
|
|
||||||
std::string icon{};
|
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
bool supports_stop{false};
|
bool supports_stop{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@@ -561,14 +552,13 @@ class ListEntitiesCoverResponse : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class CoverStateResponse : public ProtoMessage {
|
class CoverStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 22;
|
static constexpr uint16_t MESSAGE_TYPE = 22;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 19;
|
static constexpr uint16_t ESTIMATED_SIZE = 19;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "cover_state_response"; }
|
static constexpr const char *message_name() { return "cover_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
uint32_t key{0};
|
|
||||||
enums::LegacyCoverState legacy_state{};
|
enums::LegacyCoverState legacy_state{};
|
||||||
float position{0.0f};
|
float position{0.0f};
|
||||||
float tilt{0.0f};
|
float tilt{0.0f};
|
||||||
@@ -608,24 +598,17 @@ class CoverCommandRequest : public ProtoMessage {
|
|||||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesFanResponse : public ProtoMessage {
|
class ListEntitiesFanResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 14;
|
static constexpr uint16_t MESSAGE_TYPE = 14;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 73;
|
static constexpr uint16_t ESTIMATED_SIZE = 73;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_fan_response"; }
|
static constexpr const char *message_name() { return "list_entities_fan_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
bool supports_oscillation{false};
|
bool supports_oscillation{false};
|
||||||
bool supports_speed{false};
|
bool supports_speed{false};
|
||||||
bool supports_direction{false};
|
bool supports_direction{false};
|
||||||
int32_t supported_speed_count{0};
|
int32_t supported_speed_count{0};
|
||||||
bool disabled_by_default{false};
|
|
||||||
std::string icon{};
|
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
std::vector<std::string> supported_preset_modes{};
|
std::vector<std::string> supported_preset_modes{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@@ -638,14 +621,13 @@ class ListEntitiesFanResponse : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class FanStateResponse : public ProtoMessage {
|
class FanStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 23;
|
static constexpr uint16_t MESSAGE_TYPE = 23;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 26;
|
static constexpr uint16_t ESTIMATED_SIZE = 26;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "fan_state_response"; }
|
static constexpr const char *message_name() { return "fan_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
uint32_t key{0};
|
|
||||||
bool state{false};
|
bool state{false};
|
||||||
bool oscillating{false};
|
bool oscillating{false};
|
||||||
enums::FanSpeed speed{};
|
enums::FanSpeed speed{};
|
||||||
@@ -694,17 +676,13 @@ class FanCommandRequest : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesLightResponse : public ProtoMessage {
|
class ListEntitiesLightResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 15;
|
static constexpr uint16_t MESSAGE_TYPE = 15;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 85;
|
static constexpr uint16_t ESTIMATED_SIZE = 85;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_light_response"; }
|
static constexpr const char *message_name() { return "list_entities_light_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
std::vector<enums::ColorMode> supported_color_modes{};
|
std::vector<enums::ColorMode> supported_color_modes{};
|
||||||
bool legacy_supports_brightness{false};
|
bool legacy_supports_brightness{false};
|
||||||
bool legacy_supports_rgb{false};
|
bool legacy_supports_rgb{false};
|
||||||
@@ -713,9 +691,6 @@ class ListEntitiesLightResponse : public ProtoMessage {
|
|||||||
float min_mireds{0.0f};
|
float min_mireds{0.0f};
|
||||||
float max_mireds{0.0f};
|
float max_mireds{0.0f};
|
||||||
std::vector<std::string> effects{};
|
std::vector<std::string> effects{};
|
||||||
bool disabled_by_default{false};
|
|
||||||
std::string icon{};
|
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@@ -727,14 +702,13 @@ class ListEntitiesLightResponse : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class LightStateResponse : public ProtoMessage {
|
class LightStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 24;
|
static constexpr uint16_t MESSAGE_TYPE = 24;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 63;
|
static constexpr uint16_t ESTIMATED_SIZE = 63;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "light_state_response"; }
|
static constexpr const char *message_name() { return "light_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
uint32_t key{0};
|
|
||||||
bool state{false};
|
bool state{false};
|
||||||
float brightness{0.0f};
|
float brightness{0.0f};
|
||||||
enums::ColorMode color_mode{};
|
enums::ColorMode color_mode{};
|
||||||
@@ -803,26 +777,19 @@ class LightCommandRequest : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesSensorResponse : public ProtoMessage {
|
class ListEntitiesSensorResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 16;
|
static constexpr uint16_t MESSAGE_TYPE = 16;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 73;
|
static constexpr uint16_t ESTIMATED_SIZE = 73;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_sensor_response"; }
|
static constexpr const char *message_name() { return "list_entities_sensor_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
std::string icon{};
|
|
||||||
std::string unit_of_measurement{};
|
std::string unit_of_measurement{};
|
||||||
int32_t accuracy_decimals{0};
|
int32_t accuracy_decimals{0};
|
||||||
bool force_update{false};
|
bool force_update{false};
|
||||||
std::string device_class{};
|
std::string device_class{};
|
||||||
enums::SensorStateClass state_class{};
|
enums::SensorStateClass state_class{};
|
||||||
enums::SensorLastResetType legacy_last_reset_type{};
|
enums::SensorLastResetType legacy_last_reset_type{};
|
||||||
bool disabled_by_default{false};
|
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@@ -834,14 +801,13 @@ class ListEntitiesSensorResponse : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class SensorStateResponse : public ProtoMessage {
|
class SensorStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 25;
|
static constexpr uint16_t MESSAGE_TYPE = 25;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 12;
|
static constexpr uint16_t ESTIMATED_SIZE = 12;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "sensor_state_response"; }
|
static constexpr const char *message_name() { return "sensor_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
uint32_t key{0};
|
|
||||||
float state{0.0f};
|
float state{0.0f};
|
||||||
bool missing_state{false};
|
bool missing_state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
@@ -854,21 +820,14 @@ class SensorStateResponse : public ProtoMessage {
|
|||||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesSwitchResponse : public ProtoMessage {
|
class ListEntitiesSwitchResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 17;
|
static constexpr uint16_t MESSAGE_TYPE = 17;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 56;
|
static constexpr uint16_t ESTIMATED_SIZE = 56;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_switch_response"; }
|
static constexpr const char *message_name() { return "list_entities_switch_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
std::string icon{};
|
|
||||||
bool assumed_state{false};
|
bool assumed_state{false};
|
||||||
bool disabled_by_default{false};
|
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
std::string device_class{};
|
std::string device_class{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@@ -881,14 +840,13 @@ class ListEntitiesSwitchResponse : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class SwitchStateResponse : public ProtoMessage {
|
class SwitchStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 26;
|
static constexpr uint16_t MESSAGE_TYPE = 26;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 7;
|
static constexpr uint16_t ESTIMATED_SIZE = 7;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "switch_state_response"; }
|
static constexpr const char *message_name() { return "switch_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
uint32_t key{0};
|
|
||||||
bool state{false};
|
bool state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@@ -919,20 +877,13 @@ class SwitchCommandRequest : public ProtoMessage {
|
|||||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesTextSensorResponse : public ProtoMessage {
|
class ListEntitiesTextSensorResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 18;
|
static constexpr uint16_t MESSAGE_TYPE = 18;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 54;
|
static constexpr uint16_t ESTIMATED_SIZE = 54;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_text_sensor_response"; }
|
static constexpr const char *message_name() { return "list_entities_text_sensor_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
std::string icon{};
|
|
||||||
bool disabled_by_default{false};
|
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
std::string device_class{};
|
std::string device_class{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@@ -945,14 +896,13 @@ class ListEntitiesTextSensorResponse : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class TextSensorStateResponse : public ProtoMessage {
|
class TextSensorStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 27;
|
static constexpr uint16_t MESSAGE_TYPE = 27;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 16;
|
static constexpr uint16_t ESTIMATED_SIZE = 16;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "text_sensor_state_response"; }
|
static constexpr const char *message_name() { return "text_sensor_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
uint32_t key{0};
|
|
||||||
std::string state{};
|
std::string state{};
|
||||||
bool missing_state{false};
|
bool missing_state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
@@ -1045,8 +995,6 @@ class SubscribeHomeassistantServicesRequest : public ProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "subscribe_homeassistant_services_request"; }
|
static constexpr const char *message_name() { return "subscribe_homeassistant_services_request"; }
|
||||||
#endif
|
#endif
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1095,8 +1043,6 @@ class SubscribeHomeAssistantStatesRequest : public ProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "subscribe_home_assistant_states_request"; }
|
static constexpr const char *message_name() { return "subscribe_home_assistant_states_request"; }
|
||||||
#endif
|
#endif
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1149,8 +1095,6 @@ class GetTimeRequest : public ProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "get_time_request"; }
|
static constexpr const char *message_name() { return "get_time_request"; }
|
||||||
#endif
|
#endif
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -1249,20 +1193,13 @@ class ExecuteServiceRequest : public ProtoMessage {
|
|||||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesCameraResponse : public ProtoMessage {
|
class ListEntitiesCameraResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 43;
|
static constexpr uint16_t MESSAGE_TYPE = 43;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 45;
|
static constexpr uint16_t ESTIMATED_SIZE = 45;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_camera_response"; }
|
static constexpr const char *message_name() { return "list_entities_camera_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
bool disabled_by_default{false};
|
|
||||||
std::string icon{};
|
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@@ -1313,17 +1250,13 @@ class CameraImageRequest : public ProtoMessage {
|
|||||||
protected:
|
protected:
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesClimateResponse : public ProtoMessage {
|
class ListEntitiesClimateResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 46;
|
static constexpr uint16_t MESSAGE_TYPE = 46;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 151;
|
static constexpr uint16_t ESTIMATED_SIZE = 151;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_climate_response"; }
|
static constexpr const char *message_name() { return "list_entities_climate_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
bool supports_current_temperature{false};
|
bool supports_current_temperature{false};
|
||||||
bool supports_two_point_target_temperature{false};
|
bool supports_two_point_target_temperature{false};
|
||||||
std::vector<enums::ClimateMode> supported_modes{};
|
std::vector<enums::ClimateMode> supported_modes{};
|
||||||
@@ -1337,9 +1270,6 @@ class ListEntitiesClimateResponse : public ProtoMessage {
|
|||||||
std::vector<std::string> supported_custom_fan_modes{};
|
std::vector<std::string> supported_custom_fan_modes{};
|
||||||
std::vector<enums::ClimatePreset> supported_presets{};
|
std::vector<enums::ClimatePreset> supported_presets{};
|
||||||
std::vector<std::string> supported_custom_presets{};
|
std::vector<std::string> supported_custom_presets{};
|
||||||
bool disabled_by_default{false};
|
|
||||||
std::string icon{};
|
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
float visual_current_temperature_step{0.0f};
|
float visual_current_temperature_step{0.0f};
|
||||||
bool supports_current_humidity{false};
|
bool supports_current_humidity{false};
|
||||||
bool supports_target_humidity{false};
|
bool supports_target_humidity{false};
|
||||||
@@ -1356,14 +1286,13 @@ class ListEntitiesClimateResponse : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class ClimateStateResponse : public ProtoMessage {
|
class ClimateStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 47;
|
static constexpr uint16_t MESSAGE_TYPE = 47;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 65;
|
static constexpr uint16_t ESTIMATED_SIZE = 65;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "climate_state_response"; }
|
static constexpr const char *message_name() { return "climate_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
uint32_t key{0};
|
|
||||||
enums::ClimateMode mode{};
|
enums::ClimateMode mode{};
|
||||||
float current_temperature{0.0f};
|
float current_temperature{0.0f};
|
||||||
float target_temperature{0.0f};
|
float target_temperature{0.0f};
|
||||||
@@ -1430,23 +1359,16 @@ class ClimateCommandRequest : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesNumberResponse : public ProtoMessage {
|
class ListEntitiesNumberResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 49;
|
static constexpr uint16_t MESSAGE_TYPE = 49;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 80;
|
static constexpr uint16_t ESTIMATED_SIZE = 80;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_number_response"; }
|
static constexpr const char *message_name() { return "list_entities_number_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
std::string icon{};
|
|
||||||
float min_value{0.0f};
|
float min_value{0.0f};
|
||||||
float max_value{0.0f};
|
float max_value{0.0f};
|
||||||
float step{0.0f};
|
float step{0.0f};
|
||||||
bool disabled_by_default{false};
|
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
std::string unit_of_measurement{};
|
std::string unit_of_measurement{};
|
||||||
enums::NumberMode mode{};
|
enums::NumberMode mode{};
|
||||||
std::string device_class{};
|
std::string device_class{};
|
||||||
@@ -1461,14 +1383,13 @@ class ListEntitiesNumberResponse : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class NumberStateResponse : public ProtoMessage {
|
class NumberStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 50;
|
static constexpr uint16_t MESSAGE_TYPE = 50;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 12;
|
static constexpr uint16_t ESTIMATED_SIZE = 12;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "number_state_response"; }
|
static constexpr const char *message_name() { return "number_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
uint32_t key{0};
|
|
||||||
float state{0.0f};
|
float state{0.0f};
|
||||||
bool missing_state{false};
|
bool missing_state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
@@ -1499,21 +1420,14 @@ class NumberCommandRequest : public ProtoMessage {
|
|||||||
protected:
|
protected:
|
||||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesSelectResponse : public ProtoMessage {
|
class ListEntitiesSelectResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 52;
|
static constexpr uint16_t MESSAGE_TYPE = 52;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 63;
|
static constexpr uint16_t ESTIMATED_SIZE = 63;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_select_response"; }
|
static constexpr const char *message_name() { return "list_entities_select_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
std::string icon{};
|
|
||||||
std::vector<std::string> options{};
|
std::vector<std::string> options{};
|
||||||
bool disabled_by_default{false};
|
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@@ -1525,14 +1439,13 @@ class ListEntitiesSelectResponse : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class SelectStateResponse : public ProtoMessage {
|
class SelectStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 53;
|
static constexpr uint16_t MESSAGE_TYPE = 53;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 16;
|
static constexpr uint16_t ESTIMATED_SIZE = 16;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "select_state_response"; }
|
static constexpr const char *message_name() { return "select_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
uint32_t key{0};
|
|
||||||
std::string state{};
|
std::string state{};
|
||||||
bool missing_state{false};
|
bool missing_state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
@@ -1565,23 +1478,16 @@ class SelectCommandRequest : public ProtoMessage {
|
|||||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesSirenResponse : public ProtoMessage {
|
class ListEntitiesSirenResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 55;
|
static constexpr uint16_t MESSAGE_TYPE = 55;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 67;
|
static constexpr uint16_t ESTIMATED_SIZE = 67;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_siren_response"; }
|
static constexpr const char *message_name() { return "list_entities_siren_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
std::string icon{};
|
|
||||||
bool disabled_by_default{false};
|
|
||||||
std::vector<std::string> tones{};
|
std::vector<std::string> tones{};
|
||||||
bool supports_duration{false};
|
bool supports_duration{false};
|
||||||
bool supports_volume{false};
|
bool supports_volume{false};
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@@ -1593,14 +1499,13 @@ class ListEntitiesSirenResponse : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class SirenStateResponse : public ProtoMessage {
|
class SirenStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 56;
|
static constexpr uint16_t MESSAGE_TYPE = 56;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 7;
|
static constexpr uint16_t ESTIMATED_SIZE = 7;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "siren_state_response"; }
|
static constexpr const char *message_name() { return "siren_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
uint32_t key{0};
|
|
||||||
bool state{false};
|
bool state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@@ -1639,20 +1544,13 @@ class SirenCommandRequest : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesLockResponse : public ProtoMessage {
|
class ListEntitiesLockResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 58;
|
static constexpr uint16_t MESSAGE_TYPE = 58;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 60;
|
static constexpr uint16_t ESTIMATED_SIZE = 60;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_lock_response"; }
|
static constexpr const char *message_name() { return "list_entities_lock_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
std::string icon{};
|
|
||||||
bool disabled_by_default{false};
|
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
bool assumed_state{false};
|
bool assumed_state{false};
|
||||||
bool supports_open{false};
|
bool supports_open{false};
|
||||||
bool requires_code{false};
|
bool requires_code{false};
|
||||||
@@ -1668,14 +1566,13 @@ class ListEntitiesLockResponse : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class LockStateResponse : public ProtoMessage {
|
class LockStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 59;
|
static constexpr uint16_t MESSAGE_TYPE = 59;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 7;
|
static constexpr uint16_t ESTIMATED_SIZE = 7;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "lock_state_response"; }
|
static constexpr const char *message_name() { return "lock_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
uint32_t key{0};
|
|
||||||
enums::LockState state{};
|
enums::LockState state{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@@ -1709,20 +1606,13 @@ class LockCommandRequest : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesButtonResponse : public ProtoMessage {
|
class ListEntitiesButtonResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 61;
|
static constexpr uint16_t MESSAGE_TYPE = 61;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 54;
|
static constexpr uint16_t ESTIMATED_SIZE = 54;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_button_response"; }
|
static constexpr const char *message_name() { return "list_entities_button_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
std::string icon{};
|
|
||||||
bool disabled_by_default{false};
|
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
std::string device_class{};
|
std::string device_class{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@@ -1769,20 +1659,13 @@ class MediaPlayerSupportedFormat : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesMediaPlayerResponse : public ProtoMessage {
|
class ListEntitiesMediaPlayerResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 63;
|
static constexpr uint16_t MESSAGE_TYPE = 63;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 81;
|
static constexpr uint16_t ESTIMATED_SIZE = 81;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_media_player_response"; }
|
static constexpr const char *message_name() { return "list_entities_media_player_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
std::string icon{};
|
|
||||||
bool disabled_by_default{false};
|
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
bool supports_pause{false};
|
bool supports_pause{false};
|
||||||
std::vector<MediaPlayerSupportedFormat> supported_formats{};
|
std::vector<MediaPlayerSupportedFormat> supported_formats{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
@@ -1796,14 +1679,13 @@ class ListEntitiesMediaPlayerResponse : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class MediaPlayerStateResponse : public ProtoMessage {
|
class MediaPlayerStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 64;
|
static constexpr uint16_t MESSAGE_TYPE = 64;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 14;
|
static constexpr uint16_t ESTIMATED_SIZE = 14;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "media_player_state_response"; }
|
static constexpr const char *message_name() { return "media_player_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
uint32_t key{0};
|
|
||||||
enums::MediaPlayerState state{};
|
enums::MediaPlayerState state{};
|
||||||
float volume{0.0f};
|
float volume{0.0f};
|
||||||
bool muted{false};
|
bool muted{false};
|
||||||
@@ -2213,8 +2095,6 @@ class SubscribeBluetoothConnectionsFreeRequest : public ProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "subscribe_bluetooth_connections_free_request"; }
|
static constexpr const char *message_name() { return "subscribe_bluetooth_connections_free_request"; }
|
||||||
#endif
|
#endif
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2340,8 +2220,6 @@ class UnsubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "unsubscribe_bluetooth_le_advertisements_request"; }
|
static constexpr const char *message_name() { return "unsubscribe_bluetooth_le_advertisements_request"; }
|
||||||
#endif
|
#endif
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2608,8 +2486,6 @@ class VoiceAssistantConfigurationRequest : public ProtoMessage {
|
|||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "voice_assistant_configuration_request"; }
|
static constexpr const char *message_name() { return "voice_assistant_configuration_request"; }
|
||||||
#endif
|
#endif
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
#endif
|
#endif
|
||||||
@@ -2653,20 +2529,13 @@ class VoiceAssistantSetConfiguration : public ProtoMessage {
|
|||||||
protected:
|
protected:
|
||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesAlarmControlPanelResponse : public ProtoMessage {
|
class ListEntitiesAlarmControlPanelResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 94;
|
static constexpr uint16_t MESSAGE_TYPE = 94;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 53;
|
static constexpr uint16_t ESTIMATED_SIZE = 53;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_alarm_control_panel_response"; }
|
static constexpr const char *message_name() { return "list_entities_alarm_control_panel_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
std::string icon{};
|
|
||||||
bool disabled_by_default{false};
|
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
uint32_t supported_features{0};
|
uint32_t supported_features{0};
|
||||||
bool requires_code{false};
|
bool requires_code{false};
|
||||||
bool requires_code_to_arm{false};
|
bool requires_code_to_arm{false};
|
||||||
@@ -2681,14 +2550,13 @@ class ListEntitiesAlarmControlPanelResponse : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class AlarmControlPanelStateResponse : public ProtoMessage {
|
class AlarmControlPanelStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 95;
|
static constexpr uint16_t MESSAGE_TYPE = 95;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 7;
|
static constexpr uint16_t ESTIMATED_SIZE = 7;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "alarm_control_panel_state_response"; }
|
static constexpr const char *message_name() { return "alarm_control_panel_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
uint32_t key{0};
|
|
||||||
enums::AlarmControlPanelState state{};
|
enums::AlarmControlPanelState state{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@@ -2721,20 +2589,13 @@ class AlarmControlPanelCommandRequest : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesTextResponse : public ProtoMessage {
|
class ListEntitiesTextResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 97;
|
static constexpr uint16_t MESSAGE_TYPE = 97;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 64;
|
static constexpr uint16_t ESTIMATED_SIZE = 64;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_text_response"; }
|
static constexpr const char *message_name() { return "list_entities_text_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
std::string icon{};
|
|
||||||
bool disabled_by_default{false};
|
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
uint32_t min_length{0};
|
uint32_t min_length{0};
|
||||||
uint32_t max_length{0};
|
uint32_t max_length{0};
|
||||||
std::string pattern{};
|
std::string pattern{};
|
||||||
@@ -2750,14 +2611,13 @@ class ListEntitiesTextResponse : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class TextStateResponse : public ProtoMessage {
|
class TextStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 98;
|
static constexpr uint16_t MESSAGE_TYPE = 98;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 16;
|
static constexpr uint16_t ESTIMATED_SIZE = 16;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "text_state_response"; }
|
static constexpr const char *message_name() { return "text_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
uint32_t key{0};
|
|
||||||
std::string state{};
|
std::string state{};
|
||||||
bool missing_state{false};
|
bool missing_state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
@@ -2790,20 +2650,13 @@ class TextCommandRequest : public ProtoMessage {
|
|||||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesDateResponse : public ProtoMessage {
|
class ListEntitiesDateResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 100;
|
static constexpr uint16_t MESSAGE_TYPE = 100;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 45;
|
static constexpr uint16_t ESTIMATED_SIZE = 45;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_date_response"; }
|
static constexpr const char *message_name() { return "list_entities_date_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
std::string icon{};
|
|
||||||
bool disabled_by_default{false};
|
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@@ -2815,14 +2668,13 @@ class ListEntitiesDateResponse : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class DateStateResponse : public ProtoMessage {
|
class DateStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 101;
|
static constexpr uint16_t MESSAGE_TYPE = 101;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 19;
|
static constexpr uint16_t ESTIMATED_SIZE = 19;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "date_state_response"; }
|
static constexpr const char *message_name() { return "date_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
uint32_t key{0};
|
|
||||||
bool missing_state{false};
|
bool missing_state{false};
|
||||||
uint32_t year{0};
|
uint32_t year{0};
|
||||||
uint32_t month{0};
|
uint32_t month{0};
|
||||||
@@ -2858,20 +2710,13 @@ class DateCommandRequest : public ProtoMessage {
|
|||||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesTimeResponse : public ProtoMessage {
|
class ListEntitiesTimeResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 103;
|
static constexpr uint16_t MESSAGE_TYPE = 103;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 45;
|
static constexpr uint16_t ESTIMATED_SIZE = 45;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_time_response"; }
|
static constexpr const char *message_name() { return "list_entities_time_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
std::string icon{};
|
|
||||||
bool disabled_by_default{false};
|
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@@ -2883,14 +2728,13 @@ class ListEntitiesTimeResponse : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class TimeStateResponse : public ProtoMessage {
|
class TimeStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 104;
|
static constexpr uint16_t MESSAGE_TYPE = 104;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 19;
|
static constexpr uint16_t ESTIMATED_SIZE = 19;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "time_state_response"; }
|
static constexpr const char *message_name() { return "time_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
uint32_t key{0};
|
|
||||||
bool missing_state{false};
|
bool missing_state{false};
|
||||||
uint32_t hour{0};
|
uint32_t hour{0};
|
||||||
uint32_t minute{0};
|
uint32_t minute{0};
|
||||||
@@ -2926,20 +2770,13 @@ class TimeCommandRequest : public ProtoMessage {
|
|||||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesEventResponse : public ProtoMessage {
|
class ListEntitiesEventResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 107;
|
static constexpr uint16_t MESSAGE_TYPE = 107;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 72;
|
static constexpr uint16_t ESTIMATED_SIZE = 72;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_event_response"; }
|
static constexpr const char *message_name() { return "list_entities_event_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
std::string icon{};
|
|
||||||
bool disabled_by_default{false};
|
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
std::string device_class{};
|
std::string device_class{};
|
||||||
std::vector<std::string> event_types{};
|
std::vector<std::string> event_types{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
@@ -2953,14 +2790,13 @@ class ListEntitiesEventResponse : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class EventResponse : public ProtoMessage {
|
class EventResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 108;
|
static constexpr uint16_t MESSAGE_TYPE = 108;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 14;
|
static constexpr uint16_t ESTIMATED_SIZE = 14;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "event_response"; }
|
static constexpr const char *message_name() { return "event_response"; }
|
||||||
#endif
|
#endif
|
||||||
uint32_t key{0};
|
|
||||||
std::string event_type{};
|
std::string event_type{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@@ -2972,20 +2808,13 @@ class EventResponse : public ProtoMessage {
|
|||||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesValveResponse : public ProtoMessage {
|
class ListEntitiesValveResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 109;
|
static constexpr uint16_t MESSAGE_TYPE = 109;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 60;
|
static constexpr uint16_t ESTIMATED_SIZE = 60;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_valve_response"; }
|
static constexpr const char *message_name() { return "list_entities_valve_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
std::string icon{};
|
|
||||||
bool disabled_by_default{false};
|
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
std::string device_class{};
|
std::string device_class{};
|
||||||
bool assumed_state{false};
|
bool assumed_state{false};
|
||||||
bool supports_position{false};
|
bool supports_position{false};
|
||||||
@@ -3001,14 +2830,13 @@ class ListEntitiesValveResponse : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class ValveStateResponse : public ProtoMessage {
|
class ValveStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 110;
|
static constexpr uint16_t MESSAGE_TYPE = 110;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 12;
|
static constexpr uint16_t ESTIMATED_SIZE = 12;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "valve_state_response"; }
|
static constexpr const char *message_name() { return "valve_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
uint32_t key{0};
|
|
||||||
float position{0.0f};
|
float position{0.0f};
|
||||||
enums::ValveOperation current_operation{};
|
enums::ValveOperation current_operation{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
@@ -3042,20 +2870,13 @@ class ValveCommandRequest : public ProtoMessage {
|
|||||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesDateTimeResponse : public ProtoMessage {
|
class ListEntitiesDateTimeResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 112;
|
static constexpr uint16_t MESSAGE_TYPE = 112;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 45;
|
static constexpr uint16_t ESTIMATED_SIZE = 45;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_date_time_response"; }
|
static constexpr const char *message_name() { return "list_entities_date_time_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
std::string icon{};
|
|
||||||
bool disabled_by_default{false};
|
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@@ -3067,14 +2888,13 @@ class ListEntitiesDateTimeResponse : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class DateTimeStateResponse : public ProtoMessage {
|
class DateTimeStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 113;
|
static constexpr uint16_t MESSAGE_TYPE = 113;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 12;
|
static constexpr uint16_t ESTIMATED_SIZE = 12;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "date_time_state_response"; }
|
static constexpr const char *message_name() { return "date_time_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
uint32_t key{0};
|
|
||||||
bool missing_state{false};
|
bool missing_state{false};
|
||||||
uint32_t epoch_seconds{0};
|
uint32_t epoch_seconds{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
@@ -3105,20 +2925,13 @@ class DateTimeCommandRequest : public ProtoMessage {
|
|||||||
protected:
|
protected:
|
||||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesUpdateResponse : public ProtoMessage {
|
class ListEntitiesUpdateResponse : public InfoResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 116;
|
static constexpr uint16_t MESSAGE_TYPE = 116;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 54;
|
static constexpr uint16_t ESTIMATED_SIZE = 54;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "list_entities_update_response"; }
|
static constexpr const char *message_name() { return "list_entities_update_response"; }
|
||||||
#endif
|
#endif
|
||||||
std::string object_id{};
|
|
||||||
uint32_t key{0};
|
|
||||||
std::string name{};
|
|
||||||
std::string unique_id{};
|
|
||||||
std::string icon{};
|
|
||||||
bool disabled_by_default{false};
|
|
||||||
enums::EntityCategory entity_category{};
|
|
||||||
std::string device_class{};
|
std::string device_class{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void calculate_size(uint32_t &total_size) const override;
|
void calculate_size(uint32_t &total_size) const override;
|
||||||
@@ -3131,14 +2944,13 @@ class ListEntitiesUpdateResponse : public ProtoMessage {
|
|||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class UpdateStateResponse : public ProtoMessage {
|
class UpdateStateResponse : public StateResponseProtoMessage {
|
||||||
public:
|
public:
|
||||||
static constexpr uint16_t MESSAGE_TYPE = 117;
|
static constexpr uint16_t MESSAGE_TYPE = 117;
|
||||||
static constexpr uint16_t ESTIMATED_SIZE = 61;
|
static constexpr uint16_t ESTIMATED_SIZE = 61;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
static constexpr const char *message_name() { return "update_state_response"; }
|
static constexpr const char *message_name() { return "update_state_response"; }
|
||||||
#endif
|
#endif
|
||||||
uint32_t key{0};
|
|
||||||
bool missing_state{false};
|
bool missing_state{false};
|
||||||
bool in_progress{false};
|
bool in_progress{false};
|
||||||
bool has_progress{false};
|
bool has_progress{false};
|
||||||
|
@@ -620,544 +620,300 @@ void APIServerConnection::on_ping_request(const PingRequest &msg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
void APIServerConnection::on_device_info_request(const DeviceInfoRequest &msg) {
|
void APIServerConnection::on_device_info_request(const DeviceInfoRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_connection_setup_()) {
|
||||||
this->on_no_setup_connection();
|
DeviceInfoResponse ret = this->device_info(msg);
|
||||||
return;
|
if (!this->send_message(ret)) {
|
||||||
}
|
this->on_fatal_error();
|
||||||
DeviceInfoResponse ret = this->device_info(msg);
|
}
|
||||||
if (!this->send_message(ret)) {
|
|
||||||
this->on_fatal_error();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void APIServerConnection::on_list_entities_request(const ListEntitiesRequest &msg) {
|
void APIServerConnection::on_list_entities_request(const ListEntitiesRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->list_entities(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->list_entities(msg);
|
|
||||||
}
|
}
|
||||||
void APIServerConnection::on_subscribe_states_request(const SubscribeStatesRequest &msg) {
|
void APIServerConnection::on_subscribe_states_request(const SubscribeStatesRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->subscribe_states(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->subscribe_states(msg);
|
|
||||||
}
|
}
|
||||||
void APIServerConnection::on_subscribe_logs_request(const SubscribeLogsRequest &msg) {
|
void APIServerConnection::on_subscribe_logs_request(const SubscribeLogsRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->subscribe_logs(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->subscribe_logs(msg);
|
|
||||||
}
|
}
|
||||||
void APIServerConnection::on_subscribe_homeassistant_services_request(
|
void APIServerConnection::on_subscribe_homeassistant_services_request(
|
||||||
const SubscribeHomeassistantServicesRequest &msg) {
|
const SubscribeHomeassistantServicesRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->subscribe_homeassistant_services(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->subscribe_homeassistant_services(msg);
|
|
||||||
}
|
}
|
||||||
void APIServerConnection::on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) {
|
void APIServerConnection::on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->subscribe_home_assistant_states(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->subscribe_home_assistant_states(msg);
|
|
||||||
}
|
}
|
||||||
void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) {
|
void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_connection_setup_()) {
|
||||||
this->on_no_setup_connection();
|
GetTimeResponse ret = this->get_time(msg);
|
||||||
return;
|
if (!this->send_message(ret)) {
|
||||||
}
|
this->on_fatal_error();
|
||||||
GetTimeResponse ret = this->get_time(msg);
|
}
|
||||||
if (!this->send_message(ret)) {
|
|
||||||
this->on_fatal_error();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void APIServerConnection::on_execute_service_request(const ExecuteServiceRequest &msg) {
|
void APIServerConnection::on_execute_service_request(const ExecuteServiceRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->execute_service(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->execute_service(msg);
|
|
||||||
}
|
}
|
||||||
#ifdef USE_API_NOISE
|
#ifdef USE_API_NOISE
|
||||||
void APIServerConnection::on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) {
|
void APIServerConnection::on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
NoiseEncryptionSetKeyResponse ret = this->noise_encryption_set_key(msg);
|
||||||
return;
|
if (!this->send_message(ret)) {
|
||||||
}
|
this->on_fatal_error();
|
||||||
if (!this->is_authenticated()) {
|
}
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
NoiseEncryptionSetKeyResponse ret = this->noise_encryption_set_key(msg);
|
|
||||||
if (!this->send_message(ret)) {
|
|
||||||
this->on_fatal_error();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BUTTON
|
#ifdef USE_BUTTON
|
||||||
void APIServerConnection::on_button_command_request(const ButtonCommandRequest &msg) {
|
void APIServerConnection::on_button_command_request(const ButtonCommandRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->button_command(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->button_command(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_ESP32_CAMERA
|
||||||
void APIServerConnection::on_camera_image_request(const CameraImageRequest &msg) {
|
void APIServerConnection::on_camera_image_request(const CameraImageRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->camera_image(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->camera_image(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
void APIServerConnection::on_climate_command_request(const ClimateCommandRequest &msg) {
|
void APIServerConnection::on_climate_command_request(const ClimateCommandRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->climate_command(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->climate_command(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_COVER
|
#ifdef USE_COVER
|
||||||
void APIServerConnection::on_cover_command_request(const CoverCommandRequest &msg) {
|
void APIServerConnection::on_cover_command_request(const CoverCommandRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->cover_command(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->cover_command(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
void APIServerConnection::on_date_command_request(const DateCommandRequest &msg) {
|
void APIServerConnection::on_date_command_request(const DateCommandRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->date_command(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->date_command(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_DATETIME
|
#ifdef USE_DATETIME_DATETIME
|
||||||
void APIServerConnection::on_date_time_command_request(const DateTimeCommandRequest &msg) {
|
void APIServerConnection::on_date_time_command_request(const DateTimeCommandRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->datetime_command(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->datetime_command(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
void APIServerConnection::on_fan_command_request(const FanCommandRequest &msg) {
|
void APIServerConnection::on_fan_command_request(const FanCommandRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->fan_command(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->fan_command(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
void APIServerConnection::on_light_command_request(const LightCommandRequest &msg) {
|
void APIServerConnection::on_light_command_request(const LightCommandRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->light_command(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->light_command(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LOCK
|
#ifdef USE_LOCK
|
||||||
void APIServerConnection::on_lock_command_request(const LockCommandRequest &msg) {
|
void APIServerConnection::on_lock_command_request(const LockCommandRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->lock_command(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->lock_command(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_MEDIA_PLAYER
|
#ifdef USE_MEDIA_PLAYER
|
||||||
void APIServerConnection::on_media_player_command_request(const MediaPlayerCommandRequest &msg) {
|
void APIServerConnection::on_media_player_command_request(const MediaPlayerCommandRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->media_player_command(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->media_player_command(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_NUMBER
|
||||||
void APIServerConnection::on_number_command_request(const NumberCommandRequest &msg) {
|
void APIServerConnection::on_number_command_request(const NumberCommandRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->number_command(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->number_command(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
void APIServerConnection::on_select_command_request(const SelectCommandRequest &msg) {
|
void APIServerConnection::on_select_command_request(const SelectCommandRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->select_command(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->select_command(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SIREN
|
#ifdef USE_SIREN
|
||||||
void APIServerConnection::on_siren_command_request(const SirenCommandRequest &msg) {
|
void APIServerConnection::on_siren_command_request(const SirenCommandRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->siren_command(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->siren_command(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SWITCH
|
#ifdef USE_SWITCH
|
||||||
void APIServerConnection::on_switch_command_request(const SwitchCommandRequest &msg) {
|
void APIServerConnection::on_switch_command_request(const SwitchCommandRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->switch_command(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->switch_command(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
void APIServerConnection::on_text_command_request(const TextCommandRequest &msg) {
|
void APIServerConnection::on_text_command_request(const TextCommandRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->text_command(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->text_command(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_TIME
|
#ifdef USE_DATETIME_TIME
|
||||||
void APIServerConnection::on_time_command_request(const TimeCommandRequest &msg) {
|
void APIServerConnection::on_time_command_request(const TimeCommandRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->time_command(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->time_command(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_UPDATE
|
#ifdef USE_UPDATE
|
||||||
void APIServerConnection::on_update_command_request(const UpdateCommandRequest &msg) {
|
void APIServerConnection::on_update_command_request(const UpdateCommandRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->update_command(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->update_command(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VALVE
|
#ifdef USE_VALVE
|
||||||
void APIServerConnection::on_valve_command_request(const ValveCommandRequest &msg) {
|
void APIServerConnection::on_valve_command_request(const ValveCommandRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->valve_command(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->valve_command(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
|
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
|
||||||
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
|
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->subscribe_bluetooth_le_advertisements(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->subscribe_bluetooth_le_advertisements(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void APIServerConnection::on_bluetooth_device_request(const BluetoothDeviceRequest &msg) {
|
void APIServerConnection::on_bluetooth_device_request(const BluetoothDeviceRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->bluetooth_device_request(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->bluetooth_device_request(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void APIServerConnection::on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &msg) {
|
void APIServerConnection::on_bluetooth_gatt_get_services_request(const BluetoothGATTGetServicesRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->bluetooth_gatt_get_services(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->bluetooth_gatt_get_services(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void APIServerConnection::on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &msg) {
|
void APIServerConnection::on_bluetooth_gatt_read_request(const BluetoothGATTReadRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->bluetooth_gatt_read(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->bluetooth_gatt_read(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void APIServerConnection::on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &msg) {
|
void APIServerConnection::on_bluetooth_gatt_write_request(const BluetoothGATTWriteRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->bluetooth_gatt_write(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->bluetooth_gatt_write(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void APIServerConnection::on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &msg) {
|
void APIServerConnection::on_bluetooth_gatt_read_descriptor_request(const BluetoothGATTReadDescriptorRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->bluetooth_gatt_read_descriptor(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->bluetooth_gatt_read_descriptor(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void APIServerConnection::on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &msg) {
|
void APIServerConnection::on_bluetooth_gatt_write_descriptor_request(const BluetoothGATTWriteDescriptorRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->bluetooth_gatt_write_descriptor(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->bluetooth_gatt_write_descriptor(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void APIServerConnection::on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) {
|
void APIServerConnection::on_bluetooth_gatt_notify_request(const BluetoothGATTNotifyRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->bluetooth_gatt_notify(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->bluetooth_gatt_notify(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void APIServerConnection::on_subscribe_bluetooth_connections_free_request(
|
void APIServerConnection::on_subscribe_bluetooth_connections_free_request(
|
||||||
const SubscribeBluetoothConnectionsFreeRequest &msg) {
|
const SubscribeBluetoothConnectionsFreeRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
BluetoothConnectionsFreeResponse ret = this->subscribe_bluetooth_connections_free(msg);
|
||||||
return;
|
if (!this->send_message(ret)) {
|
||||||
}
|
this->on_fatal_error();
|
||||||
if (!this->is_authenticated()) {
|
}
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
BluetoothConnectionsFreeResponse ret = this->subscribe_bluetooth_connections_free(msg);
|
|
||||||
if (!this->send_message(ret)) {
|
|
||||||
this->on_fatal_error();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void APIServerConnection::on_unsubscribe_bluetooth_le_advertisements_request(
|
void APIServerConnection::on_unsubscribe_bluetooth_le_advertisements_request(
|
||||||
const UnsubscribeBluetoothLEAdvertisementsRequest &msg) {
|
const UnsubscribeBluetoothLEAdvertisementsRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->unsubscribe_bluetooth_le_advertisements(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->unsubscribe_bluetooth_le_advertisements(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void APIServerConnection::on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) {
|
void APIServerConnection::on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->bluetooth_scanner_set_mode(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->bluetooth_scanner_set_mode(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) {
|
void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->subscribe_voice_assistant(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->subscribe_voice_assistant(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
void APIServerConnection::on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) {
|
void APIServerConnection::on_voice_assistant_configuration_request(const VoiceAssistantConfigurationRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
VoiceAssistantConfigurationResponse ret = this->voice_assistant_get_configuration(msg);
|
||||||
return;
|
if (!this->send_message(ret)) {
|
||||||
}
|
this->on_fatal_error();
|
||||||
if (!this->is_authenticated()) {
|
}
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
VoiceAssistantConfigurationResponse ret = this->voice_assistant_get_configuration(msg);
|
|
||||||
if (!this->send_message(ret)) {
|
|
||||||
this->on_fatal_error();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
void APIServerConnection::on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) {
|
void APIServerConnection::on_voice_assistant_set_configuration(const VoiceAssistantSetConfiguration &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->voice_assistant_set_configuration(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->voice_assistant_set_configuration(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
void APIServerConnection::on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) {
|
void APIServerConnection::on_alarm_control_panel_command_request(const AlarmControlPanelCommandRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (this->check_authenticated_()) {
|
||||||
this->on_no_setup_connection();
|
this->alarm_control_panel_command(msg);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->alarm_control_panel_command(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -106,7 +106,7 @@ void APIServer::setup() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
this->last_connected_ = millis();
|
this->last_connected_ = App.get_loop_component_start_time();
|
||||||
|
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_ESP32_CAMERA
|
||||||
if (esp32_camera::global_esp32_camera != nullptr && !esp32_camera::global_esp32_camera->is_internal()) {
|
if (esp32_camera::global_esp32_camera != nullptr && !esp32_camera::global_esp32_camera->is_internal()) {
|
||||||
@@ -164,7 +164,7 @@ void APIServer::loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this->reboot_timeout_ != 0) {
|
if (this->reboot_timeout_ != 0) {
|
||||||
const uint32_t now = millis();
|
const uint32_t now = App.get_loop_component_start_time();
|
||||||
if (!this->is_connected()) {
|
if (!this->is_connected()) {
|
||||||
if (now - this->last_connected_ > this->reboot_timeout_) {
|
if (now - this->last_connected_ > this->reboot_timeout_) {
|
||||||
ESP_LOGE(TAG, "No client connected; rebooting");
|
ESP_LOGE(TAG, "No client connected; rebooting");
|
||||||
|
@@ -142,19 +142,27 @@ class APIServer : public Component, public Controller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool shutting_down_ = false;
|
// Pointers and pointer-like types first (4 bytes each)
|
||||||
std::unique_ptr<socket::Socket> socket_ = nullptr;
|
std::unique_ptr<socket::Socket> socket_ = nullptr;
|
||||||
uint16_t port_{6053};
|
Trigger<std::string, std::string> *client_connected_trigger_ = new Trigger<std::string, std::string>();
|
||||||
|
Trigger<std::string, std::string> *client_disconnected_trigger_ = new Trigger<std::string, std::string>();
|
||||||
|
|
||||||
|
// 4-byte aligned types
|
||||||
uint32_t reboot_timeout_{300000};
|
uint32_t reboot_timeout_{300000};
|
||||||
uint32_t batch_delay_{100};
|
uint32_t batch_delay_{100};
|
||||||
uint32_t last_connected_{0};
|
uint32_t last_connected_{0};
|
||||||
|
|
||||||
|
// Vectors and strings (12 bytes each on 32-bit)
|
||||||
std::vector<std::unique_ptr<APIConnection>> clients_;
|
std::vector<std::unique_ptr<APIConnection>> clients_;
|
||||||
std::string password_;
|
std::string password_;
|
||||||
std::vector<uint8_t> shared_write_buffer_; // Shared proto write buffer for all connections
|
std::vector<uint8_t> shared_write_buffer_; // Shared proto write buffer for all connections
|
||||||
std::vector<HomeAssistantStateSubscription> state_subs_;
|
std::vector<HomeAssistantStateSubscription> state_subs_;
|
||||||
std::vector<UserServiceDescriptor *> user_services_;
|
std::vector<UserServiceDescriptor *> user_services_;
|
||||||
Trigger<std::string, std::string> *client_connected_trigger_ = new Trigger<std::string, std::string>();
|
|
||||||
Trigger<std::string, std::string> *client_disconnected_trigger_ = new Trigger<std::string, std::string>();
|
// Group smaller types together
|
||||||
|
uint16_t port_{6053};
|
||||||
|
bool shutting_down_ = false;
|
||||||
|
// 3 bytes used, 1 byte padding
|
||||||
|
|
||||||
#ifdef USE_API_NOISE
|
#ifdef USE_API_NOISE
|
||||||
std::shared_ptr<APINoiseContext> noise_ctx_ = std::make_shared<APINoiseContext>();
|
std::shared_ptr<APINoiseContext> noise_ctx_ = std::make_shared<APINoiseContext>();
|
||||||
|
@@ -216,7 +216,7 @@ class ProtoWriteBuffer {
|
|||||||
this->buffer_->insert(this->buffer_->end(), data, data + len);
|
this->buffer_->insert(this->buffer_->end(), data, data + len);
|
||||||
}
|
}
|
||||||
void encode_string(uint32_t field_id, const std::string &value, bool force = false) {
|
void encode_string(uint32_t field_id, const std::string &value, bool force = false) {
|
||||||
this->encode_string(field_id, value.data(), value.size());
|
this->encode_string(field_id, value.data(), value.size(), force);
|
||||||
}
|
}
|
||||||
void encode_bytes(uint32_t field_id, const uint8_t *data, size_t len, bool force = false) {
|
void encode_bytes(uint32_t field_id, const uint8_t *data, size_t len, bool force = false) {
|
||||||
this->encode_string(field_id, reinterpret_cast<const char *>(data), len, force);
|
this->encode_string(field_id, reinterpret_cast<const char *>(data), len, force);
|
||||||
@@ -327,9 +327,11 @@ class ProtoWriteBuffer {
|
|||||||
class ProtoMessage {
|
class ProtoMessage {
|
||||||
public:
|
public:
|
||||||
virtual ~ProtoMessage() = default;
|
virtual ~ProtoMessage() = default;
|
||||||
virtual void encode(ProtoWriteBuffer buffer) const = 0;
|
// Default implementation for messages with no fields
|
||||||
|
virtual void encode(ProtoWriteBuffer buffer) const {}
|
||||||
void decode(const uint8_t *buffer, size_t length);
|
void decode(const uint8_t *buffer, size_t length);
|
||||||
virtual void calculate_size(uint32_t &total_size) const = 0;
|
// Default implementation for messages with no fields
|
||||||
|
virtual void calculate_size(uint32_t &total_size) const {}
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
std::string dump() const;
|
std::string dump() const;
|
||||||
virtual void dump_to(std::string &out) const = 0;
|
virtual void dump_to(std::string &out) const = 0;
|
||||||
@@ -377,6 +379,26 @@ class ProtoService {
|
|||||||
// Send the buffer
|
// Send the buffer
|
||||||
return this->send_buffer(buffer, message_type);
|
return this->send_buffer(buffer, message_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Authentication helper methods
|
||||||
|
bool check_connection_setup_() {
|
||||||
|
if (!this->is_connection_setup()) {
|
||||||
|
this->on_no_setup_connection();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool check_authenticated_() {
|
||||||
|
if (!this->check_connection_setup_()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!this->is_authenticated()) {
|
||||||
|
this->on_unauthenticated_access();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
|
@@ -21,8 +21,8 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
@coroutine_with_priority(200.0)
|
@coroutine_with_priority(200.0)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
if CORE.is_esp32 or CORE.is_libretiny:
|
if CORE.is_esp32 or CORE.is_libretiny:
|
||||||
# https://github.com/esphome/AsyncTCP/blob/master/library.json
|
# https://github.com/ESP32Async/AsyncTCP
|
||||||
cg.add_library("esphome/AsyncTCP-esphome", "2.1.4")
|
cg.add_library("ESP32Async/AsyncTCP", "3.4.4")
|
||||||
elif CORE.is_esp8266:
|
elif CORE.is_esp8266:
|
||||||
# https://github.com/esphome/ESPAsyncTCP
|
# https://github.com/ESP32Async/ESPAsyncTCP
|
||||||
cg.add_library("esphome/ESPAsyncTCP-esphome", "2.0.0")
|
cg.add_library("ESP32Async/ESPAsyncTCP", "2.0.0")
|
||||||
|
@@ -86,7 +86,7 @@ bool AudioTransferBuffer::reallocate(size_t new_buffer_size) {
|
|||||||
bool AudioTransferBuffer::allocate_buffer_(size_t buffer_size) {
|
bool AudioTransferBuffer::allocate_buffer_(size_t buffer_size) {
|
||||||
this->buffer_size_ = buffer_size;
|
this->buffer_size_ = buffer_size;
|
||||||
|
|
||||||
RAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
RAMAllocator<uint8_t> allocator;
|
||||||
|
|
||||||
this->buffer_ = allocator.allocate(this->buffer_size_);
|
this->buffer_ = allocator.allocate(this->buffer_size_);
|
||||||
if (this->buffer_ == nullptr) {
|
if (this->buffer_ == nullptr) {
|
||||||
@@ -101,7 +101,7 @@ bool AudioTransferBuffer::allocate_buffer_(size_t buffer_size) {
|
|||||||
|
|
||||||
void AudioTransferBuffer::deallocate_buffer_() {
|
void AudioTransferBuffer::deallocate_buffer_() {
|
||||||
if (this->buffer_ != nullptr) {
|
if (this->buffer_ != nullptr) {
|
||||||
RAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
RAMAllocator<uint8_t> allocator;
|
||||||
allocator.deallocate(this->buffer_, this->buffer_size_);
|
allocator.deallocate(this->buffer_, this->buffer_size_);
|
||||||
this->buffer_ = nullptr;
|
this->buffer_ = nullptr;
|
||||||
this->data_start_ = nullptr;
|
this->data_start_ = nullptr;
|
||||||
|
@@ -480,7 +480,11 @@ void BedJetHub::set_clock(uint8_t hour, uint8_t minute) {
|
|||||||
|
|
||||||
/* Internal */
|
/* Internal */
|
||||||
|
|
||||||
void BedJetHub::loop() {}
|
void BedJetHub::loop() {
|
||||||
|
// Parent BLEClientNode has a loop() method, but this component uses
|
||||||
|
// polling via update() and BLE callbacks so loop isn't needed
|
||||||
|
this->disable_loop();
|
||||||
|
}
|
||||||
void BedJetHub::update() { this->dispatch_status_(); }
|
void BedJetHub::update() { this->dispatch_status_(); }
|
||||||
|
|
||||||
void BedJetHub::dump_config() {
|
void BedJetHub::dump_config() {
|
||||||
|
@@ -83,7 +83,11 @@ void BedJetClimate::reset_state_() {
|
|||||||
this->publish_state();
|
this->publish_state();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BedJetClimate::loop() {}
|
void BedJetClimate::loop() {
|
||||||
|
// This component is controlled via the parent BedJetHub
|
||||||
|
// Empty loop not needed, disable to save CPU cycles
|
||||||
|
this->disable_loop();
|
||||||
|
}
|
||||||
|
|
||||||
void BedJetClimate::control(const ClimateCall &call) {
|
void BedJetClimate::control(const ClimateCall &call) {
|
||||||
ESP_LOGD(TAG, "Received BedJetClimate::control");
|
ESP_LOGD(TAG, "Received BedJetClimate::control");
|
||||||
|
@@ -11,7 +11,11 @@ namespace ble_client {
|
|||||||
|
|
||||||
static const char *const TAG = "ble_rssi_sensor";
|
static const char *const TAG = "ble_rssi_sensor";
|
||||||
|
|
||||||
void BLEClientRSSISensor::loop() {}
|
void BLEClientRSSISensor::loop() {
|
||||||
|
// Parent BLEClientNode has a loop() method, but this component uses
|
||||||
|
// polling via update() and BLE GAP callbacks so loop isn't needed
|
||||||
|
this->disable_loop();
|
||||||
|
}
|
||||||
|
|
||||||
void BLEClientRSSISensor::dump_config() {
|
void BLEClientRSSISensor::dump_config() {
|
||||||
LOG_SENSOR("", "BLE Client RSSI Sensor", this);
|
LOG_SENSOR("", "BLE Client RSSI Sensor", this);
|
||||||
|
@@ -11,7 +11,11 @@ namespace ble_client {
|
|||||||
|
|
||||||
static const char *const TAG = "ble_sensor";
|
static const char *const TAG = "ble_sensor";
|
||||||
|
|
||||||
void BLESensor::loop() {}
|
void BLESensor::loop() {
|
||||||
|
// Parent BLEClientNode has a loop() method, but this component uses
|
||||||
|
// polling via update() and BLE callbacks so loop isn't needed
|
||||||
|
this->disable_loop();
|
||||||
|
}
|
||||||
|
|
||||||
void BLESensor::dump_config() {
|
void BLESensor::dump_config() {
|
||||||
LOG_SENSOR("", "BLE Sensor", this);
|
LOG_SENSOR("", "BLE Sensor", this);
|
||||||
|
@@ -14,7 +14,11 @@ static const char *const TAG = "ble_text_sensor";
|
|||||||
|
|
||||||
static const std::string EMPTY = "";
|
static const std::string EMPTY = "";
|
||||||
|
|
||||||
void BLETextSensor::loop() {}
|
void BLETextSensor::loop() {
|
||||||
|
// Parent BLEClientNode has a loop() method, but this component uses
|
||||||
|
// polling via update() and BLE callbacks so loop isn't needed
|
||||||
|
this->disable_loop();
|
||||||
|
}
|
||||||
|
|
||||||
void BLETextSensor::dump_config() {
|
void BLETextSensor::dump_config() {
|
||||||
LOG_TEXT_SENSOR("", "BLE Text Sensor", this);
|
LOG_TEXT_SENSOR("", "BLE Text Sensor", this);
|
||||||
|
@@ -26,10 +26,17 @@ class BluetoothConnection : public esp32_ble_client::BLEClientBase {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class BluetoothProxy;
|
friend class BluetoothProxy;
|
||||||
bool seen_mtu_or_services_{false};
|
|
||||||
|
|
||||||
int16_t send_service_{-2};
|
// Memory optimized layout for 32-bit systems
|
||||||
|
// Group 1: Pointers (4 bytes each, naturally aligned)
|
||||||
BluetoothProxy *proxy_;
|
BluetoothProxy *proxy_;
|
||||||
|
|
||||||
|
// Group 2: 2-byte types
|
||||||
|
int16_t send_service_{-2}; // Needs to handle negative values and service count
|
||||||
|
|
||||||
|
// Group 3: 1-byte types
|
||||||
|
bool seen_mtu_or_services_{false};
|
||||||
|
// 1 byte used, 1 byte padding
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace bluetooth_proxy
|
} // namespace bluetooth_proxy
|
||||||
|
@@ -58,7 +58,7 @@ static std::vector<api::BluetoothLERawAdvertisement> &get_batch_buffer() {
|
|||||||
return batch_buffer;
|
return batch_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) {
|
bool BluetoothProxy::parse_devices(const esp32_ble::BLEScanResult *scan_results, size_t count) {
|
||||||
if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || !this->raw_advertisements_)
|
if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || !this->raw_advertisements_)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ bool BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_p
|
|||||||
|
|
||||||
// Add new advertisements to the batch buffer
|
// Add new advertisements to the batch buffer
|
||||||
for (size_t i = 0; i < count; i++) {
|
for (size_t i = 0; i < count; i++) {
|
||||||
auto &result = advertisements[i];
|
auto &result = scan_results[i];
|
||||||
uint8_t length = result.adv_data_len + result.scan_rsp_len;
|
uint8_t length = result.adv_data_len + result.scan_rsp_len;
|
||||||
|
|
||||||
batch_buffer.emplace_back();
|
batch_buffer.emplace_back();
|
||||||
|
@@ -52,7 +52,7 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
|
|||||||
public:
|
public:
|
||||||
BluetoothProxy();
|
BluetoothProxy();
|
||||||
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
|
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
|
||||||
bool parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) override;
|
bool parse_devices(const esp32_ble::BLEScanResult *scan_results, size_t count) override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
@@ -134,11 +134,17 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
|
|||||||
|
|
||||||
BluetoothConnection *get_connection_(uint64_t address, bool reserve);
|
BluetoothConnection *get_connection_(uint64_t address, bool reserve);
|
||||||
|
|
||||||
bool active_;
|
// Memory optimized layout for 32-bit systems
|
||||||
|
// Group 1: Pointers (4 bytes each, naturally aligned)
|
||||||
std::vector<BluetoothConnection *> connections_{};
|
|
||||||
api::APIConnection *api_connection_{nullptr};
|
api::APIConnection *api_connection_{nullptr};
|
||||||
|
|
||||||
|
// Group 2: Container types (typically 12 bytes on 32-bit)
|
||||||
|
std::vector<BluetoothConnection *> connections_{};
|
||||||
|
|
||||||
|
// Group 3: 1-byte types grouped together
|
||||||
|
bool active_;
|
||||||
bool raw_advertisements_{false};
|
bool raw_advertisements_{false};
|
||||||
|
// 2 bytes used, 2 bytes padding
|
||||||
};
|
};
|
||||||
|
|
||||||
extern BluetoothProxy *global_bluetooth_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
extern BluetoothProxy *global_bluetooth_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
@@ -93,9 +93,8 @@ void BME280Component::setup() {
|
|||||||
|
|
||||||
// Mark as not failed before initializing. Some devices will turn off sensors to save on batteries
|
// Mark as not failed before initializing. Some devices will turn off sensors to save on batteries
|
||||||
// and when they come back on, the COMPONENT_STATE_FAILED bit must be unset on the component.
|
// and when they come back on, the COMPONENT_STATE_FAILED bit must be unset on the component.
|
||||||
if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) {
|
if (this->is_failed()) {
|
||||||
this->component_state_ &= ~COMPONENT_STATE_MASK;
|
this->reset_to_construction_state();
|
||||||
this->component_state_ |= COMPONENT_STATE_CONSTRUCTION;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this->read_byte(BME280_REGISTER_CHIPID, &chip_id)) {
|
if (!this->read_byte(BME280_REGISTER_CHIPID, &chip_id)) {
|
||||||
|
@@ -12,8 +12,8 @@ from esphome.const import (
|
|||||||
CONF_OVERSAMPLING,
|
CONF_OVERSAMPLING,
|
||||||
CONF_PRESSURE,
|
CONF_PRESSURE,
|
||||||
CONF_TEMPERATURE,
|
CONF_TEMPERATURE,
|
||||||
DEVICE_CLASS_HUMIDITY,
|
|
||||||
DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
|
DEVICE_CLASS_ATMOSPHERIC_PRESSURE,
|
||||||
|
DEVICE_CLASS_HUMIDITY,
|
||||||
DEVICE_CLASS_TEMPERATURE,
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
ICON_GAS_CYLINDER,
|
ICON_GAS_CYLINDER,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
|
@@ -41,6 +41,7 @@ async def to_code(config):
|
|||||||
|
|
||||||
if CORE.using_arduino:
|
if CORE.using_arduino:
|
||||||
if CORE.is_esp32:
|
if CORE.is_esp32:
|
||||||
|
cg.add_library("ESP32 Async UDP", None)
|
||||||
cg.add_library("DNSServer", None)
|
cg.add_library("DNSServer", None)
|
||||||
cg.add_library("WiFi", None)
|
cg.add_library("WiFi", None)
|
||||||
if CORE.is_esp8266:
|
if CORE.is_esp8266:
|
||||||
|
@@ -37,7 +37,12 @@ void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) {
|
|||||||
request->redirect("/?save");
|
request->redirect("/?save");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CaptivePortal::setup() {}
|
void CaptivePortal::setup() {
|
||||||
|
#ifndef USE_ARDUINO
|
||||||
|
// No DNS server needed for non-Arduino frameworks
|
||||||
|
this->disable_loop();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
void CaptivePortal::start() {
|
void CaptivePortal::start() {
|
||||||
this->base_->init();
|
this->base_->init();
|
||||||
if (!this->initialized_) {
|
if (!this->initialized_) {
|
||||||
@@ -50,6 +55,8 @@ void CaptivePortal::start() {
|
|||||||
this->dns_server_->setErrorReplyCode(DNSReplyCode::NoError);
|
this->dns_server_->setErrorReplyCode(DNSReplyCode::NoError);
|
||||||
network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip();
|
network::IPAddress ip = wifi::global_wifi_component->wifi_soft_ap_ip();
|
||||||
this->dns_server_->start(53, "*", ip);
|
this->dns_server_->start(53, "*", ip);
|
||||||
|
// Re-enable loop() when DNS server is started
|
||||||
|
this->enable_loop();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
this->base_->get_server()->onNotFound([this](AsyncWebServerRequest *req) {
|
this->base_->get_server()->onNotFound([this](AsyncWebServerRequest *req) {
|
||||||
@@ -68,7 +75,11 @@ void CaptivePortal::start() {
|
|||||||
|
|
||||||
void CaptivePortal::handleRequest(AsyncWebServerRequest *req) {
|
void CaptivePortal::handleRequest(AsyncWebServerRequest *req) {
|
||||||
if (req->url() == "/") {
|
if (req->url() == "/") {
|
||||||
|
#ifndef USE_ESP8266
|
||||||
|
auto *response = req->beginResponse(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ));
|
||||||
|
#else
|
||||||
auto *response = req->beginResponse_P(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ));
|
auto *response = req->beginResponse_P(200, "text/html", INDEX_GZ, sizeof(INDEX_GZ));
|
||||||
|
#endif
|
||||||
response->addHeader("Content-Encoding", "gzip");
|
response->addHeader("Content-Encoding", "gzip");
|
||||||
req->send(response);
|
req->send(response);
|
||||||
return;
|
return;
|
||||||
|
@@ -21,8 +21,11 @@ class CaptivePortal : public AsyncWebHandler, public Component {
|
|||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
#ifdef USE_ARDUINO
|
#ifdef USE_ARDUINO
|
||||||
void loop() override {
|
void loop() override {
|
||||||
if (this->dns_server_ != nullptr)
|
if (this->dns_server_ != nullptr) {
|
||||||
this->dns_server_->processNextRequest();
|
this->dns_server_->processNextRequest();
|
||||||
|
} else {
|
||||||
|
this->disable_loop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
float get_setup_priority() const override;
|
float get_setup_priority() const override;
|
||||||
@@ -37,7 +40,7 @@ class CaptivePortal : public AsyncWebHandler, public Component {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool canHandle(AsyncWebServerRequest *request) override {
|
bool canHandle(AsyncWebServerRequest *request) const override {
|
||||||
if (!this->active_)
|
if (!this->active_)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
"""CM1106 Sensor component for ESPHome."""
|
"""CM1106 Sensor component for ESPHome."""
|
||||||
|
|
||||||
import esphome.codegen as cg
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.automation import maybe_simple_id
|
from esphome.automation import maybe_simple_id
|
||||||
|
import esphome.codegen as cg
|
||||||
from esphome.components import sensor, uart
|
from esphome.components import sensor, uart
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_CO2,
|
CONF_CO2,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
|
@@ -11,25 +11,25 @@ static const char *const TAG = "datetime.date_entity";
|
|||||||
|
|
||||||
void DateEntity::publish_state() {
|
void DateEntity::publish_state() {
|
||||||
if (this->year_ == 0 || this->month_ == 0 || this->day_ == 0) {
|
if (this->year_ == 0 || this->month_ == 0 || this->day_ == 0) {
|
||||||
this->has_state_ = false;
|
this->set_has_state(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this->year_ < 1970 || this->year_ > 3000) {
|
if (this->year_ < 1970 || this->year_ > 3000) {
|
||||||
this->has_state_ = false;
|
this->set_has_state(false);
|
||||||
ESP_LOGE(TAG, "Year must be between 1970 and 3000");
|
ESP_LOGE(TAG, "Year must be between 1970 and 3000");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this->month_ < 1 || this->month_ > 12) {
|
if (this->month_ < 1 || this->month_ > 12) {
|
||||||
this->has_state_ = false;
|
this->set_has_state(false);
|
||||||
ESP_LOGE(TAG, "Month must be between 1 and 12");
|
ESP_LOGE(TAG, "Month must be between 1 and 12");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this->day_ > days_in_month(this->month_, this->year_)) {
|
if (this->day_ > days_in_month(this->month_, this->year_)) {
|
||||||
this->has_state_ = false;
|
this->set_has_state(false);
|
||||||
ESP_LOGE(TAG, "Day must be between 1 and %d for month %d", days_in_month(this->month_, this->year_), this->month_);
|
ESP_LOGE(TAG, "Day must be between 1 and %d for month %d", days_in_month(this->month_, this->year_), this->month_);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->has_state_ = true;
|
this->set_has_state(true);
|
||||||
ESP_LOGD(TAG, "'%s': Sending date %d-%d-%d", this->get_name().c_str(), this->year_, this->month_, this->day_);
|
ESP_LOGD(TAG, "'%s': Sending date %d-%d-%d", this->get_name().c_str(), this->year_, this->month_, this->day_);
|
||||||
this->state_callback_.call();
|
this->state_callback_.call();
|
||||||
}
|
}
|
||||||
|
@@ -13,9 +13,6 @@ namespace datetime {
|
|||||||
|
|
||||||
class DateTimeBase : public EntityBase {
|
class DateTimeBase : public EntityBase {
|
||||||
public:
|
public:
|
||||||
/// Return whether this Datetime has gotten a full state yet.
|
|
||||||
bool has_state() const { return this->has_state_; }
|
|
||||||
|
|
||||||
virtual ESPTime state_as_esptime() const = 0;
|
virtual ESPTime state_as_esptime() const = 0;
|
||||||
|
|
||||||
void add_on_state_callback(std::function<void()> &&callback) { this->state_callback_.add(std::move(callback)); }
|
void add_on_state_callback(std::function<void()> &&callback) { this->state_callback_.add(std::move(callback)); }
|
||||||
@@ -31,8 +28,6 @@ class DateTimeBase : public EntityBase {
|
|||||||
#ifdef USE_TIME
|
#ifdef USE_TIME
|
||||||
time::RealTimeClock *rtc_;
|
time::RealTimeClock *rtc_;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool has_state_{false};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef USE_TIME
|
#ifdef USE_TIME
|
||||||
|
@@ -11,40 +11,40 @@ static const char *const TAG = "datetime.datetime_entity";
|
|||||||
|
|
||||||
void DateTimeEntity::publish_state() {
|
void DateTimeEntity::publish_state() {
|
||||||
if (this->year_ == 0 || this->month_ == 0 || this->day_ == 0) {
|
if (this->year_ == 0 || this->month_ == 0 || this->day_ == 0) {
|
||||||
this->has_state_ = false;
|
this->set_has_state(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this->year_ < 1970 || this->year_ > 3000) {
|
if (this->year_ < 1970 || this->year_ > 3000) {
|
||||||
this->has_state_ = false;
|
this->set_has_state(false);
|
||||||
ESP_LOGE(TAG, "Year must be between 1970 and 3000");
|
ESP_LOGE(TAG, "Year must be between 1970 and 3000");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this->month_ < 1 || this->month_ > 12) {
|
if (this->month_ < 1 || this->month_ > 12) {
|
||||||
this->has_state_ = false;
|
this->set_has_state(false);
|
||||||
ESP_LOGE(TAG, "Month must be between 1 and 12");
|
ESP_LOGE(TAG, "Month must be between 1 and 12");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this->day_ > days_in_month(this->month_, this->year_)) {
|
if (this->day_ > days_in_month(this->month_, this->year_)) {
|
||||||
this->has_state_ = false;
|
this->set_has_state(false);
|
||||||
ESP_LOGE(TAG, "Day must be between 1 and %d for month %d", days_in_month(this->month_, this->year_), this->month_);
|
ESP_LOGE(TAG, "Day must be between 1 and %d for month %d", days_in_month(this->month_, this->year_), this->month_);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this->hour_ > 23) {
|
if (this->hour_ > 23) {
|
||||||
this->has_state_ = false;
|
this->set_has_state(false);
|
||||||
ESP_LOGE(TAG, "Hour must be between 0 and 23");
|
ESP_LOGE(TAG, "Hour must be between 0 and 23");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this->minute_ > 59) {
|
if (this->minute_ > 59) {
|
||||||
this->has_state_ = false;
|
this->set_has_state(false);
|
||||||
ESP_LOGE(TAG, "Minute must be between 0 and 59");
|
ESP_LOGE(TAG, "Minute must be between 0 and 59");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this->second_ > 59) {
|
if (this->second_ > 59) {
|
||||||
this->has_state_ = false;
|
this->set_has_state(false);
|
||||||
ESP_LOGE(TAG, "Second must be between 0 and 59");
|
ESP_LOGE(TAG, "Second must be between 0 and 59");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->has_state_ = true;
|
this->set_has_state(true);
|
||||||
ESP_LOGD(TAG, "'%s': Sending datetime %04u-%02u-%02u %02d:%02d:%02d", this->get_name().c_str(), this->year_,
|
ESP_LOGD(TAG, "'%s': Sending datetime %04u-%02u-%02u %02d:%02d:%02d", this->get_name().c_str(), this->year_,
|
||||||
this->month_, this->day_, this->hour_, this->minute_, this->second_);
|
this->month_, this->day_, this->hour_, this->minute_, this->second_);
|
||||||
this->state_callback_.call();
|
this->state_callback_.call();
|
||||||
|
@@ -11,21 +11,21 @@ static const char *const TAG = "datetime.time_entity";
|
|||||||
|
|
||||||
void TimeEntity::publish_state() {
|
void TimeEntity::publish_state() {
|
||||||
if (this->hour_ > 23) {
|
if (this->hour_ > 23) {
|
||||||
this->has_state_ = false;
|
this->set_has_state(false);
|
||||||
ESP_LOGE(TAG, "Hour must be between 0 and 23");
|
ESP_LOGE(TAG, "Hour must be between 0 and 23");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this->minute_ > 59) {
|
if (this->minute_ > 59) {
|
||||||
this->has_state_ = false;
|
this->set_has_state(false);
|
||||||
ESP_LOGE(TAG, "Minute must be between 0 and 59");
|
ESP_LOGE(TAG, "Minute must be between 0 and 59");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this->second_ > 59) {
|
if (this->second_ > 59) {
|
||||||
this->has_state_ = false;
|
this->set_has_state(false);
|
||||||
ESP_LOGE(TAG, "Second must be between 0 and 59");
|
ESP_LOGE(TAG, "Second must be between 0 and 59");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->has_state_ = true;
|
this->set_has_state(true);
|
||||||
ESP_LOGD(TAG, "'%s': Sending time %02d:%02d:%02d", this->get_name().c_str(), this->hour_, this->minute_,
|
ESP_LOGD(TAG, "'%s': Sending time %02d:%02d:%02d", this->get_name().c_str(), this->hour_, this->minute_,
|
||||||
this->second_);
|
this->second_);
|
||||||
this->state_callback_.call();
|
this->state_callback_.call();
|
||||||
|
@@ -11,7 +11,7 @@ namespace display {
|
|||||||
static const char *const TAG = "display";
|
static const char *const TAG = "display";
|
||||||
|
|
||||||
void DisplayBuffer::init_internal_(uint32_t buffer_length) {
|
void DisplayBuffer::init_internal_(uint32_t buffer_length) {
|
||||||
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
RAMAllocator<uint8_t> allocator;
|
||||||
this->buffer_ = allocator.allocate(buffer_length);
|
this->buffer_ = allocator.allocate(buffer_length);
|
||||||
if (this->buffer_ == nullptr) {
|
if (this->buffer_ == nullptr) {
|
||||||
ESP_LOGE(TAG, "Could not allocate buffer for display!");
|
ESP_LOGE(TAG, "Could not allocate buffer for display!");
|
||||||
|
@@ -94,6 +94,13 @@ COMPILER_OPTIMIZATIONS = {
|
|||||||
"SIZE": "CONFIG_COMPILER_OPTIMIZATION_SIZE",
|
"SIZE": "CONFIG_COMPILER_OPTIMIZATION_SIZE",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ARDUINO_ALLOWED_VARIANTS = [
|
||||||
|
VARIANT_ESP32,
|
||||||
|
VARIANT_ESP32C3,
|
||||||
|
VARIANT_ESP32S2,
|
||||||
|
VARIANT_ESP32S3,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def get_cpu_frequencies(*frequencies):
|
def get_cpu_frequencies(*frequencies):
|
||||||
return [str(x) + "MHZ" for x in frequencies]
|
return [str(x) + "MHZ" for x in frequencies]
|
||||||
@@ -125,6 +132,8 @@ def set_core_data(config):
|
|||||||
choices = CPU_FREQUENCIES[variant]
|
choices = CPU_FREQUENCIES[variant]
|
||||||
if "160MHZ" in choices:
|
if "160MHZ" in choices:
|
||||||
cpu_frequency = "160MHZ"
|
cpu_frequency = "160MHZ"
|
||||||
|
elif "360MHZ" in choices:
|
||||||
|
cpu_frequency = "360MHZ"
|
||||||
else:
|
else:
|
||||||
cpu_frequency = choices[-1]
|
cpu_frequency = choices[-1]
|
||||||
config[CONF_CPU_FREQUENCY] = cpu_frequency
|
config[CONF_CPU_FREQUENCY] = cpu_frequency
|
||||||
@@ -143,12 +152,17 @@ def set_core_data(config):
|
|||||||
CORE.data[KEY_ESP32][KEY_COMPONENTS] = {}
|
CORE.data[KEY_ESP32][KEY_COMPONENTS] = {}
|
||||||
elif conf[CONF_TYPE] == FRAMEWORK_ARDUINO:
|
elif conf[CONF_TYPE] == FRAMEWORK_ARDUINO:
|
||||||
CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino"
|
CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino"
|
||||||
|
if variant not in ARDUINO_ALLOWED_VARIANTS:
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"ESPHome does not support using the Arduino framework for the {variant}. Please use the ESP-IDF framework instead.",
|
||||||
|
path=[CONF_FRAMEWORK, CONF_TYPE],
|
||||||
|
)
|
||||||
CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse(
|
CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION] = cv.Version.parse(
|
||||||
config[CONF_FRAMEWORK][CONF_VERSION]
|
config[CONF_FRAMEWORK][CONF_VERSION]
|
||||||
)
|
)
|
||||||
|
|
||||||
CORE.data[KEY_ESP32][KEY_BOARD] = config[CONF_BOARD]
|
CORE.data[KEY_ESP32][KEY_BOARD] = config[CONF_BOARD]
|
||||||
CORE.data[KEY_ESP32][KEY_VARIANT] = config[CONF_VARIANT]
|
CORE.data[KEY_ESP32][KEY_VARIANT] = variant
|
||||||
CORE.data[KEY_ESP32][KEY_EXTRA_BUILD_FILES] = {}
|
CORE.data[KEY_ESP32][KEY_EXTRA_BUILD_FILES] = {}
|
||||||
|
|
||||||
return config
|
return config
|
||||||
@@ -277,11 +291,8 @@ def add_extra_build_file(filename: str, path: str) -> bool:
|
|||||||
|
|
||||||
def _format_framework_arduino_version(ver: cv.Version) -> str:
|
def _format_framework_arduino_version(ver: cv.Version) -> str:
|
||||||
# format the given arduino (https://github.com/espressif/arduino-esp32/releases) version to
|
# format the given arduino (https://github.com/espressif/arduino-esp32/releases) version to
|
||||||
# a PIO platformio/framework-arduinoespressif32 value
|
# a PIO pioarduino/framework-arduinoespressif32 value
|
||||||
# List of package versions: https://api.registry.platformio.org/v3/packages/platformio/tool/framework-arduinoespressif32
|
return f"pioarduino/framework-arduinoespressif32@https://github.com/espressif/arduino-esp32/releases/download/{str(ver)}/esp32-{str(ver)}.zip"
|
||||||
if ver <= cv.Version(1, 0, 3):
|
|
||||||
return f"~2.{ver.major}{ver.minor:02d}{ver.patch:02d}.0"
|
|
||||||
return f"~3.{ver.major}{ver.minor:02d}{ver.patch:02d}.0"
|
|
||||||
|
|
||||||
|
|
||||||
def _format_framework_espidf_version(
|
def _format_framework_espidf_version(
|
||||||
@@ -305,12 +316,10 @@ def _format_framework_espidf_version(
|
|||||||
|
|
||||||
# The default/recommended arduino framework version
|
# The default/recommended arduino framework version
|
||||||
# - https://github.com/espressif/arduino-esp32/releases
|
# - https://github.com/espressif/arduino-esp32/releases
|
||||||
# - https://api.registry.platformio.org/v3/packages/platformio/tool/framework-arduinoespressif32
|
RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(3, 1, 3)
|
||||||
RECOMMENDED_ARDUINO_FRAMEWORK_VERSION = cv.Version(2, 0, 5)
|
# The platform-espressif32 version to use for arduino frameworks
|
||||||
# The platformio/espressif32 version to use for arduino frameworks
|
# - https://github.com/pioarduino/platform-espressif32/releases
|
||||||
# - https://github.com/platformio/platform-espressif32/releases
|
ARDUINO_PLATFORM_VERSION = cv.Version(53, 3, 13)
|
||||||
# - https://api.registry.platformio.org/v3/packages/platformio/platform/espressif32
|
|
||||||
ARDUINO_PLATFORM_VERSION = cv.Version(5, 4, 0)
|
|
||||||
|
|
||||||
# The default/recommended esp-idf framework version
|
# The default/recommended esp-idf framework version
|
||||||
# - https://github.com/espressif/esp-idf/releases
|
# - https://github.com/espressif/esp-idf/releases
|
||||||
@@ -353,8 +362,8 @@ SUPPORTED_PIOARDUINO_ESP_IDF_5X = [
|
|||||||
def _arduino_check_versions(value):
|
def _arduino_check_versions(value):
|
||||||
value = value.copy()
|
value = value.copy()
|
||||||
lookups = {
|
lookups = {
|
||||||
"dev": (cv.Version(2, 1, 0), "https://github.com/espressif/arduino-esp32.git"),
|
"dev": (cv.Version(3, 1, 3), "https://github.com/espressif/arduino-esp32.git"),
|
||||||
"latest": (cv.Version(2, 0, 9), None),
|
"latest": (cv.Version(3, 1, 3), None),
|
||||||
"recommended": (RECOMMENDED_ARDUINO_FRAMEWORK_VERSION, None),
|
"recommended": (RECOMMENDED_ARDUINO_FRAMEWORK_VERSION, None),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,6 +385,10 @@ def _arduino_check_versions(value):
|
|||||||
CONF_PLATFORM_VERSION, _parse_platform_version(str(ARDUINO_PLATFORM_VERSION))
|
CONF_PLATFORM_VERSION, _parse_platform_version(str(ARDUINO_PLATFORM_VERSION))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if value[CONF_SOURCE].startswith("http"):
|
||||||
|
# prefix is necessary or platformio will complain with a cryptic error
|
||||||
|
value[CONF_SOURCE] = f"framework-arduinoespressif32@{value[CONF_SOURCE]}"
|
||||||
|
|
||||||
if version != RECOMMENDED_ARDUINO_FRAMEWORK_VERSION:
|
if version != RECOMMENDED_ARDUINO_FRAMEWORK_VERSION:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"The selected Arduino framework version is not the recommended one. "
|
"The selected Arduino framework version is not the recommended one. "
|
||||||
@@ -618,6 +631,21 @@ ESP_IDF_FRAMEWORK_SCHEMA = cv.All(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _set_default_framework(config):
|
||||||
|
if CONF_FRAMEWORK not in config:
|
||||||
|
config = config.copy()
|
||||||
|
|
||||||
|
variant = config[CONF_VARIANT]
|
||||||
|
if variant in ARDUINO_ALLOWED_VARIANTS:
|
||||||
|
config[CONF_FRAMEWORK] = ARDUINO_FRAMEWORK_SCHEMA({})
|
||||||
|
config[CONF_FRAMEWORK][CONF_TYPE] = FRAMEWORK_ARDUINO
|
||||||
|
else:
|
||||||
|
config[CONF_FRAMEWORK] = ESP_IDF_FRAMEWORK_SCHEMA({})
|
||||||
|
config[CONF_FRAMEWORK][CONF_TYPE] = FRAMEWORK_ESP_IDF
|
||||||
|
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
FRAMEWORK_ESP_IDF = "esp-idf"
|
FRAMEWORK_ESP_IDF = "esp-idf"
|
||||||
FRAMEWORK_ARDUINO = "arduino"
|
FRAMEWORK_ARDUINO = "arduino"
|
||||||
FRAMEWORK_SCHEMA = cv.typed_schema(
|
FRAMEWORK_SCHEMA = cv.typed_schema(
|
||||||
@@ -627,7 +655,6 @@ FRAMEWORK_SCHEMA = cv.typed_schema(
|
|||||||
},
|
},
|
||||||
lower=True,
|
lower=True,
|
||||||
space="-",
|
space="-",
|
||||||
default_type=FRAMEWORK_ARDUINO,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -654,10 +681,11 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
),
|
),
|
||||||
cv.Optional(CONF_PARTITIONS): cv.file_,
|
cv.Optional(CONF_PARTITIONS): cv.file_,
|
||||||
cv.Optional(CONF_VARIANT): cv.one_of(*VARIANTS, upper=True),
|
cv.Optional(CONF_VARIANT): cv.one_of(*VARIANTS, upper=True),
|
||||||
cv.Optional(CONF_FRAMEWORK, default={}): FRAMEWORK_SCHEMA,
|
cv.Optional(CONF_FRAMEWORK): FRAMEWORK_SCHEMA,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
_detect_variant,
|
_detect_variant,
|
||||||
|
_set_default_framework,
|
||||||
set_core_data,
|
set_core_data,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -668,6 +696,7 @@ FINAL_VALIDATE_SCHEMA = cv.Schema(final_validate)
|
|||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_platformio_option("board", config[CONF_BOARD])
|
cg.add_platformio_option("board", config[CONF_BOARD])
|
||||||
cg.add_platformio_option("board_upload.flash_size", config[CONF_FLASH_SIZE])
|
cg.add_platformio_option("board_upload.flash_size", config[CONF_FLASH_SIZE])
|
||||||
|
cg.set_cpp_standard("gnu++17")
|
||||||
cg.add_build_flag("-DUSE_ESP32")
|
cg.add_build_flag("-DUSE_ESP32")
|
||||||
cg.add_define("ESPHOME_BOARD", config[CONF_BOARD])
|
cg.add_define("ESPHOME_BOARD", config[CONF_BOARD])
|
||||||
cg.add_build_flag(f"-DUSE_ESP32_VARIANT_{config[CONF_VARIANT]}")
|
cg.add_build_flag(f"-DUSE_ESP32_VARIANT_{config[CONF_VARIANT]}")
|
||||||
@@ -801,10 +830,7 @@ async def to_code(config):
|
|||||||
cg.add_platformio_option("framework", "arduino")
|
cg.add_platformio_option("framework", "arduino")
|
||||||
cg.add_build_flag("-DUSE_ARDUINO")
|
cg.add_build_flag("-DUSE_ARDUINO")
|
||||||
cg.add_build_flag("-DUSE_ESP32_FRAMEWORK_ARDUINO")
|
cg.add_build_flag("-DUSE_ESP32_FRAMEWORK_ARDUINO")
|
||||||
cg.add_platformio_option(
|
cg.add_platformio_option("platform_packages", [conf[CONF_SOURCE]])
|
||||||
"platform_packages",
|
|
||||||
[f"platformio/framework-arduinoespressif32@{conf[CONF_SOURCE]}"],
|
|
||||||
)
|
|
||||||
|
|
||||||
if CONF_PARTITIONS in config:
|
if CONF_PARTITIONS in config:
|
||||||
cg.add_platformio_option("board_build.partitions", config[CONF_PARTITIONS])
|
cg.add_platformio_option("board_build.partitions", config[CONF_PARTITIONS])
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
#include "ble.h"
|
#include "ble.h"
|
||||||
|
#include "ble_event_pool.h"
|
||||||
|
|
||||||
#include "esphome/core/application.h"
|
#include "esphome/core/application.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
@@ -23,9 +24,6 @@ namespace esp32_ble {
|
|||||||
|
|
||||||
static const char *const TAG = "esp32_ble";
|
static const char *const TAG = "esp32_ble";
|
||||||
|
|
||||||
static RAMAllocator<BLEEvent> EVENT_ALLOCATOR( // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
|
||||||
RAMAllocator<BLEEvent>::ALLOW_FAILURE | RAMAllocator<BLEEvent>::ALLOC_INTERNAL);
|
|
||||||
|
|
||||||
void ESP32BLE::setup() {
|
void ESP32BLE::setup() {
|
||||||
global_ble = this;
|
global_ble = this;
|
||||||
ESP_LOGCONFIG(TAG, "Running setup");
|
ESP_LOGCONFIG(TAG, "Running setup");
|
||||||
@@ -304,82 +302,191 @@ void ESP32BLE::loop() {
|
|||||||
BLEEvent *ble_event = this->ble_events_.pop();
|
BLEEvent *ble_event = this->ble_events_.pop();
|
||||||
while (ble_event != nullptr) {
|
while (ble_event != nullptr) {
|
||||||
switch (ble_event->type_) {
|
switch (ble_event->type_) {
|
||||||
case BLEEvent::GATTS:
|
case BLEEvent::GATTS: {
|
||||||
this->real_gatts_event_handler_(ble_event->event_.gatts.gatts_event, ble_event->event_.gatts.gatts_if,
|
esp_gatts_cb_event_t event = ble_event->event_.gatts.gatts_event;
|
||||||
&ble_event->event_.gatts.gatts_param);
|
esp_gatt_if_t gatts_if = ble_event->event_.gatts.gatts_if;
|
||||||
|
esp_ble_gatts_cb_param_t *param = ble_event->event_.gatts.gatts_param;
|
||||||
|
ESP_LOGV(TAG, "gatts_event [esp_gatt_if: %d] - %d", gatts_if, event);
|
||||||
|
for (auto *gatts_handler : this->gatts_event_handlers_) {
|
||||||
|
gatts_handler->gatts_event_handler(event, gatts_if, param);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case BLEEvent::GATTC:
|
}
|
||||||
this->real_gattc_event_handler_(ble_event->event_.gattc.gattc_event, ble_event->event_.gattc.gattc_if,
|
case BLEEvent::GATTC: {
|
||||||
&ble_event->event_.gattc.gattc_param);
|
esp_gattc_cb_event_t event = ble_event->event_.gattc.gattc_event;
|
||||||
|
esp_gatt_if_t gattc_if = ble_event->event_.gattc.gattc_if;
|
||||||
|
esp_ble_gattc_cb_param_t *param = ble_event->event_.gattc.gattc_param;
|
||||||
|
ESP_LOGV(TAG, "gattc_event [esp_gatt_if: %d] - %d", gattc_if, event);
|
||||||
|
for (auto *gattc_handler : this->gattc_event_handlers_) {
|
||||||
|
gattc_handler->gattc_event_handler(event, gattc_if, param);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case BLEEvent::GAP:
|
}
|
||||||
this->real_gap_event_handler_(ble_event->event_.gap.gap_event, &ble_event->event_.gap.gap_param);
|
case BLEEvent::GAP: {
|
||||||
|
esp_gap_ble_cb_event_t gap_event = ble_event->event_.gap.gap_event;
|
||||||
|
switch (gap_event) {
|
||||||
|
case ESP_GAP_BLE_SCAN_RESULT_EVT:
|
||||||
|
// Use the new scan event handler - no memcpy!
|
||||||
|
for (auto *scan_handler : this->gap_scan_event_handlers_) {
|
||||||
|
scan_handler->gap_scan_event_handler(ble_event->scan_result());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Scan complete events
|
||||||
|
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
|
||||||
|
case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
|
||||||
|
case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
|
||||||
|
// All three scan complete events have the same structure with just status
|
||||||
|
// The scan_complete struct matches ESP-IDF's layout exactly, so this reinterpret_cast is safe
|
||||||
|
// This is verified at compile-time by static_assert checks in ble_event.h
|
||||||
|
// The struct already contains our copy of the status (copied in BLEEvent constructor)
|
||||||
|
ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
|
||||||
|
for (auto *gap_handler : this->gap_event_handlers_) {
|
||||||
|
gap_handler->gap_event_handler(
|
||||||
|
gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.scan_complete));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Advertising complete events
|
||||||
|
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
|
||||||
|
case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
|
||||||
|
case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
|
||||||
|
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
|
||||||
|
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
|
||||||
|
// All advertising complete events have the same structure with just status
|
||||||
|
ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
|
||||||
|
for (auto *gap_handler : this->gap_event_handlers_) {
|
||||||
|
gap_handler->gap_event_handler(
|
||||||
|
gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.adv_complete));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// RSSI complete event
|
||||||
|
case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
|
||||||
|
ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
|
||||||
|
for (auto *gap_handler : this->gap_event_handlers_) {
|
||||||
|
gap_handler->gap_event_handler(
|
||||||
|
gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.read_rssi_complete));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Security events
|
||||||
|
case ESP_GAP_BLE_AUTH_CMPL_EVT:
|
||||||
|
case ESP_GAP_BLE_SEC_REQ_EVT:
|
||||||
|
case ESP_GAP_BLE_PASSKEY_NOTIF_EVT:
|
||||||
|
case ESP_GAP_BLE_PASSKEY_REQ_EVT:
|
||||||
|
case ESP_GAP_BLE_NC_REQ_EVT:
|
||||||
|
ESP_LOGV(TAG, "gap_event_handler - %d", gap_event);
|
||||||
|
for (auto *gap_handler : this->gap_event_handlers_) {
|
||||||
|
gap_handler->gap_event_handler(
|
||||||
|
gap_event, reinterpret_cast<esp_ble_gap_cb_param_t *>(&ble_event->event_.gap.security));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Unknown/unhandled event
|
||||||
|
ESP_LOGW(TAG, "Unhandled GAP event type in loop: %d", gap_event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ble_event->~BLEEvent();
|
// Return the event to the pool
|
||||||
EVENT_ALLOCATOR.deallocate(ble_event, 1);
|
this->ble_event_pool_.release(ble_event);
|
||||||
ble_event = this->ble_events_.pop();
|
ble_event = this->ble_events_.pop();
|
||||||
}
|
}
|
||||||
if (this->advertising_ != nullptr) {
|
if (this->advertising_ != nullptr) {
|
||||||
this->advertising_->loop();
|
this->advertising_->loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Log dropped events periodically
|
||||||
|
uint16_t dropped = this->ble_events_.get_and_reset_dropped_count();
|
||||||
|
if (dropped > 0) {
|
||||||
|
ESP_LOGW(TAG, "Dropped %u BLE events due to buffer overflow", dropped);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
|
// Helper function to load new event data based on type
|
||||||
BLEEvent *new_event = EVENT_ALLOCATOR.allocate(1);
|
void load_ble_event(BLEEvent *event, esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) {
|
||||||
if (new_event == nullptr) {
|
event->load_gap_event(e, p);
|
||||||
// Memory too fragmented to allocate new event. Can only drop it until memory comes back
|
}
|
||||||
|
|
||||||
|
void load_ble_event(BLEEvent *event, esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) {
|
||||||
|
event->load_gattc_event(e, i, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_ble_event(BLEEvent *event, esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) {
|
||||||
|
event->load_gatts_event(e, i, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args> void enqueue_ble_event(Args... args) {
|
||||||
|
// Allocate an event from the pool
|
||||||
|
BLEEvent *event = global_ble->ble_event_pool_.allocate();
|
||||||
|
if (event == nullptr) {
|
||||||
|
// No events available - queue is full or we're out of memory
|
||||||
|
global_ble->ble_events_.increment_dropped_count();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
new (new_event) BLEEvent(event, param);
|
|
||||||
global_ble->ble_events_.push(new_event);
|
|
||||||
} // NOLINT(clang-analyzer-unix.Malloc)
|
|
||||||
|
|
||||||
void ESP32BLE::real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
|
// Load new event data (replaces previous event)
|
||||||
ESP_LOGV(TAG, "(BLE) gap_event_handler - %d", event);
|
load_ble_event(event, args...);
|
||||||
for (auto *gap_handler : this->gap_event_handlers_) {
|
|
||||||
gap_handler->gap_event_handler(event, param);
|
// Push the event to the queue
|
||||||
|
global_ble->ble_events_.push(event);
|
||||||
|
// Push always succeeds because we're the only producer and the pool ensures we never exceed queue size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explicit template instantiations for the friend function
|
||||||
|
template void enqueue_ble_event(esp_gap_ble_cb_event_t, esp_ble_gap_cb_param_t *);
|
||||||
|
template void enqueue_ble_event(esp_gatts_cb_event_t, esp_gatt_if_t, esp_ble_gatts_cb_param_t *);
|
||||||
|
template void enqueue_ble_event(esp_gattc_cb_event_t, esp_gatt_if_t, esp_ble_gattc_cb_param_t *);
|
||||||
|
|
||||||
|
void ESP32BLE::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
|
||||||
|
switch (event) {
|
||||||
|
// Queue GAP events that components need to handle
|
||||||
|
// Scanning events - used by esp32_ble_tracker
|
||||||
|
case ESP_GAP_BLE_SCAN_RESULT_EVT:
|
||||||
|
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
|
||||||
|
case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
|
||||||
|
case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
|
||||||
|
// Advertising events - used by esp32_ble_beacon and esp32_ble server
|
||||||
|
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
|
||||||
|
case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
|
||||||
|
case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
|
||||||
|
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
|
||||||
|
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
|
||||||
|
// Connection events - used by ble_client
|
||||||
|
case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
|
||||||
|
// Security events - used by ble_client and bluetooth_proxy
|
||||||
|
case ESP_GAP_BLE_AUTH_CMPL_EVT:
|
||||||
|
case ESP_GAP_BLE_SEC_REQ_EVT:
|
||||||
|
case ESP_GAP_BLE_PASSKEY_NOTIF_EVT:
|
||||||
|
case ESP_GAP_BLE_PASSKEY_REQ_EVT:
|
||||||
|
case ESP_GAP_BLE_NC_REQ_EVT:
|
||||||
|
enqueue_ble_event(event, param);
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Ignore these GAP events as they are not relevant for our use case
|
||||||
|
case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
|
||||||
|
case ESP_GAP_BLE_SET_PKT_LENGTH_COMPLETE_EVT:
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
ESP_LOGW(TAG, "Ignoring unexpected GAP event type: %d", event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESP32BLE::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
|
void ESP32BLE::gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
|
||||||
esp_ble_gatts_cb_param_t *param) {
|
esp_ble_gatts_cb_param_t *param) {
|
||||||
BLEEvent *new_event = EVENT_ALLOCATOR.allocate(1);
|
enqueue_ble_event(event, gatts_if, param);
|
||||||
if (new_event == nullptr) {
|
|
||||||
// Memory too fragmented to allocate new event. Can only drop it until memory comes back
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
new (new_event) BLEEvent(event, gatts_if, param);
|
|
||||||
global_ble->ble_events_.push(new_event);
|
|
||||||
} // NOLINT(clang-analyzer-unix.Malloc)
|
|
||||||
|
|
||||||
void ESP32BLE::real_gatts_event_handler_(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if,
|
|
||||||
esp_ble_gatts_cb_param_t *param) {
|
|
||||||
ESP_LOGV(TAG, "(BLE) gatts_event [esp_gatt_if: %d] - %d", gatts_if, event);
|
|
||||||
for (auto *gatts_handler : this->gatts_event_handlers_) {
|
|
||||||
gatts_handler->gatts_event_handler(event, gatts_if, param);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESP32BLE::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
void ESP32BLE::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||||
esp_ble_gattc_cb_param_t *param) {
|
esp_ble_gattc_cb_param_t *param) {
|
||||||
BLEEvent *new_event = EVENT_ALLOCATOR.allocate(1);
|
enqueue_ble_event(event, gattc_if, param);
|
||||||
if (new_event == nullptr) {
|
|
||||||
// Memory too fragmented to allocate new event. Can only drop it until memory comes back
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
new (new_event) BLEEvent(event, gattc_if, param);
|
|
||||||
global_ble->ble_events_.push(new_event);
|
|
||||||
} // NOLINT(clang-analyzer-unix.Malloc)
|
|
||||||
|
|
||||||
void ESP32BLE::real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
|
||||||
esp_ble_gattc_cb_param_t *param) {
|
|
||||||
ESP_LOGV(TAG, "(BLE) gattc_event [esp_gatt_if: %d] - %d", gattc_if, event);
|
|
||||||
for (auto *gattc_handler : this->gattc_event_handlers_) {
|
|
||||||
gattc_handler->gattc_event_handler(event, gattc_if, param);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float ESP32BLE::get_setup_priority() const { return setup_priority::BLUETOOTH; }
|
float ESP32BLE::get_setup_priority() const { return setup_priority::BLUETOOTH; }
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "ble_advertising.h"
|
#include "ble_advertising.h"
|
||||||
#include "ble_uuid.h"
|
#include "ble_uuid.h"
|
||||||
|
#include "ble_scan_result.h"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
@@ -11,6 +12,7 @@
|
|||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
#include "ble_event.h"
|
#include "ble_event.h"
|
||||||
|
#include "ble_event_pool.h"
|
||||||
#include "queue.h"
|
#include "queue.h"
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
@@ -22,6 +24,16 @@
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace esp32_ble {
|
namespace esp32_ble {
|
||||||
|
|
||||||
|
// Maximum number of BLE scan results to buffer
|
||||||
|
#ifdef USE_PSRAM
|
||||||
|
static constexpr uint8_t SCAN_RESULT_BUFFER_SIZE = 32;
|
||||||
|
#else
|
||||||
|
static constexpr uint8_t SCAN_RESULT_BUFFER_SIZE = 20;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Maximum size of the BLE event queue - must be power of 2 for lock-free queue
|
||||||
|
static constexpr size_t MAX_BLE_QUEUE_SIZE = 64;
|
||||||
|
|
||||||
uint64_t ble_addr_to_uint64(const esp_bd_addr_t address);
|
uint64_t ble_addr_to_uint64(const esp_bd_addr_t address);
|
||||||
|
|
||||||
// NOLINTNEXTLINE(modernize-use-using)
|
// NOLINTNEXTLINE(modernize-use-using)
|
||||||
@@ -57,6 +69,11 @@ class GAPEventHandler {
|
|||||||
virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) = 0;
|
virtual void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class GAPScanEventHandler {
|
||||||
|
public:
|
||||||
|
virtual void gap_scan_event_handler(const BLEScanResult &scan_result) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class GATTcEventHandler {
|
class GATTcEventHandler {
|
||||||
public:
|
public:
|
||||||
virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||||
@@ -101,6 +118,9 @@ class ESP32BLE : public Component {
|
|||||||
void advertising_register_raw_advertisement_callback(std::function<void(bool)> &&callback);
|
void advertising_register_raw_advertisement_callback(std::function<void(bool)> &&callback);
|
||||||
|
|
||||||
void register_gap_event_handler(GAPEventHandler *handler) { this->gap_event_handlers_.push_back(handler); }
|
void register_gap_event_handler(GAPEventHandler *handler) { this->gap_event_handlers_.push_back(handler); }
|
||||||
|
void register_gap_scan_event_handler(GAPScanEventHandler *handler) {
|
||||||
|
this->gap_scan_event_handlers_.push_back(handler);
|
||||||
|
}
|
||||||
void register_gattc_event_handler(GATTcEventHandler *handler) { this->gattc_event_handlers_.push_back(handler); }
|
void register_gattc_event_handler(GATTcEventHandler *handler) { this->gattc_event_handlers_.push_back(handler); }
|
||||||
void register_gatts_event_handler(GATTsEventHandler *handler) { this->gatts_event_handlers_.push_back(handler); }
|
void register_gatts_event_handler(GATTsEventHandler *handler) { this->gatts_event_handlers_.push_back(handler); }
|
||||||
void register_ble_status_event_handler(BLEStatusEventHandler *handler) {
|
void register_ble_status_event_handler(BLEStatusEventHandler *handler) {
|
||||||
@@ -113,22 +133,23 @@ class ESP32BLE : public Component {
|
|||||||
static void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
|
static void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
|
||||||
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
|
static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
|
||||||
|
|
||||||
void real_gatts_event_handler_(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
|
|
||||||
void real_gattc_event_handler_(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
|
|
||||||
void real_gap_event_handler_(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
|
|
||||||
|
|
||||||
bool ble_setup_();
|
bool ble_setup_();
|
||||||
bool ble_dismantle_();
|
bool ble_dismantle_();
|
||||||
bool ble_pre_setup_();
|
bool ble_pre_setup_();
|
||||||
void advertising_init_();
|
void advertising_init_();
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<typename... Args> friend void enqueue_ble_event(Args... args);
|
||||||
|
|
||||||
std::vector<GAPEventHandler *> gap_event_handlers_;
|
std::vector<GAPEventHandler *> gap_event_handlers_;
|
||||||
|
std::vector<GAPScanEventHandler *> gap_scan_event_handlers_;
|
||||||
std::vector<GATTcEventHandler *> gattc_event_handlers_;
|
std::vector<GATTcEventHandler *> gattc_event_handlers_;
|
||||||
std::vector<GATTsEventHandler *> gatts_event_handlers_;
|
std::vector<GATTsEventHandler *> gatts_event_handlers_;
|
||||||
std::vector<BLEStatusEventHandler *> ble_status_event_handlers_;
|
std::vector<BLEStatusEventHandler *> ble_status_event_handlers_;
|
||||||
BLEComponentState state_{BLE_COMPONENT_STATE_OFF};
|
BLEComponentState state_{BLE_COMPONENT_STATE_OFF};
|
||||||
|
|
||||||
Queue<BLEEvent> ble_events_;
|
LockFreeQueue<BLEEvent, MAX_BLE_QUEUE_SIZE> ble_events_;
|
||||||
|
BLEEventPool<MAX_BLE_QUEUE_SIZE> ble_event_pool_;
|
||||||
BLEAdvertising *advertising_{};
|
BLEAdvertising *advertising_{};
|
||||||
esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE};
|
esp_ble_io_cap_t io_cap_{ESP_IO_CAP_NONE};
|
||||||
uint32_t advertising_cycle_time_{};
|
uint32_t advertising_cycle_time_{};
|
||||||
|
@@ -2,92 +2,399 @@
|
|||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
#include <cstddef> // for offsetof
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <esp_gap_ble_api.h>
|
#include <esp_gap_ble_api.h>
|
||||||
#include <esp_gattc_api.h>
|
#include <esp_gattc_api.h>
|
||||||
#include <esp_gatts_api.h>
|
#include <esp_gatts_api.h>
|
||||||
|
|
||||||
|
#include "ble_scan_result.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace esp32_ble {
|
namespace esp32_ble {
|
||||||
|
|
||||||
|
// Compile-time verification that ESP-IDF scan complete events only contain a status field
|
||||||
|
// This ensures our reinterpret_cast in ble.cpp is safe
|
||||||
|
static_assert(sizeof(esp_ble_gap_cb_param_t::ble_scan_param_cmpl_evt_param) == sizeof(esp_bt_status_t),
|
||||||
|
"ESP-IDF scan_param_cmpl structure has unexpected size");
|
||||||
|
static_assert(sizeof(esp_ble_gap_cb_param_t::ble_scan_start_cmpl_evt_param) == sizeof(esp_bt_status_t),
|
||||||
|
"ESP-IDF scan_start_cmpl structure has unexpected size");
|
||||||
|
static_assert(sizeof(esp_ble_gap_cb_param_t::ble_scan_stop_cmpl_evt_param) == sizeof(esp_bt_status_t),
|
||||||
|
"ESP-IDF scan_stop_cmpl structure has unexpected size");
|
||||||
|
|
||||||
|
// Verify the status field is at offset 0 (first member)
|
||||||
|
static_assert(offsetof(esp_ble_gap_cb_param_t, scan_param_cmpl.status) == 0,
|
||||||
|
"status must be first member of scan_param_cmpl");
|
||||||
|
static_assert(offsetof(esp_ble_gap_cb_param_t, scan_start_cmpl.status) == 0,
|
||||||
|
"status must be first member of scan_start_cmpl");
|
||||||
|
static_assert(offsetof(esp_ble_gap_cb_param_t, scan_stop_cmpl.status) == 0,
|
||||||
|
"status must be first member of scan_stop_cmpl");
|
||||||
|
|
||||||
|
// Compile-time verification for advertising complete events
|
||||||
|
static_assert(sizeof(esp_ble_gap_cb_param_t::ble_adv_data_cmpl_evt_param) == sizeof(esp_bt_status_t),
|
||||||
|
"ESP-IDF adv_data_cmpl structure has unexpected size");
|
||||||
|
static_assert(sizeof(esp_ble_gap_cb_param_t::ble_scan_rsp_data_cmpl_evt_param) == sizeof(esp_bt_status_t),
|
||||||
|
"ESP-IDF scan_rsp_data_cmpl structure has unexpected size");
|
||||||
|
static_assert(sizeof(esp_ble_gap_cb_param_t::ble_adv_data_raw_cmpl_evt_param) == sizeof(esp_bt_status_t),
|
||||||
|
"ESP-IDF adv_data_raw_cmpl structure has unexpected size");
|
||||||
|
static_assert(sizeof(esp_ble_gap_cb_param_t::ble_adv_start_cmpl_evt_param) == sizeof(esp_bt_status_t),
|
||||||
|
"ESP-IDF adv_start_cmpl structure has unexpected size");
|
||||||
|
static_assert(sizeof(esp_ble_gap_cb_param_t::ble_adv_stop_cmpl_evt_param) == sizeof(esp_bt_status_t),
|
||||||
|
"ESP-IDF adv_stop_cmpl structure has unexpected size");
|
||||||
|
|
||||||
|
// Verify the status field is at offset 0 for advertising events
|
||||||
|
static_assert(offsetof(esp_ble_gap_cb_param_t, adv_data_cmpl.status) == 0,
|
||||||
|
"status must be first member of adv_data_cmpl");
|
||||||
|
static_assert(offsetof(esp_ble_gap_cb_param_t, scan_rsp_data_cmpl.status) == 0,
|
||||||
|
"status must be first member of scan_rsp_data_cmpl");
|
||||||
|
static_assert(offsetof(esp_ble_gap_cb_param_t, adv_data_raw_cmpl.status) == 0,
|
||||||
|
"status must be first member of adv_data_raw_cmpl");
|
||||||
|
static_assert(offsetof(esp_ble_gap_cb_param_t, adv_start_cmpl.status) == 0,
|
||||||
|
"status must be first member of adv_start_cmpl");
|
||||||
|
static_assert(offsetof(esp_ble_gap_cb_param_t, adv_stop_cmpl.status) == 0,
|
||||||
|
"status must be first member of adv_stop_cmpl");
|
||||||
|
|
||||||
|
// Compile-time verification for RSSI complete event structure
|
||||||
|
static_assert(offsetof(esp_ble_gap_cb_param_t, read_rssi_cmpl.status) == 0,
|
||||||
|
"status must be first member of read_rssi_cmpl");
|
||||||
|
static_assert(offsetof(esp_ble_gap_cb_param_t, read_rssi_cmpl.rssi) == sizeof(esp_bt_status_t),
|
||||||
|
"rssi must immediately follow status in read_rssi_cmpl");
|
||||||
|
static_assert(offsetof(esp_ble_gap_cb_param_t, read_rssi_cmpl.remote_addr) == sizeof(esp_bt_status_t) + sizeof(int8_t),
|
||||||
|
"remote_addr must follow rssi in read_rssi_cmpl");
|
||||||
|
|
||||||
// Received GAP, GATTC and GATTS events are only queued, and get processed in the main loop().
|
// Received GAP, GATTC and GATTS events are only queued, and get processed in the main loop().
|
||||||
// This class stores each event in a single type.
|
// This class stores each event with minimal memory usage.
|
||||||
|
// GAP events (99% of traffic) don't have the vector overhead.
|
||||||
|
// GATTC/GATTS events use heap allocation for their param and data.
|
||||||
|
//
|
||||||
|
// Event flow:
|
||||||
|
// 1. ESP-IDF BLE stack calls our static handlers in the BLE task context
|
||||||
|
// 2. The handlers create a BLEEvent instance, copying only the data we need
|
||||||
|
// 3. The event is pushed to a thread-safe queue
|
||||||
|
// 4. In the main loop(), events are popped from the queue and processed
|
||||||
|
// 5. The event destructor cleans up any external allocations
|
||||||
|
//
|
||||||
|
// Thread safety:
|
||||||
|
// - GAP events: We copy only the fields we need directly into the union
|
||||||
|
// - GATTC/GATTS events: We heap-allocate and copy the entire param struct, ensuring
|
||||||
|
// the data remains valid even after the BLE callback returns. The original
|
||||||
|
// param pointer from ESP-IDF is only valid during the callback.
|
||||||
|
//
|
||||||
|
// CRITICAL DESIGN NOTE:
|
||||||
|
// The heap allocations for GATTC/GATTS events are REQUIRED for memory safety.
|
||||||
|
// DO NOT attempt to optimize by removing these allocations or storing pointers
|
||||||
|
// to the original ESP-IDF data. The ESP-IDF callback data has a different lifetime
|
||||||
|
// than our event processing, and accessing it after the callback returns would
|
||||||
|
// result in use-after-free bugs and crashes.
|
||||||
class BLEEvent {
|
class BLEEvent {
|
||||||
public:
|
public:
|
||||||
BLEEvent(esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) {
|
|
||||||
this->event_.gap.gap_event = e;
|
|
||||||
memcpy(&this->event_.gap.gap_param, p, sizeof(esp_ble_gap_cb_param_t));
|
|
||||||
this->type_ = GAP;
|
|
||||||
};
|
|
||||||
|
|
||||||
BLEEvent(esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) {
|
|
||||||
this->event_.gattc.gattc_event = e;
|
|
||||||
this->event_.gattc.gattc_if = i;
|
|
||||||
memcpy(&this->event_.gattc.gattc_param, p, sizeof(esp_ble_gattc_cb_param_t));
|
|
||||||
// Need to also make a copy of relevant event data.
|
|
||||||
switch (e) {
|
|
||||||
case ESP_GATTC_NOTIFY_EVT:
|
|
||||||
this->data.assign(p->notify.value, p->notify.value + p->notify.value_len);
|
|
||||||
this->event_.gattc.gattc_param.notify.value = this->data.data();
|
|
||||||
break;
|
|
||||||
case ESP_GATTC_READ_CHAR_EVT:
|
|
||||||
case ESP_GATTC_READ_DESCR_EVT:
|
|
||||||
this->data.assign(p->read.value, p->read.value + p->read.value_len);
|
|
||||||
this->event_.gattc.gattc_param.read.value = this->data.data();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this->type_ = GATTC;
|
|
||||||
};
|
|
||||||
|
|
||||||
BLEEvent(esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) {
|
|
||||||
this->event_.gatts.gatts_event = e;
|
|
||||||
this->event_.gatts.gatts_if = i;
|
|
||||||
memcpy(&this->event_.gatts.gatts_param, p, sizeof(esp_ble_gatts_cb_param_t));
|
|
||||||
// Need to also make a copy of relevant event data.
|
|
||||||
switch (e) {
|
|
||||||
case ESP_GATTS_WRITE_EVT:
|
|
||||||
this->data.assign(p->write.value, p->write.value + p->write.len);
|
|
||||||
this->event_.gatts.gatts_param.write.value = this->data.data();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this->type_ = GATTS;
|
|
||||||
};
|
|
||||||
|
|
||||||
union {
|
|
||||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
|
||||||
struct gap_event {
|
|
||||||
esp_gap_ble_cb_event_t gap_event;
|
|
||||||
esp_ble_gap_cb_param_t gap_param;
|
|
||||||
} gap;
|
|
||||||
|
|
||||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
|
||||||
struct gattc_event {
|
|
||||||
esp_gattc_cb_event_t gattc_event;
|
|
||||||
esp_gatt_if_t gattc_if;
|
|
||||||
esp_ble_gattc_cb_param_t gattc_param;
|
|
||||||
} gattc;
|
|
||||||
|
|
||||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
|
||||||
struct gatts_event {
|
|
||||||
esp_gatts_cb_event_t gatts_event;
|
|
||||||
esp_gatt_if_t gatts_if;
|
|
||||||
esp_ble_gatts_cb_param_t gatts_param;
|
|
||||||
} gatts;
|
|
||||||
} event_;
|
|
||||||
|
|
||||||
std::vector<uint8_t> data{};
|
|
||||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||||
enum ble_event_t : uint8_t {
|
enum ble_event_t : uint8_t {
|
||||||
GAP,
|
GAP,
|
||||||
GATTC,
|
GATTC,
|
||||||
GATTS,
|
GATTS,
|
||||||
} type_;
|
};
|
||||||
|
|
||||||
|
// Type definitions for cleaner method signatures
|
||||||
|
struct StatusOnlyData {
|
||||||
|
esp_bt_status_t status;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RSSICompleteData {
|
||||||
|
esp_bt_status_t status;
|
||||||
|
int8_t rssi;
|
||||||
|
esp_bd_addr_t remote_addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constructor for GAP events - no external allocations needed
|
||||||
|
BLEEvent(esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) {
|
||||||
|
this->type_ = GAP;
|
||||||
|
this->init_gap_data_(e, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructor for GATTC events - uses heap allocation
|
||||||
|
// IMPORTANT: The heap allocation is REQUIRED and must not be removed as an optimization.
|
||||||
|
// The param pointer from ESP-IDF is only valid during the callback execution.
|
||||||
|
// Since BLE events are processed asynchronously in the main loop, we must create
|
||||||
|
// our own copy to ensure the data remains valid until the event is processed.
|
||||||
|
BLEEvent(esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) {
|
||||||
|
this->type_ = GATTC;
|
||||||
|
this->init_gattc_data_(e, i, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructor for GATTS events - uses heap allocation
|
||||||
|
// IMPORTANT: The heap allocation is REQUIRED and must not be removed as an optimization.
|
||||||
|
// The param pointer from ESP-IDF is only valid during the callback execution.
|
||||||
|
// Since BLE events are processed asynchronously in the main loop, we must create
|
||||||
|
// our own copy to ensure the data remains valid until the event is processed.
|
||||||
|
BLEEvent(esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) {
|
||||||
|
this->type_ = GATTS;
|
||||||
|
this->init_gatts_data_(e, i, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destructor to clean up heap allocations
|
||||||
|
~BLEEvent() { this->cleanup_heap_data(); }
|
||||||
|
|
||||||
|
// Default constructor for pre-allocation in pool
|
||||||
|
BLEEvent() : type_(GAP) {}
|
||||||
|
|
||||||
|
// Clean up any heap-allocated data
|
||||||
|
void cleanup_heap_data() {
|
||||||
|
if (this->type_ == GAP) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this->type_ == GATTC) {
|
||||||
|
delete this->event_.gattc.gattc_param;
|
||||||
|
delete this->event_.gattc.data;
|
||||||
|
this->event_.gattc.gattc_param = nullptr;
|
||||||
|
this->event_.gattc.data = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this->type_ == GATTS) {
|
||||||
|
delete this->event_.gatts.gatts_param;
|
||||||
|
delete this->event_.gatts.data;
|
||||||
|
this->event_.gatts.gatts_param = nullptr;
|
||||||
|
this->event_.gatts.data = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load new event data for reuse (replaces previous event data)
|
||||||
|
void load_gap_event(esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) {
|
||||||
|
this->cleanup_heap_data();
|
||||||
|
this->type_ = GAP;
|
||||||
|
this->init_gap_data_(e, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_gattc_event(esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) {
|
||||||
|
this->cleanup_heap_data();
|
||||||
|
this->type_ = GATTC;
|
||||||
|
this->init_gattc_data_(e, i, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_gatts_event(esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) {
|
||||||
|
this->cleanup_heap_data();
|
||||||
|
this->type_ = GATTS;
|
||||||
|
this->init_gatts_data_(e, i, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable copy to prevent double-delete
|
||||||
|
BLEEvent(const BLEEvent &) = delete;
|
||||||
|
BLEEvent &operator=(const BLEEvent &) = delete;
|
||||||
|
|
||||||
|
union {
|
||||||
|
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||||
|
struct gap_event {
|
||||||
|
esp_gap_ble_cb_event_t gap_event;
|
||||||
|
union {
|
||||||
|
BLEScanResult scan_result; // 73 bytes - Used by: esp32_ble_tracker
|
||||||
|
// This matches ESP-IDF's scan complete event structures
|
||||||
|
// All three (scan_param_cmpl, scan_start_cmpl, scan_stop_cmpl) have identical layout
|
||||||
|
// Used by: esp32_ble_tracker
|
||||||
|
StatusOnlyData scan_complete; // 1 byte
|
||||||
|
// Advertising complete events all have same structure
|
||||||
|
// Used by: esp32_ble_beacon, esp32_ble server components
|
||||||
|
// ADV_DATA_SET, SCAN_RSP_DATA_SET, ADV_DATA_RAW_SET, ADV_START, ADV_STOP
|
||||||
|
StatusOnlyData adv_complete; // 1 byte
|
||||||
|
// RSSI complete event
|
||||||
|
// Used by: ble_client (ble_rssi_sensor component)
|
||||||
|
RSSICompleteData read_rssi_complete; // 8 bytes
|
||||||
|
// Security events - we store the full security union
|
||||||
|
// Used by: ble_client (automation), bluetooth_proxy, esp32_ble_client
|
||||||
|
esp_ble_sec_t security; // Variable size, but fits within scan_result size
|
||||||
|
};
|
||||||
|
} gap; // 80 bytes total
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||||
|
struct gattc_event {
|
||||||
|
esp_gattc_cb_event_t gattc_event;
|
||||||
|
esp_gatt_if_t gattc_if;
|
||||||
|
esp_ble_gattc_cb_param_t *gattc_param; // Heap-allocated
|
||||||
|
std::vector<uint8_t> *data; // Heap-allocated
|
||||||
|
} gattc; // 16 bytes (pointers only)
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||||
|
struct gatts_event {
|
||||||
|
esp_gatts_cb_event_t gatts_event;
|
||||||
|
esp_gatt_if_t gatts_if;
|
||||||
|
esp_ble_gatts_cb_param_t *gatts_param; // Heap-allocated
|
||||||
|
std::vector<uint8_t> *data; // Heap-allocated
|
||||||
|
} gatts; // 16 bytes (pointers only)
|
||||||
|
} event_; // 80 bytes
|
||||||
|
|
||||||
|
ble_event_t type_;
|
||||||
|
|
||||||
|
// Helper methods to access event data
|
||||||
|
ble_event_t type() const { return type_; }
|
||||||
|
esp_gap_ble_cb_event_t gap_event_type() const { return event_.gap.gap_event; }
|
||||||
|
const BLEScanResult &scan_result() const { return event_.gap.scan_result; }
|
||||||
|
esp_bt_status_t scan_complete_status() const { return event_.gap.scan_complete.status; }
|
||||||
|
esp_bt_status_t adv_complete_status() const { return event_.gap.adv_complete.status; }
|
||||||
|
const RSSICompleteData &read_rssi_complete() const { return event_.gap.read_rssi_complete; }
|
||||||
|
const esp_ble_sec_t &security() const { return event_.gap.security; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Initialize GAP event data
|
||||||
|
void init_gap_data_(esp_gap_ble_cb_event_t e, esp_ble_gap_cb_param_t *p) {
|
||||||
|
this->event_.gap.gap_event = e;
|
||||||
|
|
||||||
|
if (p == nullptr) {
|
||||||
|
return; // Invalid event, but we can't log in header file
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy data based on event type
|
||||||
|
switch (e) {
|
||||||
|
case ESP_GAP_BLE_SCAN_RESULT_EVT:
|
||||||
|
memcpy(this->event_.gap.scan_result.bda, p->scan_rst.bda, sizeof(esp_bd_addr_t));
|
||||||
|
this->event_.gap.scan_result.ble_addr_type = p->scan_rst.ble_addr_type;
|
||||||
|
this->event_.gap.scan_result.rssi = p->scan_rst.rssi;
|
||||||
|
this->event_.gap.scan_result.adv_data_len = p->scan_rst.adv_data_len;
|
||||||
|
this->event_.gap.scan_result.scan_rsp_len = p->scan_rst.scan_rsp_len;
|
||||||
|
this->event_.gap.scan_result.search_evt = p->scan_rst.search_evt;
|
||||||
|
memcpy(this->event_.gap.scan_result.ble_adv, p->scan_rst.ble_adv,
|
||||||
|
ESP_BLE_ADV_DATA_LEN_MAX + ESP_BLE_SCAN_RSP_DATA_LEN_MAX);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
|
||||||
|
this->event_.gap.scan_complete.status = p->scan_param_cmpl.status;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
|
||||||
|
this->event_.gap.scan_complete.status = p->scan_start_cmpl.status;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
|
||||||
|
this->event_.gap.scan_complete.status = p->scan_stop_cmpl.status;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Advertising complete events - all have same structure with just status
|
||||||
|
// Used by: esp32_ble_beacon, esp32_ble server components
|
||||||
|
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
|
||||||
|
this->event_.gap.adv_complete.status = p->adv_data_cmpl.status;
|
||||||
|
break;
|
||||||
|
case ESP_GAP_BLE_SCAN_RSP_DATA_SET_COMPLETE_EVT:
|
||||||
|
this->event_.gap.adv_complete.status = p->scan_rsp_data_cmpl.status;
|
||||||
|
break;
|
||||||
|
case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: // Used by: esp32_ble_beacon
|
||||||
|
this->event_.gap.adv_complete.status = p->adv_data_raw_cmpl.status;
|
||||||
|
break;
|
||||||
|
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: // Used by: esp32_ble_beacon
|
||||||
|
this->event_.gap.adv_complete.status = p->adv_start_cmpl.status;
|
||||||
|
break;
|
||||||
|
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: // Used by: esp32_ble_beacon
|
||||||
|
this->event_.gap.adv_complete.status = p->adv_stop_cmpl.status;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// RSSI complete event
|
||||||
|
// Used by: ble_client (ble_rssi_sensor)
|
||||||
|
case ESP_GAP_BLE_READ_RSSI_COMPLETE_EVT:
|
||||||
|
this->event_.gap.read_rssi_complete.status = p->read_rssi_cmpl.status;
|
||||||
|
this->event_.gap.read_rssi_complete.rssi = p->read_rssi_cmpl.rssi;
|
||||||
|
memcpy(this->event_.gap.read_rssi_complete.remote_addr, p->read_rssi_cmpl.remote_addr, sizeof(esp_bd_addr_t));
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Security events - copy the entire security union
|
||||||
|
// Used by: ble_client, bluetooth_proxy, esp32_ble_client
|
||||||
|
case ESP_GAP_BLE_AUTH_CMPL_EVT: // Used by: bluetooth_proxy, esp32_ble_client
|
||||||
|
case ESP_GAP_BLE_SEC_REQ_EVT: // Used by: esp32_ble_client
|
||||||
|
case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: // Used by: ble_client automation
|
||||||
|
case ESP_GAP_BLE_PASSKEY_REQ_EVT: // Used by: ble_client automation
|
||||||
|
case ESP_GAP_BLE_NC_REQ_EVT: // Used by: ble_client automation
|
||||||
|
memcpy(&this->event_.gap.security, &p->ble_security, sizeof(esp_ble_sec_t));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// We only store data for GAP events that components currently use
|
||||||
|
// Unknown events still get queued and logged in ble.cpp:375 as
|
||||||
|
// "Unhandled GAP event type in loop" - this helps identify new events
|
||||||
|
// that components might need in the future
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize GATTC event data
|
||||||
|
void init_gattc_data_(esp_gattc_cb_event_t e, esp_gatt_if_t i, esp_ble_gattc_cb_param_t *p) {
|
||||||
|
this->event_.gattc.gattc_event = e;
|
||||||
|
this->event_.gattc.gattc_if = i;
|
||||||
|
|
||||||
|
if (p == nullptr) {
|
||||||
|
this->event_.gattc.gattc_param = nullptr;
|
||||||
|
this->event_.gattc.data = nullptr;
|
||||||
|
return; // Invalid event, but we can't log in header file
|
||||||
|
}
|
||||||
|
|
||||||
|
// Heap-allocate param and data
|
||||||
|
// Heap allocation is used because GATTC/GATTS events are rare (<1% of events)
|
||||||
|
// while GAP events (99%) are stored inline to minimize memory usage
|
||||||
|
// IMPORTANT: This heap allocation provides clear ownership semantics:
|
||||||
|
// - The BLEEvent owns the allocated memory for its lifetime
|
||||||
|
// - The data remains valid from the BLE callback context until processed in the main loop
|
||||||
|
// - Without this copy, we'd have use-after-free bugs as ESP-IDF reuses the callback memory
|
||||||
|
this->event_.gattc.gattc_param = new esp_ble_gattc_cb_param_t(*p);
|
||||||
|
|
||||||
|
// Copy data for events that need it
|
||||||
|
// The param struct contains pointers (e.g., notify.value) that point to temporary buffers.
|
||||||
|
// We must copy this data to ensure it remains valid when the event is processed later.
|
||||||
|
switch (e) {
|
||||||
|
case ESP_GATTC_NOTIFY_EVT:
|
||||||
|
this->event_.gattc.data = new std::vector<uint8_t>(p->notify.value, p->notify.value + p->notify.value_len);
|
||||||
|
this->event_.gattc.gattc_param->notify.value = this->event_.gattc.data->data();
|
||||||
|
break;
|
||||||
|
case ESP_GATTC_READ_CHAR_EVT:
|
||||||
|
case ESP_GATTC_READ_DESCR_EVT:
|
||||||
|
this->event_.gattc.data = new std::vector<uint8_t>(p->read.value, p->read.value + p->read.value_len);
|
||||||
|
this->event_.gattc.gattc_param->read.value = this->event_.gattc.data->data();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this->event_.gattc.data = nullptr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize GATTS event data
|
||||||
|
void init_gatts_data_(esp_gatts_cb_event_t e, esp_gatt_if_t i, esp_ble_gatts_cb_param_t *p) {
|
||||||
|
this->event_.gatts.gatts_event = e;
|
||||||
|
this->event_.gatts.gatts_if = i;
|
||||||
|
|
||||||
|
if (p == nullptr) {
|
||||||
|
this->event_.gatts.gatts_param = nullptr;
|
||||||
|
this->event_.gatts.data = nullptr;
|
||||||
|
return; // Invalid event, but we can't log in header file
|
||||||
|
}
|
||||||
|
|
||||||
|
// Heap-allocate param and data
|
||||||
|
// Heap allocation is used because GATTC/GATTS events are rare (<1% of events)
|
||||||
|
// while GAP events (99%) are stored inline to minimize memory usage
|
||||||
|
// IMPORTANT: This heap allocation provides clear ownership semantics:
|
||||||
|
// - The BLEEvent owns the allocated memory for its lifetime
|
||||||
|
// - The data remains valid from the BLE callback context until processed in the main loop
|
||||||
|
// - Without this copy, we'd have use-after-free bugs as ESP-IDF reuses the callback memory
|
||||||
|
this->event_.gatts.gatts_param = new esp_ble_gatts_cb_param_t(*p);
|
||||||
|
|
||||||
|
// Copy data for events that need it
|
||||||
|
// The param struct contains pointers (e.g., write.value) that point to temporary buffers.
|
||||||
|
// We must copy this data to ensure it remains valid when the event is processed later.
|
||||||
|
switch (e) {
|
||||||
|
case ESP_GATTS_WRITE_EVT:
|
||||||
|
this->event_.gatts.data = new std::vector<uint8_t>(p->write.value, p->write.value + p->write.len);
|
||||||
|
this->event_.gatts.gatts_param->write.value = this->event_.gatts.data->data();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this->event_.gatts.data = nullptr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Verify the gap_event struct hasn't grown beyond expected size
|
||||||
|
// The gap member in the union should be 80 bytes (including the gap_event enum)
|
||||||
|
static_assert(sizeof(decltype(((BLEEvent *) nullptr)->event_.gap)) <= 80, "gap_event struct has grown beyond 80 bytes");
|
||||||
|
|
||||||
|
// Verify esp_ble_sec_t fits within our union
|
||||||
|
static_assert(sizeof(esp_ble_sec_t) <= 73, "esp_ble_sec_t is larger than BLEScanResult");
|
||||||
|
|
||||||
|
// BLEEvent total size: 84 bytes (80 byte union + 1 byte type + 3 bytes padding)
|
||||||
|
|
||||||
} // namespace esp32_ble
|
} // namespace esp32_ble
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
|
72
esphome/components/esp32_ble/ble_event_pool.h
Normal file
72
esphome/components/esp32_ble/ble_event_pool.h
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstddef>
|
||||||
|
#include "ble_event.h"
|
||||||
|
#include "queue.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace esp32_ble {
|
||||||
|
|
||||||
|
// BLE Event Pool - On-demand pool of BLEEvent objects to avoid heap fragmentation
|
||||||
|
// Events are allocated on first use and reused thereafter, growing to peak usage
|
||||||
|
template<uint8_t SIZE> class BLEEventPool {
|
||||||
|
public:
|
||||||
|
BLEEventPool() : total_created_(0) {}
|
||||||
|
|
||||||
|
~BLEEventPool() {
|
||||||
|
// Clean up any remaining events in the free list
|
||||||
|
BLEEvent *event;
|
||||||
|
while ((event = this->free_list_.pop()) != nullptr) {
|
||||||
|
delete event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate an event from the pool
|
||||||
|
// Returns nullptr if pool is full
|
||||||
|
BLEEvent *allocate() {
|
||||||
|
// Try to get from free list first
|
||||||
|
BLEEvent *event = this->free_list_.pop();
|
||||||
|
if (event != nullptr)
|
||||||
|
return event;
|
||||||
|
|
||||||
|
// Need to create a new event
|
||||||
|
if (this->total_created_ >= SIZE) {
|
||||||
|
// Pool is at capacity
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use internal RAM for better performance
|
||||||
|
RAMAllocator<BLEEvent> allocator(RAMAllocator<BLEEvent>::ALLOC_INTERNAL);
|
||||||
|
event = allocator.allocate(1);
|
||||||
|
|
||||||
|
if (event == nullptr) {
|
||||||
|
// Memory allocation failed
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Placement new to construct the object
|
||||||
|
new (event) BLEEvent();
|
||||||
|
this->total_created_++;
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return an event to the pool for reuse
|
||||||
|
void release(BLEEvent *event) {
|
||||||
|
if (event != nullptr) {
|
||||||
|
this->free_list_.push(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
LockFreeQueue<BLEEvent, SIZE> free_list_; // Free events ready for reuse
|
||||||
|
uint8_t total_created_; // Total events created (high water mark)
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace esp32_ble
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif
|
24
esphome/components/esp32_ble/ble_scan_result.h
Normal file
24
esphome/components/esp32_ble/ble_scan_result.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
#include <esp_gap_ble_api.h>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace esp32_ble {
|
||||||
|
|
||||||
|
// Structure for BLE scan results - only fields we actually use
|
||||||
|
struct __attribute__((packed)) BLEScanResult {
|
||||||
|
esp_bd_addr_t bda;
|
||||||
|
uint8_t ble_addr_type;
|
||||||
|
int8_t rssi;
|
||||||
|
uint8_t ble_adv[ESP_BLE_ADV_DATA_LEN_MAX + ESP_BLE_SCAN_RSP_DATA_LEN_MAX];
|
||||||
|
uint8_t adv_data_len;
|
||||||
|
uint8_t scan_rsp_len;
|
||||||
|
uint8_t search_evt;
|
||||||
|
}; // ~73 bytes vs ~400 bytes for full esp_ble_gap_cb_param_t
|
||||||
|
|
||||||
|
} // namespace esp32_ble
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif
|
@@ -2,52 +2,81 @@
|
|||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
#include <mutex>
|
#include <atomic>
|
||||||
#include <queue>
|
#include <cstddef>
|
||||||
|
|
||||||
#include <freertos/FreeRTOS.h>
|
|
||||||
#include <freertos/semphr.h>
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* BLE events come in from a separate Task (thread) in the ESP32 stack. Rather
|
* BLE events come in from a separate Task (thread) in the ESP32 stack. Rather
|
||||||
* than trying to deal with various locking strategies, all incoming GAP and GATT
|
* than using mutex-based locking, this lock-free queue allows the BLE
|
||||||
* events will simply be placed on a semaphore guarded queue. The next time the
|
* task to enqueue events without blocking. The main loop() then processes
|
||||||
* component runs loop(), these events are popped off the queue and handed at
|
* these events at a safer time.
|
||||||
* this safer time.
|
*
|
||||||
|
* This is a Single-Producer Single-Consumer (SPSC) lock-free ring buffer.
|
||||||
|
* The BLE task is the only producer, and the main loop() is the only consumer.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace esp32_ble {
|
namespace esp32_ble {
|
||||||
|
|
||||||
template<class T> class Queue {
|
template<class T, uint8_t SIZE> class LockFreeQueue {
|
||||||
public:
|
public:
|
||||||
Queue() { m_ = xSemaphoreCreateMutex(); }
|
LockFreeQueue() : head_(0), tail_(0), dropped_count_(0) {}
|
||||||
|
|
||||||
void push(T *element) {
|
bool push(T *element) {
|
||||||
if (element == nullptr)
|
if (element == nullptr)
|
||||||
return;
|
return false;
|
||||||
// It is not called from main loop. Thus it won't block main thread.
|
|
||||||
xSemaphoreTake(m_, portMAX_DELAY);
|
uint8_t current_tail = tail_.load(std::memory_order_relaxed);
|
||||||
q_.push(element);
|
uint8_t next_tail = (current_tail + 1) % SIZE;
|
||||||
xSemaphoreGive(m_);
|
|
||||||
|
if (next_tail == head_.load(std::memory_order_acquire)) {
|
||||||
|
// Buffer full
|
||||||
|
dropped_count_.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_[current_tail] = element;
|
||||||
|
tail_.store(next_tail, std::memory_order_release);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
T *pop() {
|
T *pop() {
|
||||||
T *element = nullptr;
|
uint8_t current_head = head_.load(std::memory_order_relaxed);
|
||||||
|
|
||||||
if (xSemaphoreTake(m_, 5L / portTICK_PERIOD_MS)) {
|
if (current_head == tail_.load(std::memory_order_acquire)) {
|
||||||
if (!q_.empty()) {
|
return nullptr; // Empty
|
||||||
element = q_.front();
|
|
||||||
q_.pop();
|
|
||||||
}
|
|
||||||
xSemaphoreGive(m_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
T *element = buffer_[current_head];
|
||||||
|
head_.store((current_head + 1) % SIZE, std::memory_order_release);
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t size() const {
|
||||||
|
uint8_t tail = tail_.load(std::memory_order_acquire);
|
||||||
|
uint8_t head = head_.load(std::memory_order_acquire);
|
||||||
|
return (tail - head + SIZE) % SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t get_and_reset_dropped_count() { return dropped_count_.exchange(0, std::memory_order_relaxed); }
|
||||||
|
|
||||||
|
void increment_dropped_count() { dropped_count_.fetch_add(1, std::memory_order_relaxed); }
|
||||||
|
|
||||||
|
bool empty() const { return head_.load(std::memory_order_acquire) == tail_.load(std::memory_order_acquire); }
|
||||||
|
|
||||||
|
bool full() const {
|
||||||
|
uint8_t next_tail = (tail_.load(std::memory_order_relaxed) + 1) % SIZE;
|
||||||
|
return next_tail == head_.load(std::memory_order_acquire);
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::queue<T *> q_;
|
T *buffer_[SIZE];
|
||||||
SemaphoreHandle_t m_;
|
// Atomic: written by producer (push/increment), read+reset by consumer (get_and_reset)
|
||||||
|
std::atomic<uint16_t> dropped_count_; // 65535 max - more than enough for drop tracking
|
||||||
|
// Atomic: written by consumer (pop), read by producer (push) to check if full
|
||||||
|
std::atomic<uint8_t> head_;
|
||||||
|
// Atomic: written by producer (push), read by consumer (pop) to check if empty
|
||||||
|
std::atomic<uint8_t> tail_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace esp32_ble
|
} // namespace esp32_ble
|
||||||
|
@@ -22,6 +22,16 @@ void BLEClientBase::setup() {
|
|||||||
this->connection_index_ = connection_index++;
|
this->connection_index_ = connection_index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BLEClientBase::set_state(espbt::ClientState st) {
|
||||||
|
ESP_LOGV(TAG, "[%d] [%s] Set state %d", this->connection_index_, this->address_str_.c_str(), (int) st);
|
||||||
|
ESPBTClient::set_state(st);
|
||||||
|
|
||||||
|
if (st == espbt::ClientState::READY_TO_CONNECT) {
|
||||||
|
// Enable loop when we need to connect
|
||||||
|
this->enable_loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void BLEClientBase::loop() {
|
void BLEClientBase::loop() {
|
||||||
if (!esp32_ble::global_ble->is_active()) {
|
if (!esp32_ble::global_ble->is_active()) {
|
||||||
this->set_state(espbt::ClientState::INIT);
|
this->set_state(espbt::ClientState::INIT);
|
||||||
@@ -37,9 +47,14 @@ void BLEClientBase::loop() {
|
|||||||
}
|
}
|
||||||
// READY_TO_CONNECT means we have discovered the device
|
// READY_TO_CONNECT means we have discovered the device
|
||||||
// and the scanner has been stopped by the tracker.
|
// and the scanner has been stopped by the tracker.
|
||||||
if (this->state_ == espbt::ClientState::READY_TO_CONNECT) {
|
else if (this->state_ == espbt::ClientState::READY_TO_CONNECT) {
|
||||||
this->connect();
|
this->connect();
|
||||||
}
|
}
|
||||||
|
// If its idle, we can disable the loop as set_state
|
||||||
|
// will enable it again when we need to connect.
|
||||||
|
else if (this->state_ == espbt::ClientState::IDLE) {
|
||||||
|
this->disable_loop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float BLEClientBase::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; }
|
float BLEClientBase::get_setup_priority() const { return setup_priority::AFTER_BLUETOOTH; }
|
||||||
|
@@ -93,22 +93,37 @@ class BLEClientBase : public espbt::ESPBTClient, public Component {
|
|||||||
|
|
||||||
bool check_addr(esp_bd_addr_t &addr) { return memcmp(addr, this->remote_bda_, sizeof(esp_bd_addr_t)) == 0; }
|
bool check_addr(esp_bd_addr_t &addr) { return memcmp(addr, this->remote_bda_, sizeof(esp_bd_addr_t)) == 0; }
|
||||||
|
|
||||||
|
void set_state(espbt::ClientState st) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int gattc_if_;
|
// Memory optimized layout for 32-bit systems
|
||||||
esp_bd_addr_t remote_bda_;
|
// Group 1: 8-byte types
|
||||||
esp_ble_addr_type_t remote_addr_type_{BLE_ADDR_TYPE_PUBLIC};
|
|
||||||
uint16_t conn_id_{UNSET_CONN_ID};
|
|
||||||
uint64_t address_{0};
|
uint64_t address_{0};
|
||||||
bool auto_connect_{false};
|
|
||||||
|
// Group 2: Container types (grouped for memory optimization)
|
||||||
std::string address_str_{};
|
std::string address_str_{};
|
||||||
uint8_t connection_index_;
|
|
||||||
int16_t service_count_{0};
|
|
||||||
uint16_t mtu_{23};
|
|
||||||
bool paired_{false};
|
|
||||||
espbt::ConnectionType connection_type_{espbt::ConnectionType::V1};
|
|
||||||
std::vector<BLEService *> services_;
|
std::vector<BLEService *> services_;
|
||||||
|
|
||||||
|
// Group 3: 4-byte types
|
||||||
|
int gattc_if_;
|
||||||
esp_gatt_status_t status_{ESP_GATT_OK};
|
esp_gatt_status_t status_{ESP_GATT_OK};
|
||||||
|
|
||||||
|
// Group 4: Arrays (6 bytes)
|
||||||
|
esp_bd_addr_t remote_bda_;
|
||||||
|
|
||||||
|
// Group 5: 2-byte types
|
||||||
|
uint16_t conn_id_{UNSET_CONN_ID};
|
||||||
|
uint16_t mtu_{23};
|
||||||
|
|
||||||
|
// Group 6: 1-byte types and small enums
|
||||||
|
esp_ble_addr_type_t remote_addr_type_{BLE_ADDR_TYPE_PUBLIC};
|
||||||
|
espbt::ConnectionType connection_type_{espbt::ConnectionType::V1};
|
||||||
|
uint8_t connection_index_;
|
||||||
|
uint8_t service_count_{0}; // ESP32 has max handles < 255, typical devices have < 50 services
|
||||||
|
bool auto_connect_{false};
|
||||||
|
bool paired_{false};
|
||||||
|
// 6 bytes used, 2 bytes padding
|
||||||
|
|
||||||
void log_event_(const char *name);
|
void log_event_(const char *name);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -268,6 +268,7 @@ async def to_code(config):
|
|||||||
|
|
||||||
parent = await cg.get_variable(config[esp32_ble.CONF_BLE_ID])
|
parent = await cg.get_variable(config[esp32_ble.CONF_BLE_ID])
|
||||||
cg.add(parent.register_gap_event_handler(var))
|
cg.add(parent.register_gap_event_handler(var))
|
||||||
|
cg.add(parent.register_gap_scan_event_handler(var))
|
||||||
cg.add(parent.register_gattc_event_handler(var))
|
cg.add(parent.register_gattc_event_handler(var))
|
||||||
cg.add(parent.register_ble_status_event_handler(var))
|
cg.add(parent.register_ble_status_event_handler(var))
|
||||||
cg.add(var.set_parent(parent))
|
cg.add(var.set_parent(parent))
|
||||||
|
@@ -50,17 +50,15 @@ void ESP32BLETracker::setup() {
|
|||||||
ESP_LOGE(TAG, "BLE Tracker was marked failed by ESP32BLE");
|
ESP_LOGE(TAG, "BLE Tracker was marked failed by ESP32BLE");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ExternalRAMAllocator<esp_ble_gap_cb_param_t::ble_scan_result_evt_param> allocator(
|
RAMAllocator<BLEScanResult> allocator;
|
||||||
ExternalRAMAllocator<esp_ble_gap_cb_param_t::ble_scan_result_evt_param>::ALLOW_FAILURE);
|
this->scan_ring_buffer_ = allocator.allocate(SCAN_RESULT_BUFFER_SIZE);
|
||||||
this->scan_result_buffer_ = allocator.allocate(ESP32BLETracker::SCAN_RESULT_BUFFER_SIZE);
|
|
||||||
|
|
||||||
if (this->scan_result_buffer_ == nullptr) {
|
if (this->scan_ring_buffer_ == nullptr) {
|
||||||
ESP_LOGE(TAG, "Could not allocate buffer for BLE Tracker!");
|
ESP_LOGE(TAG, "Could not allocate ring buffer for BLE Tracker!");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
global_esp32_ble_tracker = this;
|
global_esp32_ble_tracker = this;
|
||||||
this->scan_result_lock_ = xSemaphoreCreateMutex();
|
|
||||||
|
|
||||||
#ifdef USE_OTA
|
#ifdef USE_OTA
|
||||||
ota::get_global_ota_callback()->add_on_state_callback(
|
ota::get_global_ota_callback()->add_on_state_callback(
|
||||||
@@ -120,27 +118,31 @@ void ESP32BLETracker::loop() {
|
|||||||
}
|
}
|
||||||
bool promote_to_connecting = discovered && !searching && !connecting;
|
bool promote_to_connecting = discovered && !searching && !connecting;
|
||||||
|
|
||||||
if (this->scanner_state_ == ScannerState::RUNNING &&
|
// Process scan results from lock-free SPSC ring buffer
|
||||||
this->scan_result_index_ && // if it looks like we have a scan result we will take the lock
|
// Consumer side: This runs in the main loop thread
|
||||||
xSemaphoreTake(this->scan_result_lock_, 0)) {
|
if (this->scanner_state_ == ScannerState::RUNNING) {
|
||||||
uint32_t index = this->scan_result_index_;
|
// Load our own index with relaxed ordering (we're the only writer)
|
||||||
if (index >= ESP32BLETracker::SCAN_RESULT_BUFFER_SIZE) {
|
uint8_t read_idx = this->ring_read_index_.load(std::memory_order_relaxed);
|
||||||
ESP_LOGW(TAG, "Too many BLE events to process. Some devices may not show up.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->raw_advertisements_) {
|
// Load producer's index with acquire to see their latest writes
|
||||||
for (auto *listener : this->listeners_) {
|
uint8_t write_idx = this->ring_write_index_.load(std::memory_order_acquire);
|
||||||
listener->parse_devices(this->scan_result_buffer_, this->scan_result_index_);
|
|
||||||
}
|
|
||||||
for (auto *client : this->clients_) {
|
|
||||||
client->parse_devices(this->scan_result_buffer_, this->scan_result_index_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->parse_advertisements_) {
|
while (read_idx != write_idx) {
|
||||||
for (size_t i = 0; i < index; i++) {
|
// Process one result at a time directly from ring buffer
|
||||||
|
BLEScanResult &scan_result = this->scan_ring_buffer_[read_idx];
|
||||||
|
|
||||||
|
if (this->raw_advertisements_) {
|
||||||
|
for (auto *listener : this->listeners_) {
|
||||||
|
listener->parse_devices(&scan_result, 1);
|
||||||
|
}
|
||||||
|
for (auto *client : this->clients_) {
|
||||||
|
client->parse_devices(&scan_result, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->parse_advertisements_) {
|
||||||
ESPBTDevice device;
|
ESPBTDevice device;
|
||||||
device.parse_scan_rst(this->scan_result_buffer_[i]);
|
device.parse_scan_rst(scan_result);
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (auto *listener : this->listeners_) {
|
for (auto *listener : this->listeners_) {
|
||||||
@@ -161,9 +163,19 @@ void ESP32BLETracker::loop() {
|
|||||||
this->print_bt_device_info(device);
|
this->print_bt_device_info(device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Move to next entry in ring buffer
|
||||||
|
read_idx = (read_idx + 1) % SCAN_RESULT_BUFFER_SIZE;
|
||||||
|
|
||||||
|
// Store with release to ensure reads complete before index update
|
||||||
|
this->ring_read_index_.store(read_idx, std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log dropped results periodically
|
||||||
|
size_t dropped = this->scan_results_dropped_.exchange(0, std::memory_order_relaxed);
|
||||||
|
if (dropped > 0) {
|
||||||
|
ESP_LOGW(TAG, "Dropped %zu BLE scan results due to buffer overflow", dropped);
|
||||||
}
|
}
|
||||||
this->scan_result_index_ = 0;
|
|
||||||
xSemaphoreGive(this->scan_result_lock_);
|
|
||||||
}
|
}
|
||||||
if (this->scanner_state_ == ScannerState::STOPPED) {
|
if (this->scanner_state_ == ScannerState::STOPPED) {
|
||||||
this->end_of_scan_(); // Change state to IDLE
|
this->end_of_scan_(); // Change state to IDLE
|
||||||
@@ -370,9 +382,6 @@ void ESP32BLETracker::recalculate_advertisement_parser_types() {
|
|||||||
|
|
||||||
void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
|
void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case ESP_GAP_BLE_SCAN_RESULT_EVT:
|
|
||||||
this->gap_scan_result_(param->scan_rst);
|
|
||||||
break;
|
|
||||||
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
|
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
|
||||||
this->gap_scan_set_param_complete_(param->scan_param_cmpl);
|
this->gap_scan_set_param_complete_(param->scan_param_cmpl);
|
||||||
break;
|
break;
|
||||||
@@ -385,11 +394,57 @@ void ESP32BLETracker::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_ga
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Forward all events to clients (scan results are handled separately via gap_scan_event_handler)
|
||||||
for (auto *client : this->clients_) {
|
for (auto *client : this->clients_) {
|
||||||
client->gap_event_handler(event, param);
|
client->gap_event_handler(event, param);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ESP32BLETracker::gap_scan_event_handler(const BLEScanResult &scan_result) {
|
||||||
|
ESP_LOGV(TAG, "gap_scan_result - event %d", scan_result.search_evt);
|
||||||
|
|
||||||
|
if (scan_result.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) {
|
||||||
|
// Lock-free SPSC ring buffer write (Producer side)
|
||||||
|
// This runs in the ESP-IDF Bluetooth stack callback thread
|
||||||
|
// IMPORTANT: Only this thread writes to ring_write_index_
|
||||||
|
|
||||||
|
// Load our own index with relaxed ordering (we're the only writer)
|
||||||
|
uint8_t write_idx = this->ring_write_index_.load(std::memory_order_relaxed);
|
||||||
|
uint8_t next_write_idx = (write_idx + 1) % SCAN_RESULT_BUFFER_SIZE;
|
||||||
|
|
||||||
|
// Load consumer's index with acquire to see their latest updates
|
||||||
|
uint8_t read_idx = this->ring_read_index_.load(std::memory_order_acquire);
|
||||||
|
|
||||||
|
// Check if buffer is full
|
||||||
|
if (next_write_idx != read_idx) {
|
||||||
|
// Write to ring buffer
|
||||||
|
this->scan_ring_buffer_[write_idx] = scan_result;
|
||||||
|
|
||||||
|
// Store with release to ensure the write is visible before index update
|
||||||
|
this->ring_write_index_.store(next_write_idx, std::memory_order_release);
|
||||||
|
} else {
|
||||||
|
// Buffer full, track dropped results
|
||||||
|
this->scan_results_dropped_.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
} else if (scan_result.search_evt == ESP_GAP_SEARCH_INQ_CMPL_EVT) {
|
||||||
|
// Scan finished on its own
|
||||||
|
if (this->scanner_state_ != ScannerState::RUNNING) {
|
||||||
|
if (this->scanner_state_ == ScannerState::STOPPING) {
|
||||||
|
ESP_LOGE(TAG, "Scan was not running when scan completed.");
|
||||||
|
} else if (this->scanner_state_ == ScannerState::STARTING) {
|
||||||
|
ESP_LOGE(TAG, "Scan was not started when scan completed.");
|
||||||
|
} else if (this->scanner_state_ == ScannerState::FAILED) {
|
||||||
|
ESP_LOGE(TAG, "Scan was in failed state when scan completed.");
|
||||||
|
} else if (this->scanner_state_ == ScannerState::IDLE) {
|
||||||
|
ESP_LOGE(TAG, "Scan was idle when scan completed.");
|
||||||
|
} else if (this->scanner_state_ == ScannerState::STOPPED) {
|
||||||
|
ESP_LOGE(TAG, "Scan was stopped when scan completed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->set_scanner_state_(ScannerState::STOPPED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ESP32BLETracker::gap_scan_set_param_complete_(const esp_ble_gap_cb_param_t::ble_scan_param_cmpl_evt_param ¶m) {
|
void ESP32BLETracker::gap_scan_set_param_complete_(const esp_ble_gap_cb_param_t::ble_scan_param_cmpl_evt_param ¶m) {
|
||||||
ESP_LOGV(TAG, "gap_scan_set_param_complete - status %d", param.status);
|
ESP_LOGV(TAG, "gap_scan_set_param_complete - status %d", param.status);
|
||||||
if (param.status == ESP_BT_STATUS_DONE) {
|
if (param.status == ESP_BT_STATUS_DONE) {
|
||||||
@@ -444,34 +499,6 @@ void ESP32BLETracker::gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_
|
|||||||
this->set_scanner_state_(ScannerState::STOPPED);
|
this->set_scanner_state_(ScannerState::STOPPED);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESP32BLETracker::gap_scan_result_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) {
|
|
||||||
ESP_LOGV(TAG, "gap_scan_result - event %d", param.search_evt);
|
|
||||||
if (param.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) {
|
|
||||||
if (xSemaphoreTake(this->scan_result_lock_, 0)) {
|
|
||||||
if (this->scan_result_index_ < ESP32BLETracker::SCAN_RESULT_BUFFER_SIZE) {
|
|
||||||
this->scan_result_buffer_[this->scan_result_index_++] = param;
|
|
||||||
}
|
|
||||||
xSemaphoreGive(this->scan_result_lock_);
|
|
||||||
}
|
|
||||||
} else if (param.search_evt == ESP_GAP_SEARCH_INQ_CMPL_EVT) {
|
|
||||||
// Scan finished on its own
|
|
||||||
if (this->scanner_state_ != ScannerState::RUNNING) {
|
|
||||||
if (this->scanner_state_ == ScannerState::STOPPING) {
|
|
||||||
ESP_LOGE(TAG, "Scan was not running when scan completed.");
|
|
||||||
} else if (this->scanner_state_ == ScannerState::STARTING) {
|
|
||||||
ESP_LOGE(TAG, "Scan was not started when scan completed.");
|
|
||||||
} else if (this->scanner_state_ == ScannerState::FAILED) {
|
|
||||||
ESP_LOGE(TAG, "Scan was in failed state when scan completed.");
|
|
||||||
} else if (this->scanner_state_ == ScannerState::IDLE) {
|
|
||||||
ESP_LOGE(TAG, "Scan was idle when scan completed.");
|
|
||||||
} else if (this->scanner_state_ == ScannerState::STOPPED) {
|
|
||||||
ESP_LOGE(TAG, "Scan was stopped when scan completed.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this->set_scanner_state_(ScannerState::STOPPED);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ESP32BLETracker::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
void ESP32BLETracker::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||||
esp_ble_gattc_cb_param_t *param) {
|
esp_ble_gattc_cb_param_t *param) {
|
||||||
for (auto *client : this->clients_) {
|
for (auto *client : this->clients_) {
|
||||||
@@ -494,13 +521,15 @@ optional<ESPBLEiBeacon> ESPBLEiBeacon::from_manufacturer_data(const ServiceData
|
|||||||
return ESPBLEiBeacon(data.data.data());
|
return ESPBLEiBeacon(data.data.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) {
|
void ESPBTDevice::parse_scan_rst(const BLEScanResult &scan_result) {
|
||||||
this->scan_result_ = param;
|
|
||||||
for (uint8_t i = 0; i < ESP_BD_ADDR_LEN; i++)
|
for (uint8_t i = 0; i < ESP_BD_ADDR_LEN; i++)
|
||||||
this->address_[i] = param.bda[i];
|
this->address_[i] = scan_result.bda[i];
|
||||||
this->address_type_ = param.ble_addr_type;
|
this->address_type_ = static_cast<esp_ble_addr_type_t>(scan_result.ble_addr_type);
|
||||||
this->rssi_ = param.rssi;
|
this->rssi_ = scan_result.rssi;
|
||||||
this->parse_adv_(param);
|
|
||||||
|
// Parse advertisement data directly
|
||||||
|
uint8_t total_len = scan_result.adv_data_len + scan_result.scan_rsp_len;
|
||||||
|
this->parse_adv_(scan_result.ble_adv, total_len);
|
||||||
|
|
||||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||||
ESP_LOGVV(TAG, "Parse Result:");
|
ESP_LOGVV(TAG, "Parse Result:");
|
||||||
@@ -558,13 +587,13 @@ void ESPBTDevice::parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_e
|
|||||||
ESP_LOGVV(TAG, " Data: %s", format_hex_pretty(data.data).c_str());
|
ESP_LOGVV(TAG, " Data: %s", format_hex_pretty(data.data).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOGVV(TAG, " Adv data: %s", format_hex_pretty(param.ble_adv, param.adv_data_len + param.scan_rsp_len).c_str());
|
ESP_LOGVV(TAG, " Adv data: %s",
|
||||||
|
format_hex_pretty(scan_result.ble_adv, scan_result.adv_data_len + scan_result.scan_rsp_len).c_str());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
void ESPBTDevice::parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) {
|
|
||||||
|
void ESPBTDevice::parse_adv_(const uint8_t *payload, uint8_t len) {
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
const uint8_t *payload = param.ble_adv;
|
|
||||||
uint8_t len = param.adv_data_len + param.scan_rsp_len;
|
|
||||||
|
|
||||||
while (offset + 2 < len) {
|
while (offset + 2 < len) {
|
||||||
const uint8_t field_length = payload[offset++]; // First byte is length of adv record
|
const uint8_t field_length = payload[offset++]; // First byte is length of adv record
|
||||||
|
@@ -6,6 +6,7 @@
|
|||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -62,7 +63,7 @@ class ESPBLEiBeacon {
|
|||||||
|
|
||||||
class ESPBTDevice {
|
class ESPBTDevice {
|
||||||
public:
|
public:
|
||||||
void parse_scan_rst(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m);
|
void parse_scan_rst(const BLEScanResult &scan_result);
|
||||||
|
|
||||||
std::string address_str() const;
|
std::string address_str() const;
|
||||||
|
|
||||||
@@ -84,8 +85,6 @@ class ESPBTDevice {
|
|||||||
|
|
||||||
const std::vector<ServiceData> &get_service_datas() const { return service_datas_; }
|
const std::vector<ServiceData> &get_service_datas() const { return service_datas_; }
|
||||||
|
|
||||||
const esp_ble_gap_cb_param_t::ble_scan_result_evt_param &get_scan_result() const { return scan_result_; }
|
|
||||||
|
|
||||||
bool resolve_irk(const uint8_t *irk) const;
|
bool resolve_irk(const uint8_t *irk) const;
|
||||||
|
|
||||||
optional<ESPBLEiBeacon> get_ibeacon() const {
|
optional<ESPBLEiBeacon> get_ibeacon() const {
|
||||||
@@ -98,7 +97,7 @@ class ESPBTDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void parse_adv_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m);
|
void parse_adv_(const uint8_t *payload, uint8_t len);
|
||||||
|
|
||||||
esp_bd_addr_t address_{
|
esp_bd_addr_t address_{
|
||||||
0,
|
0,
|
||||||
@@ -112,7 +111,6 @@ class ESPBTDevice {
|
|||||||
std::vector<ESPBTUUID> service_uuids_{};
|
std::vector<ESPBTUUID> service_uuids_{};
|
||||||
std::vector<ServiceData> manufacturer_datas_{};
|
std::vector<ServiceData> manufacturer_datas_{};
|
||||||
std::vector<ServiceData> service_datas_{};
|
std::vector<ServiceData> service_datas_{};
|
||||||
esp_ble_gap_cb_param_t::ble_scan_result_evt_param scan_result_{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ESP32BLETracker;
|
class ESP32BLETracker;
|
||||||
@@ -121,9 +119,7 @@ class ESPBTDeviceListener {
|
|||||||
public:
|
public:
|
||||||
virtual void on_scan_end() {}
|
virtual void on_scan_end() {}
|
||||||
virtual bool parse_device(const ESPBTDevice &device) = 0;
|
virtual bool parse_device(const ESPBTDevice &device) = 0;
|
||||||
virtual bool parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) {
|
virtual bool parse_devices(const BLEScanResult *scan_results, size_t count) { return false; };
|
||||||
return false;
|
|
||||||
};
|
|
||||||
virtual AdvertisementParserType get_advertisement_parser_type() {
|
virtual AdvertisementParserType get_advertisement_parser_type() {
|
||||||
return AdvertisementParserType::PARSED_ADVERTISEMENTS;
|
return AdvertisementParserType::PARSED_ADVERTISEMENTS;
|
||||||
};
|
};
|
||||||
@@ -133,7 +129,7 @@ class ESPBTDeviceListener {
|
|||||||
ESP32BLETracker *parent_{nullptr};
|
ESP32BLETracker *parent_{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ClientState {
|
enum class ClientState : uint8_t {
|
||||||
// Connection is allocated
|
// Connection is allocated
|
||||||
INIT,
|
INIT,
|
||||||
// Client is disconnecting
|
// Client is disconnecting
|
||||||
@@ -169,7 +165,7 @@ enum class ScannerState {
|
|||||||
STOPPED,
|
STOPPED,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ConnectionType {
|
enum class ConnectionType : uint8_t {
|
||||||
// The default connection type, we hold all the services in ram
|
// The default connection type, we hold all the services in ram
|
||||||
// for the duration of the connection.
|
// for the duration of the connection.
|
||||||
V1,
|
V1,
|
||||||
@@ -197,19 +193,24 @@ class ESPBTClient : public ESPBTDeviceListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClientState state() const { return state_; }
|
ClientState state() const { return state_; }
|
||||||
int app_id;
|
|
||||||
|
// Memory optimized layout
|
||||||
|
uint8_t app_id; // App IDs are small integers assigned sequentially
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
// Group 1: 1-byte types
|
||||||
ClientState state_{ClientState::INIT};
|
ClientState state_{ClientState::INIT};
|
||||||
// want_disconnect_ is set to true when a disconnect is requested
|
// want_disconnect_ is set to true when a disconnect is requested
|
||||||
// while the client is connecting. This is used to disconnect the
|
// while the client is connecting. This is used to disconnect the
|
||||||
// client as soon as we get the connection id (conn_id_) from the
|
// client as soon as we get the connection id (conn_id_) from the
|
||||||
// ESP_GATTC_OPEN_EVT event.
|
// ESP_GATTC_OPEN_EVT event.
|
||||||
bool want_disconnect_{false};
|
bool want_disconnect_{false};
|
||||||
|
// 2 bytes used, 2 bytes padding
|
||||||
};
|
};
|
||||||
|
|
||||||
class ESP32BLETracker : public Component,
|
class ESP32BLETracker : public Component,
|
||||||
public GAPEventHandler,
|
public GAPEventHandler,
|
||||||
|
public GAPScanEventHandler,
|
||||||
public GATTcEventHandler,
|
public GATTcEventHandler,
|
||||||
public BLEStatusEventHandler,
|
public BLEStatusEventHandler,
|
||||||
public Parented<ESP32BLE> {
|
public Parented<ESP32BLE> {
|
||||||
@@ -240,6 +241,7 @@ class ESP32BLETracker : public Component,
|
|||||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||||
esp_ble_gattc_cb_param_t *param) override;
|
esp_ble_gattc_cb_param_t *param) override;
|
||||||
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
|
void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override;
|
||||||
|
void gap_scan_event_handler(const BLEScanResult &scan_result) override;
|
||||||
void ble_before_disabled_event_handler() override;
|
void ble_before_disabled_event_handler() override;
|
||||||
|
|
||||||
void add_scanner_state_callback(std::function<void(ScannerState)> &&callback) {
|
void add_scanner_state_callback(std::function<void(ScannerState)> &&callback) {
|
||||||
@@ -264,7 +266,7 @@ class ESP32BLETracker : public Component,
|
|||||||
/// Called to set the scanner state. Will also call callbacks to let listeners know when state is changed.
|
/// Called to set the scanner state. Will also call callbacks to let listeners know when state is changed.
|
||||||
void set_scanner_state_(ScannerState state);
|
void set_scanner_state_(ScannerState state);
|
||||||
|
|
||||||
int app_id_{0};
|
uint8_t app_id_{0};
|
||||||
|
|
||||||
/// Vector of addresses that have already been printed in print_bt_device_info
|
/// Vector of addresses that have already been printed in print_bt_device_info
|
||||||
std::vector<uint64_t> already_discovered_;
|
std::vector<uint64_t> already_discovered_;
|
||||||
@@ -285,14 +287,16 @@ class ESP32BLETracker : public Component,
|
|||||||
bool ble_was_disabled_{true};
|
bool ble_was_disabled_{true};
|
||||||
bool raw_advertisements_{false};
|
bool raw_advertisements_{false};
|
||||||
bool parse_advertisements_{false};
|
bool parse_advertisements_{false};
|
||||||
SemaphoreHandle_t scan_result_lock_;
|
|
||||||
size_t scan_result_index_{0};
|
// Lock-free Single-Producer Single-Consumer (SPSC) ring buffer for scan results
|
||||||
#ifdef USE_PSRAM
|
// Producer: ESP-IDF Bluetooth stack callback (gap_scan_event_handler)
|
||||||
const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 32;
|
// Consumer: ESPHome main loop (loop() method)
|
||||||
#else
|
// This design ensures zero blocking in the BT callback and prevents scan result loss
|
||||||
const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 20;
|
BLEScanResult *scan_ring_buffer_;
|
||||||
#endif // USE_PSRAM
|
std::atomic<uint8_t> ring_write_index_{0}; // Written only by BT callback (producer)
|
||||||
esp_ble_gap_cb_param_t::ble_scan_result_evt_param *scan_result_buffer_;
|
std::atomic<uint8_t> ring_read_index_{0}; // Written only by main loop (consumer)
|
||||||
|
std::atomic<uint16_t> scan_results_dropped_{0}; // Tracks buffer overflow events
|
||||||
|
|
||||||
esp_bt_status_t scan_start_failed_{ESP_BT_STATUS_SUCCESS};
|
esp_bt_status_t scan_start_failed_{ESP_BT_STATUS_SUCCESS};
|
||||||
esp_bt_status_t scan_set_param_failed_{ESP_BT_STATUS_SUCCESS};
|
esp_bt_status_t scan_set_param_failed_{ESP_BT_STATUS_SUCCESS};
|
||||||
int connecting_{0};
|
int connecting_{0};
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
from esphome import automation, pins
|
from esphome import automation, pins
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import i2c
|
||||||
from esphome.components.esp32 import add_idf_component
|
from esphome.components.esp32 import add_idf_component
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
@@ -7,6 +8,7 @@ from esphome.const import (
|
|||||||
CONF_CONTRAST,
|
CONF_CONTRAST,
|
||||||
CONF_DATA_PINS,
|
CONF_DATA_PINS,
|
||||||
CONF_FREQUENCY,
|
CONF_FREQUENCY,
|
||||||
|
CONF_I2C_ID,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_PIN,
|
CONF_PIN,
|
||||||
CONF_RESET_PIN,
|
CONF_RESET_PIN,
|
||||||
@@ -149,93 +151,104 @@ CONF_ON_IMAGE = "on_image"
|
|||||||
|
|
||||||
camera_range_param = cv.int_range(min=-2, max=2)
|
camera_range_param = cv.int_range(min=-2, max=2)
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
|
CONFIG_SCHEMA = cv.All(
|
||||||
{
|
cv.ENTITY_BASE_SCHEMA.extend(
|
||||||
cv.GenerateID(): cv.declare_id(ESP32Camera),
|
{
|
||||||
# pin assignment
|
cv.GenerateID(): cv.declare_id(ESP32Camera),
|
||||||
cv.Required(CONF_DATA_PINS): cv.All(
|
# pin assignment
|
||||||
[pins.internal_gpio_input_pin_number], cv.Length(min=8, max=8)
|
cv.Required(CONF_DATA_PINS): cv.All(
|
||||||
),
|
[pins.internal_gpio_input_pin_number], cv.Length(min=8, max=8)
|
||||||
cv.Required(CONF_VSYNC_PIN): pins.internal_gpio_input_pin_number,
|
),
|
||||||
cv.Required(CONF_HREF_PIN): pins.internal_gpio_input_pin_number,
|
cv.Required(CONF_VSYNC_PIN): pins.internal_gpio_input_pin_number,
|
||||||
cv.Required(CONF_PIXEL_CLOCK_PIN): pins.internal_gpio_input_pin_number,
|
cv.Required(CONF_HREF_PIN): pins.internal_gpio_input_pin_number,
|
||||||
cv.Required(CONF_EXTERNAL_CLOCK): cv.Schema(
|
cv.Required(CONF_PIXEL_CLOCK_PIN): pins.internal_gpio_input_pin_number,
|
||||||
{
|
cv.Required(CONF_EXTERNAL_CLOCK): cv.Schema(
|
||||||
cv.Required(CONF_PIN): pins.internal_gpio_input_pin_number,
|
{
|
||||||
cv.Optional(CONF_FREQUENCY, default="20MHz"): cv.All(
|
cv.Required(CONF_PIN): pins.internal_gpio_input_pin_number,
|
||||||
cv.frequency, cv.Range(min=8e6, max=20e6)
|
cv.Optional(CONF_FREQUENCY, default="20MHz"): cv.All(
|
||||||
),
|
cv.frequency, cv.Range(min=8e6, max=20e6)
|
||||||
}
|
),
|
||||||
),
|
}
|
||||||
cv.Required(CONF_I2C_PINS): cv.Schema(
|
),
|
||||||
{
|
cv.Optional(CONF_I2C_PINS): cv.Schema(
|
||||||
cv.Required(CONF_SDA): pins.internal_gpio_output_pin_number,
|
{
|
||||||
cv.Required(CONF_SCL): pins.internal_gpio_output_pin_number,
|
cv.Required(CONF_SDA): pins.internal_gpio_output_pin_number,
|
||||||
}
|
cv.Required(CONF_SCL): pins.internal_gpio_output_pin_number,
|
||||||
),
|
}
|
||||||
cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number,
|
),
|
||||||
cv.Optional(CONF_POWER_DOWN_PIN): pins.internal_gpio_output_pin_number,
|
cv.Optional(CONF_I2C_ID): cv.Any(
|
||||||
# image
|
cv.use_id(i2c.InternalI2CBus),
|
||||||
cv.Optional(CONF_RESOLUTION, default="640X480"): cv.enum(
|
msg="I2C bus must be an internal ESP32 I2C bus",
|
||||||
FRAME_SIZES, upper=True
|
),
|
||||||
),
|
cv.Optional(CONF_RESET_PIN): pins.internal_gpio_output_pin_number,
|
||||||
cv.Optional(CONF_JPEG_QUALITY, default=10): cv.int_range(min=6, max=63),
|
cv.Optional(CONF_POWER_DOWN_PIN): pins.internal_gpio_output_pin_number,
|
||||||
cv.Optional(CONF_CONTRAST, default=0): camera_range_param,
|
# image
|
||||||
cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param,
|
cv.Optional(CONF_RESOLUTION, default="640X480"): cv.enum(
|
||||||
cv.Optional(CONF_SATURATION, default=0): camera_range_param,
|
FRAME_SIZES, upper=True
|
||||||
cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean,
|
),
|
||||||
cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean,
|
cv.Optional(CONF_JPEG_QUALITY, default=10): cv.int_range(min=6, max=63),
|
||||||
cv.Optional(CONF_SPECIAL_EFFECT, default="NONE"): cv.enum(
|
cv.Optional(CONF_CONTRAST, default=0): camera_range_param,
|
||||||
ENUM_SPECIAL_EFFECT, upper=True
|
cv.Optional(CONF_BRIGHTNESS, default=0): camera_range_param,
|
||||||
),
|
cv.Optional(CONF_SATURATION, default=0): camera_range_param,
|
||||||
# exposure
|
cv.Optional(CONF_VERTICAL_FLIP, default=True): cv.boolean,
|
||||||
cv.Optional(CONF_AGC_MODE, default="AUTO"): cv.enum(
|
cv.Optional(CONF_HORIZONTAL_MIRROR, default=True): cv.boolean,
|
||||||
ENUM_GAIN_CONTROL_MODE, upper=True
|
cv.Optional(CONF_SPECIAL_EFFECT, default="NONE"): cv.enum(
|
||||||
),
|
ENUM_SPECIAL_EFFECT, upper=True
|
||||||
cv.Optional(CONF_AEC2, default=False): cv.boolean,
|
),
|
||||||
cv.Optional(CONF_AE_LEVEL, default=0): camera_range_param,
|
# exposure
|
||||||
cv.Optional(CONF_AEC_VALUE, default=300): cv.int_range(min=0, max=1200),
|
cv.Optional(CONF_AGC_MODE, default="AUTO"): cv.enum(
|
||||||
# gains
|
ENUM_GAIN_CONTROL_MODE, upper=True
|
||||||
cv.Optional(CONF_AEC_MODE, default="AUTO"): cv.enum(
|
),
|
||||||
ENUM_GAIN_CONTROL_MODE, upper=True
|
cv.Optional(CONF_AEC2, default=False): cv.boolean,
|
||||||
),
|
cv.Optional(CONF_AE_LEVEL, default=0): camera_range_param,
|
||||||
cv.Optional(CONF_AGC_VALUE, default=0): cv.int_range(min=0, max=30),
|
cv.Optional(CONF_AEC_VALUE, default=300): cv.int_range(min=0, max=1200),
|
||||||
cv.Optional(CONF_AGC_GAIN_CEILING, default="2X"): cv.enum(
|
# gains
|
||||||
ENUM_GAIN_CEILING, upper=True
|
cv.Optional(CONF_AEC_MODE, default="AUTO"): cv.enum(
|
||||||
),
|
ENUM_GAIN_CONTROL_MODE, upper=True
|
||||||
# white balance
|
),
|
||||||
cv.Optional(CONF_WB_MODE, default="AUTO"): cv.enum(ENUM_WB_MODE, upper=True),
|
cv.Optional(CONF_AGC_VALUE, default=0): cv.int_range(min=0, max=30),
|
||||||
# test pattern
|
cv.Optional(CONF_AGC_GAIN_CEILING, default="2X"): cv.enum(
|
||||||
cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean,
|
ENUM_GAIN_CEILING, upper=True
|
||||||
# framerates
|
),
|
||||||
cv.Optional(CONF_MAX_FRAMERATE, default="10 fps"): cv.All(
|
# white balance
|
||||||
cv.framerate, cv.Range(min=0, min_included=False, max=60)
|
cv.Optional(CONF_WB_MODE, default="AUTO"): cv.enum(
|
||||||
),
|
ENUM_WB_MODE, upper=True
|
||||||
cv.Optional(CONF_IDLE_FRAMERATE, default="0.1 fps"): cv.All(
|
),
|
||||||
cv.framerate, cv.Range(min=0, max=1)
|
# test pattern
|
||||||
),
|
cv.Optional(CONF_TEST_PATTERN, default=False): cv.boolean,
|
||||||
cv.Optional(CONF_FRAME_BUFFER_COUNT, default=1): cv.int_range(min=1, max=2),
|
# framerates
|
||||||
cv.Optional(CONF_ON_STREAM_START): automation.validate_automation(
|
cv.Optional(CONF_MAX_FRAMERATE, default="10 fps"): cv.All(
|
||||||
{
|
cv.framerate, cv.Range(min=0, min_included=False, max=60)
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
),
|
||||||
ESP32CameraStreamStartTrigger
|
cv.Optional(CONF_IDLE_FRAMERATE, default="0.1 fps"): cv.All(
|
||||||
),
|
cv.framerate, cv.Range(min=0, max=1)
|
||||||
}
|
),
|
||||||
),
|
cv.Optional(CONF_FRAME_BUFFER_COUNT, default=1): cv.int_range(min=1, max=2),
|
||||||
cv.Optional(CONF_ON_STREAM_STOP): automation.validate_automation(
|
cv.Optional(CONF_ON_STREAM_START): automation.validate_automation(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||||
ESP32CameraStreamStopTrigger
|
ESP32CameraStreamStartTrigger
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_ON_IMAGE): automation.validate_automation(
|
cv.Optional(CONF_ON_STREAM_STOP): automation.validate_automation(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESP32CameraImageTrigger),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||||
}
|
ESP32CameraStreamStopTrigger
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
),
|
||||||
|
cv.Optional(CONF_ON_IMAGE): automation.validate_automation(
|
||||||
|
{
|
||||||
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||||
|
ESP32CameraImageTrigger
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
).extend(cv.COMPONENT_SCHEMA),
|
||||||
|
cv.has_exactly_one_key(CONF_I2C_PINS, CONF_I2C_ID),
|
||||||
|
)
|
||||||
|
|
||||||
SETTERS = {
|
SETTERS = {
|
||||||
# pin assignment
|
# pin assignment
|
||||||
@@ -280,8 +293,12 @@ async def to_code(config):
|
|||||||
|
|
||||||
extclk = config[CONF_EXTERNAL_CLOCK]
|
extclk = config[CONF_EXTERNAL_CLOCK]
|
||||||
cg.add(var.set_external_clock(extclk[CONF_PIN], extclk[CONF_FREQUENCY]))
|
cg.add(var.set_external_clock(extclk[CONF_PIN], extclk[CONF_FREQUENCY]))
|
||||||
i2c_pins = config[CONF_I2C_PINS]
|
if i2c_id := config.get(CONF_I2C_ID):
|
||||||
cg.add(var.set_i2c_pins(i2c_pins[CONF_SDA], i2c_pins[CONF_SCL]))
|
i2c_hub = await cg.get_variable(i2c_id)
|
||||||
|
cg.add(var.set_i2c_id(i2c_hub))
|
||||||
|
else:
|
||||||
|
i2c_pins = config[CONF_I2C_PINS]
|
||||||
|
cg.add(var.set_i2c_pins(i2c_pins[CONF_SDA], i2c_pins[CONF_SCL]))
|
||||||
cg.add(var.set_max_update_interval(1000 / config[CONF_MAX_FRAMERATE]))
|
cg.add(var.set_max_update_interval(1000 / config[CONF_MAX_FRAMERATE]))
|
||||||
if config[CONF_IDLE_FRAMERATE] == 0:
|
if config[CONF_IDLE_FRAMERATE] == 0:
|
||||||
cg.add(var.set_idle_update_interval(0))
|
cg.add(var.set_idle_update_interval(0))
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
#include "esp32_camera.h"
|
#include "esp32_camera.h"
|
||||||
#include "esphome/core/log.h"
|
|
||||||
#include "esphome/core/hal.h"
|
|
||||||
#include "esphome/core/application.h"
|
#include "esphome/core/application.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
#include <freertos/task.h>
|
#include <freertos/task.h>
|
||||||
|
|
||||||
@@ -16,6 +16,12 @@ static const char *const TAG = "esp32_camera";
|
|||||||
void ESP32Camera::setup() {
|
void ESP32Camera::setup() {
|
||||||
global_esp32_camera = this;
|
global_esp32_camera = this;
|
||||||
|
|
||||||
|
#ifdef USE_I2C
|
||||||
|
if (this->i2c_bus_ != nullptr) {
|
||||||
|
this->config_.sccb_i2c_port = this->i2c_bus_->get_port();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* initialize time to now */
|
/* initialize time to now */
|
||||||
this->last_update_ = millis();
|
this->last_update_ = millis();
|
||||||
|
|
||||||
@@ -57,7 +63,7 @@ void ESP32Camera::dump_config() {
|
|||||||
" External Clock: Pin:%d Frequency:%u\n"
|
" External Clock: Pin:%d Frequency:%u\n"
|
||||||
" I2C Pins: SDA:%d SCL:%d\n"
|
" I2C Pins: SDA:%d SCL:%d\n"
|
||||||
" Reset Pin: %d",
|
" Reset Pin: %d",
|
||||||
this->name_.c_str(), YESNO(this->internal_), conf.pin_d0, conf.pin_d1, conf.pin_d2, conf.pin_d3,
|
this->name_.c_str(), YESNO(this->is_internal()), conf.pin_d0, conf.pin_d1, conf.pin_d2, conf.pin_d3,
|
||||||
conf.pin_d4, conf.pin_d5, conf.pin_d6, conf.pin_d7, conf.pin_vsync, conf.pin_href, conf.pin_pclk,
|
conf.pin_d4, conf.pin_d5, conf.pin_d6, conf.pin_d7, conf.pin_vsync, conf.pin_href, conf.pin_pclk,
|
||||||
conf.pin_xclk, conf.xclk_freq_hz, conf.pin_sccb_sda, conf.pin_sccb_scl, conf.pin_reset);
|
conf.pin_xclk, conf.xclk_freq_hz, conf.pin_sccb_sda, conf.pin_sccb_scl, conf.pin_reset);
|
||||||
switch (this->config_.frame_size) {
|
switch (this->config_.frame_size) {
|
||||||
@@ -246,6 +252,13 @@ void ESP32Camera::set_i2c_pins(uint8_t sda, uint8_t scl) {
|
|||||||
this->config_.pin_sccb_sda = sda;
|
this->config_.pin_sccb_sda = sda;
|
||||||
this->config_.pin_sccb_scl = scl;
|
this->config_.pin_sccb_scl = scl;
|
||||||
}
|
}
|
||||||
|
#ifdef USE_I2C
|
||||||
|
void ESP32Camera::set_i2c_id(i2c::InternalI2CBus *i2c_bus) {
|
||||||
|
this->i2c_bus_ = i2c_bus;
|
||||||
|
this->config_.pin_sccb_sda = -1;
|
||||||
|
this->config_.pin_sccb_scl = -1;
|
||||||
|
}
|
||||||
|
#endif // USE_I2C
|
||||||
void ESP32Camera::set_reset_pin(uint8_t pin) { this->config_.pin_reset = pin; }
|
void ESP32Camera::set_reset_pin(uint8_t pin) { this->config_.pin_reset = pin; }
|
||||||
void ESP32Camera::set_power_down_pin(uint8_t pin) { this->config_.pin_pwdn = pin; }
|
void ESP32Camera::set_power_down_pin(uint8_t pin) { this->config_.pin_pwdn = pin; }
|
||||||
|
|
||||||
|
@@ -2,13 +2,17 @@
|
|||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
#include <esp_camera.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/queue.h>
|
||||||
#include "esphome/core/automation.h"
|
#include "esphome/core/automation.h"
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/entity_base.h"
|
#include "esphome/core/entity_base.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include <esp_camera.h>
|
|
||||||
#include <freertos/FreeRTOS.h>
|
#ifdef USE_I2C
|
||||||
#include <freertos/queue.h>
|
#include "esphome/components/i2c/i2c_bus.h"
|
||||||
|
#endif // USE_I2C
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace esp32_camera {
|
namespace esp32_camera {
|
||||||
@@ -118,6 +122,9 @@ class ESP32Camera : public EntityBase, public Component {
|
|||||||
void set_pixel_clock_pin(uint8_t pin);
|
void set_pixel_clock_pin(uint8_t pin);
|
||||||
void set_external_clock(uint8_t pin, uint32_t frequency);
|
void set_external_clock(uint8_t pin, uint32_t frequency);
|
||||||
void set_i2c_pins(uint8_t sda, uint8_t scl);
|
void set_i2c_pins(uint8_t sda, uint8_t scl);
|
||||||
|
#ifdef USE_I2C
|
||||||
|
void set_i2c_id(i2c::InternalI2CBus *i2c_bus);
|
||||||
|
#endif // USE_I2C
|
||||||
void set_reset_pin(uint8_t pin);
|
void set_reset_pin(uint8_t pin);
|
||||||
void set_power_down_pin(uint8_t pin);
|
void set_power_down_pin(uint8_t pin);
|
||||||
/* -- image */
|
/* -- image */
|
||||||
@@ -210,6 +217,9 @@ class ESP32Camera : public EntityBase, public Component {
|
|||||||
|
|
||||||
uint32_t last_idle_request_{0};
|
uint32_t last_idle_request_{0};
|
||||||
uint32_t last_update_{0};
|
uint32_t last_update_{0};
|
||||||
|
#ifdef USE_I2C
|
||||||
|
i2c::InternalI2CBus *i2c_bus_{nullptr};
|
||||||
|
#endif // USE_I2C
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
@@ -3,7 +3,7 @@ import esphome.config_validation as cv
|
|||||||
from esphome.const import CONF_ID, CONF_MODE, CONF_PORT
|
from esphome.const import CONF_ID, CONF_MODE, CONF_PORT
|
||||||
|
|
||||||
CODEOWNERS = ["@ayufan"]
|
CODEOWNERS = ["@ayufan"]
|
||||||
DEPENDENCIES = ["esp32_camera"]
|
DEPENDENCIES = ["esp32_camera", "network"]
|
||||||
MULTI_CONF = True
|
MULTI_CONF = True
|
||||||
|
|
||||||
esp32_camera_web_server_ns = cg.esphome_ns.namespace("esp32_camera_web_server")
|
esp32_camera_web_server_ns = cg.esphome_ns.namespace("esp32_camera_web_server")
|
||||||
|
@@ -1,25 +0,0 @@
|
|||||||
#ifdef USE_ESP32
|
|
||||||
#include "esp32_hall.h"
|
|
||||||
#include "esphome/core/log.h"
|
|
||||||
#include "esphome/core/hal.h"
|
|
||||||
#include <driver/adc.h>
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace esp32_hall {
|
|
||||||
|
|
||||||
static const char *const TAG = "esp32_hall";
|
|
||||||
|
|
||||||
void ESP32HallSensor::update() {
|
|
||||||
adc1_config_width(ADC_WIDTH_BIT_12);
|
|
||||||
int value_int = hall_sensor_read();
|
|
||||||
float value = (value_int / 4095.0f) * 10000.0f;
|
|
||||||
ESP_LOGD(TAG, "'%s': Got reading %.0f µT", this->name_.c_str(), value);
|
|
||||||
this->publish_state(value);
|
|
||||||
}
|
|
||||||
std::string ESP32HallSensor::unique_id() { return get_mac_address() + "-hall"; }
|
|
||||||
void ESP32HallSensor::dump_config() { LOG_SENSOR("", "ESP32 Hall Sensor", this); }
|
|
||||||
|
|
||||||
} // namespace esp32_hall
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
||||||
#endif
|
|
@@ -1,23 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include "esphome/components/sensor/sensor.h"
|
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
|
||||||
|
|
||||||
namespace esphome {
|
|
||||||
namespace esp32_hall {
|
|
||||||
|
|
||||||
class ESP32HallSensor : public sensor::Sensor, public PollingComponent {
|
|
||||||
public:
|
|
||||||
void dump_config() override;
|
|
||||||
|
|
||||||
void update() override;
|
|
||||||
|
|
||||||
std::string unique_id() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace esp32_hall
|
|
||||||
} // namespace esphome
|
|
||||||
|
|
||||||
#endif
|
|
@@ -1,24 +0,0 @@
|
|||||||
import esphome.codegen as cg
|
|
||||||
from esphome.components import sensor
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.const import ICON_MAGNET, STATE_CLASS_MEASUREMENT, UNIT_MICROTESLA
|
|
||||||
|
|
||||||
DEPENDENCIES = ["esp32"]
|
|
||||||
|
|
||||||
esp32_hall_ns = cg.esphome_ns.namespace("esp32_hall")
|
|
||||||
ESP32HallSensor = esp32_hall_ns.class_(
|
|
||||||
"ESP32HallSensor", sensor.Sensor, cg.PollingComponent
|
|
||||||
)
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = sensor.sensor_schema(
|
|
||||||
ESP32HallSensor,
|
|
||||||
unit_of_measurement=UNIT_MICROTESLA,
|
|
||||||
icon=ICON_MAGNET,
|
|
||||||
accuracy_decimals=1,
|
|
||||||
state_class=STATE_CLASS_MEASUREMENT,
|
|
||||||
).extend(cv.polling_component_schema("60s"))
|
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
|
||||||
var = await sensor.new_sensor(config)
|
|
||||||
await cg.register_component(var, config)
|
|
@@ -168,6 +168,8 @@ void ESP32ImprovComponent::loop() {
|
|||||||
case improv::STATE_PROVISIONED: {
|
case improv::STATE_PROVISIONED: {
|
||||||
this->incoming_data_.clear();
|
this->incoming_data_.clear();
|
||||||
this->set_status_indicator_state_(false);
|
this->set_status_indicator_state_(false);
|
||||||
|
// Provisioning complete, no further loop execution needed
|
||||||
|
this->disable_loop();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -254,6 +256,7 @@ void ESP32ImprovComponent::start() {
|
|||||||
|
|
||||||
ESP_LOGD(TAG, "Setting Improv to start");
|
ESP_LOGD(TAG, "Setting Improv to start");
|
||||||
this->should_start_ = true;
|
this->should_start_ = true;
|
||||||
|
this->enable_loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESP32ImprovComponent::stop() {
|
void ESP32ImprovComponent::stop() {
|
||||||
|
@@ -1,48 +1,8 @@
|
|||||||
import esphome.codegen as cg
|
|
||||||
from esphome.components import esp32
|
from esphome.components import esp32
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import KEY_CORE, KEY_FRAMEWORK_VERSION
|
|
||||||
from esphome.core import CORE
|
|
||||||
|
|
||||||
CODEOWNERS = ["@jesserockz"]
|
CODEOWNERS = ["@jesserockz"]
|
||||||
|
|
||||||
RMT_TX_CHANNELS = {
|
|
||||||
esp32.const.VARIANT_ESP32: [0, 1, 2, 3, 4, 5, 6, 7],
|
|
||||||
esp32.const.VARIANT_ESP32S2: [0, 1, 2, 3],
|
|
||||||
esp32.const.VARIANT_ESP32S3: [0, 1, 2, 3],
|
|
||||||
esp32.const.VARIANT_ESP32C3: [0, 1],
|
|
||||||
esp32.const.VARIANT_ESP32C6: [0, 1],
|
|
||||||
esp32.const.VARIANT_ESP32H2: [0, 1],
|
|
||||||
}
|
|
||||||
|
|
||||||
RMT_RX_CHANNELS = {
|
|
||||||
esp32.const.VARIANT_ESP32: [0, 1, 2, 3, 4, 5, 6, 7],
|
|
||||||
esp32.const.VARIANT_ESP32S2: [0, 1, 2, 3],
|
|
||||||
esp32.const.VARIANT_ESP32S3: [4, 5, 6, 7],
|
|
||||||
esp32.const.VARIANT_ESP32C3: [2, 3],
|
|
||||||
esp32.const.VARIANT_ESP32C6: [2, 3],
|
|
||||||
esp32.const.VARIANT_ESP32H2: [2, 3],
|
|
||||||
}
|
|
||||||
|
|
||||||
rmt_channel_t = cg.global_ns.enum("rmt_channel_t")
|
|
||||||
RMT_CHANNEL_ENUMS = {
|
|
||||||
0: rmt_channel_t.RMT_CHANNEL_0,
|
|
||||||
1: rmt_channel_t.RMT_CHANNEL_1,
|
|
||||||
2: rmt_channel_t.RMT_CHANNEL_2,
|
|
||||||
3: rmt_channel_t.RMT_CHANNEL_3,
|
|
||||||
4: rmt_channel_t.RMT_CHANNEL_4,
|
|
||||||
5: rmt_channel_t.RMT_CHANNEL_5,
|
|
||||||
6: rmt_channel_t.RMT_CHANNEL_6,
|
|
||||||
7: rmt_channel_t.RMT_CHANNEL_7,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def use_new_rmt_driver():
|
|
||||||
framework_version = CORE.data[KEY_CORE][KEY_FRAMEWORK_VERSION]
|
|
||||||
if CORE.using_esp_idf and framework_version >= cv.Version(5, 0, 0):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def validate_clock_resolution():
|
def validate_clock_resolution():
|
||||||
def _validator(value):
|
def _validator(value):
|
||||||
@@ -60,21 +20,3 @@ def validate_clock_resolution():
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
return _validator
|
return _validator
|
||||||
|
|
||||||
|
|
||||||
def validate_rmt_channel(*, tx: bool):
|
|
||||||
rmt_channels = RMT_TX_CHANNELS if tx else RMT_RX_CHANNELS
|
|
||||||
|
|
||||||
def _validator(value):
|
|
||||||
cv.only_on_esp32(value)
|
|
||||||
value = cv.int_(value)
|
|
||||||
variant = esp32.get_esp32_variant()
|
|
||||||
if variant not in rmt_channels:
|
|
||||||
raise cv.Invalid(f"ESP32 variant {variant} does not support RMT.")
|
|
||||||
if value not in rmt_channels[variant]:
|
|
||||||
raise cv.Invalid(
|
|
||||||
f"RMT channel {value} does not support {'transmitting' if tx else 'receiving'} for ESP32 variant {variant}."
|
|
||||||
)
|
|
||||||
return cv.enum(RMT_CHANNEL_ENUMS)(value)
|
|
||||||
|
|
||||||
return _validator
|
|
||||||
|
@@ -42,7 +42,6 @@ void ESP32RMTLEDStripLightOutput::setup() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
RAMAllocator<rmt_symbol_word_t> rmt_allocator(this->use_psram_ ? 0 : RAMAllocator<rmt_symbol_word_t>::ALLOC_INTERNAL);
|
RAMAllocator<rmt_symbol_word_t> rmt_allocator(this->use_psram_ ? 0 : RAMAllocator<rmt_symbol_word_t>::ALLOC_INTERNAL);
|
||||||
|
|
||||||
// 8 bits per byte, 1 rmt_symbol_word_t per bit + 1 rmt_symbol_word_t for reset
|
// 8 bits per byte, 1 rmt_symbol_word_t per bit + 1 rmt_symbol_word_t for reset
|
||||||
@@ -79,36 +78,6 @@ void ESP32RMTLEDStripLightOutput::setup() {
|
|||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
RAMAllocator<rmt_item32_t> rmt_allocator(this->use_psram_ ? 0 : RAMAllocator<rmt_item32_t>::ALLOC_INTERNAL);
|
|
||||||
|
|
||||||
// 8 bits per byte, 1 rmt_item32_t per bit + 1 rmt_item32_t for reset
|
|
||||||
this->rmt_buf_ = rmt_allocator.allocate(buffer_size * 8 + 1);
|
|
||||||
|
|
||||||
rmt_config_t config;
|
|
||||||
memset(&config, 0, sizeof(config));
|
|
||||||
config.channel = this->channel_;
|
|
||||||
config.rmt_mode = RMT_MODE_TX;
|
|
||||||
config.gpio_num = gpio_num_t(this->pin_);
|
|
||||||
config.mem_block_num = 1;
|
|
||||||
config.clk_div = RMT_CLK_DIV;
|
|
||||||
config.tx_config.loop_en = false;
|
|
||||||
config.tx_config.carrier_level = RMT_CARRIER_LEVEL_LOW;
|
|
||||||
config.tx_config.carrier_en = false;
|
|
||||||
config.tx_config.idle_level = RMT_IDLE_LEVEL_LOW;
|
|
||||||
config.tx_config.idle_output_en = true;
|
|
||||||
|
|
||||||
if (rmt_config(&config) != ESP_OK) {
|
|
||||||
ESP_LOGE(TAG, "Cannot initialize RMT!");
|
|
||||||
this->mark_failed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (rmt_driver_install(config.channel, 0, 0) != ESP_OK) {
|
|
||||||
ESP_LOGE(TAG, "Cannot install RMT driver!");
|
|
||||||
this->mark_failed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high,
|
void ESP32RMTLEDStripLightOutput::set_led_params(uint32_t bit0_high, uint32_t bit0_low, uint32_t bit1_high,
|
||||||
@@ -145,11 +114,7 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) {
|
|||||||
|
|
||||||
ESP_LOGVV(TAG, "Writing RGB values to bus");
|
ESP_LOGVV(TAG, "Writing RGB values to bus");
|
||||||
|
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
esp_err_t error = rmt_tx_wait_all_done(this->channel_, 1000);
|
esp_err_t error = rmt_tx_wait_all_done(this->channel_, 1000);
|
||||||
#else
|
|
||||||
esp_err_t error = rmt_wait_tx_done(this->channel_, pdMS_TO_TICKS(1000));
|
|
||||||
#endif
|
|
||||||
if (error != ESP_OK) {
|
if (error != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "RMT TX timeout");
|
ESP_LOGE(TAG, "RMT TX timeout");
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
@@ -162,11 +127,7 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) {
|
|||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
uint8_t *psrc = this->buf_;
|
uint8_t *psrc = this->buf_;
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
rmt_symbol_word_t *pdest = this->rmt_buf_;
|
rmt_symbol_word_t *pdest = this->rmt_buf_;
|
||||||
#else
|
|
||||||
rmt_item32_t *pdest = this->rmt_buf_;
|
|
||||||
#endif
|
|
||||||
while (size < buffer_size) {
|
while (size < buffer_size) {
|
||||||
uint8_t b = *psrc;
|
uint8_t b = *psrc;
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
@@ -184,15 +145,11 @@ void ESP32RMTLEDStripLightOutput::write_state(light::LightState *state) {
|
|||||||
len++;
|
len++;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
rmt_transmit_config_t config;
|
rmt_transmit_config_t config;
|
||||||
memset(&config, 0, sizeof(config));
|
memset(&config, 0, sizeof(config));
|
||||||
config.loop_count = 0;
|
config.loop_count = 0;
|
||||||
config.flags.eot_level = 0;
|
config.flags.eot_level = 0;
|
||||||
error = rmt_transmit(this->channel_, this->encoder_, this->rmt_buf_, len * sizeof(rmt_symbol_word_t), &config);
|
error = rmt_transmit(this->channel_, this->encoder_, this->rmt_buf_, len * sizeof(rmt_symbol_word_t), &config);
|
||||||
#else
|
|
||||||
error = rmt_write_items(this->channel_, this->rmt_buf_, len, false);
|
|
||||||
#endif
|
|
||||||
if (error != ESP_OK) {
|
if (error != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "RMT TX error");
|
ESP_LOGE(TAG, "RMT TX error");
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
@@ -251,11 +208,7 @@ void ESP32RMTLEDStripLightOutput::dump_config() {
|
|||||||
"ESP32 RMT LED Strip:\n"
|
"ESP32 RMT LED Strip:\n"
|
||||||
" Pin: %u",
|
" Pin: %u",
|
||||||
this->pin_);
|
this->pin_);
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
ESP_LOGCONFIG(TAG, " RMT Symbols: %" PRIu32, this->rmt_symbols_);
|
ESP_LOGCONFIG(TAG, " RMT Symbols: %" PRIu32, this->rmt_symbols_);
|
||||||
#else
|
|
||||||
ESP_LOGCONFIG(TAG, " Channel: %u", this->channel_);
|
|
||||||
#endif
|
|
||||||
const char *rgb_order;
|
const char *rgb_order;
|
||||||
switch (this->rgb_order_) {
|
switch (this->rgb_order_) {
|
||||||
case ORDER_RGB:
|
case ORDER_RGB:
|
||||||
|
@@ -11,12 +11,7 @@
|
|||||||
#include <driver/gpio.h>
|
#include <driver/gpio.h>
|
||||||
#include <esp_err.h>
|
#include <esp_err.h>
|
||||||
#include <esp_idf_version.h>
|
#include <esp_idf_version.h>
|
||||||
|
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
#include <driver/rmt_tx.h>
|
#include <driver/rmt_tx.h>
|
||||||
#else
|
|
||||||
#include <driver/rmt.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace esp32_rmt_led_strip {
|
namespace esp32_rmt_led_strip {
|
||||||
@@ -61,11 +56,7 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight {
|
|||||||
uint32_t reset_time_high, uint32_t reset_time_low);
|
uint32_t reset_time_high, uint32_t reset_time_low);
|
||||||
|
|
||||||
void set_rgb_order(RGBOrder rgb_order) { this->rgb_order_ = rgb_order; }
|
void set_rgb_order(RGBOrder rgb_order) { this->rgb_order_ = rgb_order; }
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
void set_rmt_symbols(uint32_t rmt_symbols) { this->rmt_symbols_ = rmt_symbols; }
|
void set_rmt_symbols(uint32_t rmt_symbols) { this->rmt_symbols_ = rmt_symbols; }
|
||||||
#else
|
|
||||||
void set_rmt_channel(rmt_channel_t channel) { this->channel_ = channel; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void clear_effect_data() override {
|
void clear_effect_data() override {
|
||||||
for (int i = 0; i < this->size(); i++)
|
for (int i = 0; i < this->size(); i++)
|
||||||
@@ -81,17 +72,11 @@ class ESP32RMTLEDStripLightOutput : public light::AddressableLight {
|
|||||||
|
|
||||||
uint8_t *buf_{nullptr};
|
uint8_t *buf_{nullptr};
|
||||||
uint8_t *effect_data_{nullptr};
|
uint8_t *effect_data_{nullptr};
|
||||||
#if ESP_IDF_VERSION_MAJOR >= 5
|
|
||||||
rmt_channel_handle_t channel_{nullptr};
|
rmt_channel_handle_t channel_{nullptr};
|
||||||
rmt_encoder_handle_t encoder_{nullptr};
|
rmt_encoder_handle_t encoder_{nullptr};
|
||||||
rmt_symbol_word_t *rmt_buf_{nullptr};
|
rmt_symbol_word_t *rmt_buf_{nullptr};
|
||||||
rmt_symbol_word_t bit0_, bit1_, reset_;
|
rmt_symbol_word_t bit0_, bit1_, reset_;
|
||||||
uint32_t rmt_symbols_{48};
|
uint32_t rmt_symbols_{48};
|
||||||
#else
|
|
||||||
rmt_item32_t *rmt_buf_{nullptr};
|
|
||||||
rmt_item32_t bit0_, bit1_, reset_;
|
|
||||||
rmt_channel_t channel_{RMT_CHANNEL_0};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
uint8_t pin_;
|
uint8_t pin_;
|
||||||
uint16_t num_leds_;
|
uint16_t num_leds_;
|
||||||
|
@@ -3,7 +3,7 @@ import logging
|
|||||||
|
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import esp32, esp32_rmt, light
|
from esphome.components import esp32, light
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_CHIPSET,
|
CONF_CHIPSET,
|
||||||
@@ -13,11 +13,9 @@ from esphome.const import (
|
|||||||
CONF_OUTPUT_ID,
|
CONF_OUTPUT_ID,
|
||||||
CONF_PIN,
|
CONF_PIN,
|
||||||
CONF_RGB_ORDER,
|
CONF_RGB_ORDER,
|
||||||
CONF_RMT_CHANNEL,
|
|
||||||
CONF_RMT_SYMBOLS,
|
CONF_RMT_SYMBOLS,
|
||||||
CONF_USE_DMA,
|
CONF_USE_DMA,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -69,53 +67,6 @@ CONF_RESET_HIGH = "reset_high"
|
|||||||
CONF_RESET_LOW = "reset_low"
|
CONF_RESET_LOW = "reset_low"
|
||||||
|
|
||||||
|
|
||||||
class OptionalForIDF5(cv.SplitDefault):
|
|
||||||
@property
|
|
||||||
def default(self):
|
|
||||||
if not esp32_rmt.use_new_rmt_driver():
|
|
||||||
return cv.UNDEFINED
|
|
||||||
return super().default
|
|
||||||
|
|
||||||
@default.setter
|
|
||||||
def default(self, value):
|
|
||||||
# Ignore default set from vol.Optional
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def only_with_new_rmt_driver(obj):
|
|
||||||
if not esp32_rmt.use_new_rmt_driver():
|
|
||||||
raise cv.Invalid(
|
|
||||||
"This feature is only available for the IDF framework version 5."
|
|
||||||
)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
def not_with_new_rmt_driver(obj):
|
|
||||||
if esp32_rmt.use_new_rmt_driver():
|
|
||||||
raise cv.Invalid(
|
|
||||||
"This feature is not available for the IDF framework version 5."
|
|
||||||
)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
def final_validation(config):
|
|
||||||
if not esp32_rmt.use_new_rmt_driver():
|
|
||||||
if CONF_RMT_CHANNEL not in config:
|
|
||||||
if CORE.using_esp_idf:
|
|
||||||
raise cv.Invalid(
|
|
||||||
"rmt_channel is a required option for IDF version < 5."
|
|
||||||
)
|
|
||||||
raise cv.Invalid(
|
|
||||||
"rmt_channel is a required option for the Arduino framework."
|
|
||||||
)
|
|
||||||
_LOGGER.warning(
|
|
||||||
"RMT_LED_STRIP support for IDF version < 5 is deprecated and will be removed soon."
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
FINAL_VALIDATE_SCHEMA = final_validation
|
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
light.ADDRESSABLE_LIGHT_SCHEMA.extend(
|
light.ADDRESSABLE_LIGHT_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
@@ -123,20 +74,17 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_number,
|
cv.Required(CONF_PIN): pins.internal_gpio_output_pin_number,
|
||||||
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
|
cv.Required(CONF_NUM_LEDS): cv.positive_not_null_int,
|
||||||
cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True),
|
cv.Required(CONF_RGB_ORDER): cv.enum(RGB_ORDERS, upper=True),
|
||||||
cv.Optional(CONF_RMT_CHANNEL): cv.All(
|
cv.SplitDefault(
|
||||||
not_with_new_rmt_driver, esp32_rmt.validate_rmt_channel(tx=True)
|
|
||||||
),
|
|
||||||
OptionalForIDF5(
|
|
||||||
CONF_RMT_SYMBOLS,
|
CONF_RMT_SYMBOLS,
|
||||||
esp32_idf=192,
|
esp32=192,
|
||||||
esp32_s2_idf=192,
|
esp32_s2=192,
|
||||||
esp32_s3_idf=192,
|
esp32_s3=192,
|
||||||
esp32_p4_idf=192,
|
esp32_p4=192,
|
||||||
esp32_c3_idf=96,
|
esp32_c3=96,
|
||||||
esp32_c5_idf=96,
|
esp32_c5=96,
|
||||||
esp32_c6_idf=96,
|
esp32_c6=96,
|
||||||
esp32_h2_idf=96,
|
esp32_h2=96,
|
||||||
): cv.All(only_with_new_rmt_driver, cv.int_range(min=2)),
|
): cv.int_range(min=2),
|
||||||
cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
|
cv.Optional(CONF_MAX_REFRESH_RATE): cv.positive_time_period_microseconds,
|
||||||
cv.Optional(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True),
|
cv.Optional(CONF_CHIPSET): cv.one_of(*CHIPSETS, upper=True),
|
||||||
cv.Optional(CONF_IS_RGBW, default=False): cv.boolean,
|
cv.Optional(CONF_IS_RGBW, default=False): cv.boolean,
|
||||||
@@ -145,7 +93,6 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
esp32.only_on_variant(
|
esp32.only_on_variant(
|
||||||
supported=[esp32.const.VARIANT_ESP32S3, esp32.const.VARIANT_ESP32P4]
|
supported=[esp32.const.VARIANT_ESP32S3, esp32.const.VARIANT_ESP32P4]
|
||||||
),
|
),
|
||||||
cv.only_with_esp_idf,
|
|
||||||
cv.boolean,
|
cv.boolean,
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_USE_PSRAM, default=True): cv.boolean,
|
cv.Optional(CONF_USE_PSRAM, default=True): cv.boolean,
|
||||||
@@ -218,15 +165,6 @@ async def to_code(config):
|
|||||||
cg.add(var.set_is_rgbw(config[CONF_IS_RGBW]))
|
cg.add(var.set_is_rgbw(config[CONF_IS_RGBW]))
|
||||||
cg.add(var.set_is_wrgb(config[CONF_IS_WRGB]))
|
cg.add(var.set_is_wrgb(config[CONF_IS_WRGB]))
|
||||||
cg.add(var.set_use_psram(config[CONF_USE_PSRAM]))
|
cg.add(var.set_use_psram(config[CONF_USE_PSRAM]))
|
||||||
|
cg.add(var.set_rmt_symbols(config[CONF_RMT_SYMBOLS]))
|
||||||
if esp32_rmt.use_new_rmt_driver():
|
if CONF_USE_DMA in config:
|
||||||
cg.add(var.set_rmt_symbols(config[CONF_RMT_SYMBOLS]))
|
cg.add(var.set_use_dma(config[CONF_USE_DMA]))
|
||||||
if CONF_USE_DMA in config:
|
|
||||||
cg.add(var.set_use_dma(config[CONF_USE_DMA]))
|
|
||||||
else:
|
|
||||||
rmt_channel_t = cg.global_ns.enum("rmt_channel_t")
|
|
||||||
cg.add(
|
|
||||||
var.set_rmt_channel(
|
|
||||||
getattr(rmt_channel_t, f"RMT_CHANNEL_{config[CONF_RMT_CHANNEL]}")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
@@ -183,6 +183,7 @@ async def to_code(config):
|
|||||||
|
|
||||||
cg.add_platformio_option("board", config[CONF_BOARD])
|
cg.add_platformio_option("board", config[CONF_BOARD])
|
||||||
cg.add_build_flag("-DUSE_ESP8266")
|
cg.add_build_flag("-DUSE_ESP8266")
|
||||||
|
cg.set_cpp_standard("gnu++17")
|
||||||
cg.add_define("ESPHOME_BOARD", config[CONF_BOARD])
|
cg.add_define("ESPHOME_BOARD", config[CONF_BOARD])
|
||||||
cg.add_define("ESPHOME_VARIANT", "ESP8266")
|
cg.add_define("ESPHOME_VARIANT", "ESP8266")
|
||||||
|
|
||||||
|
@@ -26,19 +26,19 @@ void ESPHomeOTAComponent::setup() {
|
|||||||
ota::register_ota_platform(this);
|
ota::register_ota_platform(this);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
server_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0); // monitored for incoming connections
|
this->server_ = socket::socket_ip_loop_monitored(SOCK_STREAM, 0); // monitored for incoming connections
|
||||||
if (server_ == nullptr) {
|
if (this->server_ == nullptr) {
|
||||||
ESP_LOGW(TAG, "Could not create socket");
|
ESP_LOGW(TAG, "Could not create socket");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int enable = 1;
|
int enable = 1;
|
||||||
int err = server_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
|
int err = this->server_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
ESP_LOGW(TAG, "Socket unable to set reuseaddr: errno %d", err);
|
ESP_LOGW(TAG, "Socket unable to set reuseaddr: errno %d", err);
|
||||||
// we can still continue
|
// we can still continue
|
||||||
}
|
}
|
||||||
err = server_->setblocking(false);
|
err = this->server_->setblocking(false);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
ESP_LOGW(TAG, "Socket unable to set nonblocking mode: errno %d", err);
|
ESP_LOGW(TAG, "Socket unable to set nonblocking mode: errno %d", err);
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
@@ -54,14 +54,14 @@ void ESPHomeOTAComponent::setup() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = server_->bind((struct sockaddr *) &server, sizeof(server));
|
err = this->server_->bind((struct sockaddr *) &server, sizeof(server));
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno);
|
ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno);
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = server_->listen(4);
|
err = this->server_->listen(4);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
ESP_LOGW(TAG, "Socket unable to listen: errno %d", errno);
|
ESP_LOGW(TAG, "Socket unable to listen: errno %d", errno);
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
@@ -82,7 +82,14 @@ void ESPHomeOTAComponent::dump_config() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESPHomeOTAComponent::loop() { this->handle_(); }
|
void ESPHomeOTAComponent::loop() {
|
||||||
|
// Skip handle_() call if no client connected and no incoming connections
|
||||||
|
// This optimization reduces idle loop overhead when OTA is not active
|
||||||
|
// Note: No need to check server_ for null as the component is marked failed in setup() if server_ creation fails
|
||||||
|
if (this->client_ != nullptr || this->server_->ready()) {
|
||||||
|
this->handle_();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static const uint8_t FEATURE_SUPPORTS_COMPRESSION = 0x01;
|
static const uint8_t FEATURE_SUPPORTS_COMPRESSION = 0x01;
|
||||||
|
|
||||||
@@ -101,23 +108,21 @@ void ESPHomeOTAComponent::handle_() {
|
|||||||
size_t size_acknowledged = 0;
|
size_t size_acknowledged = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (client_ == nullptr) {
|
if (this->client_ == nullptr) {
|
||||||
// Check if the server socket is ready before accepting
|
// We already checked server_->ready() in loop(), so we can accept directly
|
||||||
if (this->server_->ready()) {
|
struct sockaddr_storage source_addr;
|
||||||
struct sockaddr_storage source_addr;
|
socklen_t addr_len = sizeof(source_addr);
|
||||||
socklen_t addr_len = sizeof(source_addr);
|
this->client_ = this->server_->accept((struct sockaddr *) &source_addr, &addr_len);
|
||||||
client_ = server_->accept((struct sockaddr *) &source_addr, &addr_len);
|
if (this->client_ == nullptr)
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
if (client_ == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int enable = 1;
|
int enable = 1;
|
||||||
int err = client_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
|
int err = this->client_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
ESP_LOGW(TAG, "Socket could not enable TCP nodelay, errno %d", errno);
|
ESP_LOGW(TAG, "Socket could not enable TCP nodelay, errno %d", errno);
|
||||||
client_->close();
|
this->client_->close();
|
||||||
client_ = nullptr;
|
this->client_ = nullptr;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -106,7 +106,7 @@ void EthernetComponent::setup() {
|
|||||||
.post_cb = nullptr,
|
.post_cb = nullptr,
|
||||||
};
|
};
|
||||||
|
|
||||||
#if USE_ESP_IDF && (ESP_IDF_VERSION_MAJOR >= 5)
|
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||||
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(host, &devcfg);
|
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(host, &devcfg);
|
||||||
#else
|
#else
|
||||||
spi_device_handle_t spi_handle = nullptr;
|
spi_device_handle_t spi_handle = nullptr;
|
||||||
@@ -274,6 +274,9 @@ void EthernetComponent::loop() {
|
|||||||
ESP_LOGW(TAG, "Connection lost; reconnecting");
|
ESP_LOGW(TAG, "Connection lost; reconnecting");
|
||||||
this->state_ = EthernetComponentState::CONNECTING;
|
this->state_ = EthernetComponentState::CONNECTING;
|
||||||
this->start_connect_();
|
this->start_connect_();
|
||||||
|
} else {
|
||||||
|
// When connected and stable, disable the loop to save CPU cycles
|
||||||
|
this->disable_loop();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -397,11 +400,13 @@ void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base
|
|||||||
case ETHERNET_EVENT_START:
|
case ETHERNET_EVENT_START:
|
||||||
event_name = "ETH started";
|
event_name = "ETH started";
|
||||||
global_eth_component->started_ = true;
|
global_eth_component->started_ = true;
|
||||||
|
global_eth_component->enable_loop_soon_any_context();
|
||||||
break;
|
break;
|
||||||
case ETHERNET_EVENT_STOP:
|
case ETHERNET_EVENT_STOP:
|
||||||
event_name = "ETH stopped";
|
event_name = "ETH stopped";
|
||||||
global_eth_component->started_ = false;
|
global_eth_component->started_ = false;
|
||||||
global_eth_component->connected_ = false;
|
global_eth_component->connected_ = false;
|
||||||
|
global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes
|
||||||
break;
|
break;
|
||||||
case ETHERNET_EVENT_CONNECTED:
|
case ETHERNET_EVENT_CONNECTED:
|
||||||
event_name = "ETH connected";
|
event_name = "ETH connected";
|
||||||
@@ -409,6 +414,7 @@ void EthernetComponent::eth_event_handler(void *arg, esp_event_base_t event_base
|
|||||||
case ETHERNET_EVENT_DISCONNECTED:
|
case ETHERNET_EVENT_DISCONNECTED:
|
||||||
event_name = "ETH disconnected";
|
event_name = "ETH disconnected";
|
||||||
global_eth_component->connected_ = false;
|
global_eth_component->connected_ = false;
|
||||||
|
global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
@@ -425,8 +431,10 @@ void EthernetComponent::got_ip_event_handler(void *arg, esp_event_base_t event_b
|
|||||||
global_eth_component->got_ipv4_address_ = true;
|
global_eth_component->got_ipv4_address_ = true;
|
||||||
#if USE_NETWORK_IPV6 && (USE_NETWORK_MIN_IPV6_ADDR_COUNT > 0)
|
#if USE_NETWORK_IPV6 && (USE_NETWORK_MIN_IPV6_ADDR_COUNT > 0)
|
||||||
global_eth_component->connected_ = global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT;
|
global_eth_component->connected_ = global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT;
|
||||||
|
global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes
|
||||||
#else
|
#else
|
||||||
global_eth_component->connected_ = true;
|
global_eth_component->connected_ = true;
|
||||||
|
global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes
|
||||||
#endif /* USE_NETWORK_IPV6 */
|
#endif /* USE_NETWORK_IPV6 */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -439,8 +447,10 @@ void EthernetComponent::got_ip6_event_handler(void *arg, esp_event_base_t event_
|
|||||||
#if (USE_NETWORK_MIN_IPV6_ADDR_COUNT > 0)
|
#if (USE_NETWORK_MIN_IPV6_ADDR_COUNT > 0)
|
||||||
global_eth_component->connected_ =
|
global_eth_component->connected_ =
|
||||||
global_eth_component->got_ipv4_address_ && (global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT);
|
global_eth_component->got_ipv4_address_ && (global_eth_component->ipv6_count_ >= USE_NETWORK_MIN_IPV6_ADDR_COUNT);
|
||||||
|
global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes
|
||||||
#else
|
#else
|
||||||
global_eth_component->connected_ = global_eth_component->got_ipv4_address_;
|
global_eth_component->connected_ = global_eth_component->got_ipv4_address_;
|
||||||
|
global_eth_component->enable_loop_soon_any_context(); // Enable loop when connection state changes
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#endif /* USE_NETWORK_IPV6 */
|
#endif /* USE_NETWORK_IPV6 */
|
||||||
@@ -620,6 +630,7 @@ bool EthernetComponent::powerdown() {
|
|||||||
}
|
}
|
||||||
this->connected_ = false;
|
this->connected_ = false;
|
||||||
this->started_ = false;
|
this->started_ = false;
|
||||||
|
// No need to enable_loop() here as this is only called during shutdown/reboot
|
||||||
if (this->phy_->pwrctl(this->phy_, false) != ESP_OK) {
|
if (this->phy_->pwrctl(this->phy_, false) != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Error powering down ethernet PHY");
|
ESP_LOGE(TAG, "Error powering down ethernet PHY");
|
||||||
return false;
|
return false;
|
||||||
|
@@ -41,39 +41,48 @@ void FanCall::perform() {
|
|||||||
void FanCall::validate_() {
|
void FanCall::validate_() {
|
||||||
auto traits = this->parent_.get_traits();
|
auto traits = this->parent_.get_traits();
|
||||||
|
|
||||||
if (this->speed_.has_value())
|
if (this->speed_.has_value()) {
|
||||||
this->speed_ = clamp(*this->speed_, 1, traits.supported_speed_count());
|
this->speed_ = clamp(*this->speed_, 1, traits.supported_speed_count());
|
||||||
|
|
||||||
if (this->binary_state_.has_value() && *this->binary_state_) {
|
// https://developers.home-assistant.io/docs/core/entity/fan/#preset-modes
|
||||||
// when turning on, if neither current nor new speed available, set speed to 100%
|
// "Manually setting a speed must disable any set preset mode"
|
||||||
if (traits.supports_speed() && !this->parent_.state && this->parent_.speed == 0 && !this->speed_.has_value()) {
|
this->preset_mode_.clear();
|
||||||
this->speed_ = traits.supported_speed_count();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->oscillating_.has_value() && !traits.supports_oscillation()) {
|
|
||||||
ESP_LOGW(TAG, "'%s' - This fan does not support oscillation!", this->parent_.get_name().c_str());
|
|
||||||
this->oscillating_.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->speed_.has_value() && !traits.supports_speed()) {
|
|
||||||
ESP_LOGW(TAG, "'%s' - This fan does not support speeds!", this->parent_.get_name().c_str());
|
|
||||||
this->speed_.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->direction_.has_value() && !traits.supports_direction()) {
|
|
||||||
ESP_LOGW(TAG, "'%s' - This fan does not support directions!", this->parent_.get_name().c_str());
|
|
||||||
this->direction_.reset();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this->preset_mode_.empty()) {
|
if (!this->preset_mode_.empty()) {
|
||||||
const auto &preset_modes = traits.supported_preset_modes();
|
const auto &preset_modes = traits.supported_preset_modes();
|
||||||
if (preset_modes.find(this->preset_mode_) == preset_modes.end()) {
|
if (preset_modes.find(this->preset_mode_) == preset_modes.end()) {
|
||||||
ESP_LOGW(TAG, "'%s' - This fan does not support preset mode '%s'!", this->parent_.get_name().c_str(),
|
ESP_LOGW(TAG, "%s: Preset mode '%s' not supported", this->parent_.get_name().c_str(), this->preset_mode_.c_str());
|
||||||
this->preset_mode_.c_str());
|
|
||||||
this->preset_mode_.clear();
|
this->preset_mode_.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// when turning on...
|
||||||
|
if (!this->parent_.state && this->binary_state_.has_value() &&
|
||||||
|
*this->binary_state_
|
||||||
|
// ..,and no preset mode will be active...
|
||||||
|
&& this->preset_mode_.empty() &&
|
||||||
|
this->parent_.preset_mode.empty()
|
||||||
|
// ...and neither current nor new speed is available...
|
||||||
|
&& traits.supports_speed() && this->parent_.speed == 0 && !this->speed_.has_value()) {
|
||||||
|
// ...set speed to 100%
|
||||||
|
this->speed_ = traits.supported_speed_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->oscillating_.has_value() && !traits.supports_oscillation()) {
|
||||||
|
ESP_LOGW(TAG, "%s: Oscillation not supported", this->parent_.get_name().c_str());
|
||||||
|
this->oscillating_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->speed_.has_value() && !traits.supports_speed()) {
|
||||||
|
ESP_LOGW(TAG, "%s: Speed control not supported", this->parent_.get_name().c_str());
|
||||||
|
this->speed_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->direction_.has_value() && !traits.supports_direction()) {
|
||||||
|
ESP_LOGW(TAG, "%s: Direction control not supported", this->parent_.get_name().c_str());
|
||||||
|
this->direction_.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FanCall FanRestoreState::to_call(Fan &fan) {
|
FanCall FanRestoreState::to_call(Fan &fan) {
|
||||||
|
@@ -67,10 +67,10 @@ class Font
|
|||||||
inline int get_height() { return this->height_; }
|
inline int get_height() { return this->height_; }
|
||||||
inline int get_bpp() { return this->bpp_; }
|
inline int get_bpp() { return this->bpp_; }
|
||||||
|
|
||||||
const std::vector<Glyph, ExternalRAMAllocator<Glyph>> &get_glyphs() const { return glyphs_; }
|
const std::vector<Glyph, RAMAllocator<Glyph>> &get_glyphs() const { return glyphs_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<Glyph, ExternalRAMAllocator<Glyph>> glyphs_;
|
std::vector<Glyph, RAMAllocator<Glyph>> glyphs_;
|
||||||
int baseline_;
|
int baseline_;
|
||||||
int height_;
|
int height_;
|
||||||
uint8_t bpp_; // bits per pixel
|
uint8_t bpp_; // bits per pixel
|
||||||
|
@@ -125,6 +125,6 @@ async def to_code(config):
|
|||||||
cg.add(var.set_max_temperature(config[CONF_MAX_TEMPERATURE]))
|
cg.add(var.set_max_temperature(config[CONF_MAX_TEMPERATURE]))
|
||||||
cg.add(var.set_min_temperature(config[CONF_MIN_TEMPERATURE]))
|
cg.add(var.set_min_temperature(config[CONF_MIN_TEMPERATURE]))
|
||||||
|
|
||||||
cg.add_library("tonia/HeatpumpIR", "1.0.32")
|
cg.add_library("tonia/HeatpumpIR", "1.0.35")
|
||||||
if CORE.is_libretiny:
|
if CORE.is_libretiny:
|
||||||
CORE.add_platformio_option("lib_ignore", "IRremoteESP8266")
|
CORE.add_platformio_option("lib_ignore", "IRremoteESP8266")
|
||||||
|
@@ -175,7 +175,7 @@ async def to_code(config):
|
|||||||
not config.get(CONF_VERIFY_SSL),
|
not config.get(CONF_VERIFY_SSL),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
cg.add_library("WiFiClientSecure", None)
|
cg.add_library("NetworkClientSecure", None)
|
||||||
cg.add_library("HTTPClient", None)
|
cg.add_library("HTTPClient", None)
|
||||||
if CORE.is_esp8266:
|
if CORE.is_esp8266:
|
||||||
cg.add_library("ESP8266HTTPClient", None)
|
cg.add_library("ESP8266HTTPClient", None)
|
||||||
|
@@ -239,7 +239,7 @@ template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> {
|
|||||||
|
|
||||||
std::string response_body;
|
std::string response_body;
|
||||||
if (this->capture_response_.value(x...)) {
|
if (this->capture_response_.value(x...)) {
|
||||||
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
RAMAllocator<uint8_t> allocator;
|
||||||
uint8_t *buf = allocator.allocate(max_length);
|
uint8_t *buf = allocator.allocate(max_length);
|
||||||
if (buf != nullptr) {
|
if (buf != nullptr) {
|
||||||
size_t read_index = 0;
|
size_t read_index = 0;
|
||||||
|
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#if defined(USE_ESP32) || defined(USE_RP2040)
|
#if defined(USE_ESP32) || defined(USE_RP2040)
|
||||||
#include <HTTPClient.h>
|
#include <HTTPClient.h>
|
||||||
|
#include <WiFiClient.h>
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP8266
|
#ifdef USE_ESP8266
|
||||||
#include <ESP8266HTTPClient.h>
|
#include <ESP8266HTTPClient.h>
|
||||||
|
@@ -54,7 +54,7 @@ void HttpRequestUpdate::update_task(void *params) {
|
|||||||
UPDATE_RETURN;
|
UPDATE_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
RAMAllocator<uint8_t> allocator;
|
||||||
uint8_t *data = allocator.allocate(container->content_length);
|
uint8_t *data = allocator.allocate(container->content_length);
|
||||||
if (data == nullptr) {
|
if (data == nullptr) {
|
||||||
std::string msg = str_sprintf("Failed to allocate %d bytes for manifest", container->content_length);
|
std::string msg = str_sprintf("Failed to allocate %d bytes for manifest", container->content_length);
|
||||||
|
@@ -22,8 +22,9 @@ import esphome.final_validate as fv
|
|||||||
CODEOWNERS = ["@esphome/core"]
|
CODEOWNERS = ["@esphome/core"]
|
||||||
i2c_ns = cg.esphome_ns.namespace("i2c")
|
i2c_ns = cg.esphome_ns.namespace("i2c")
|
||||||
I2CBus = i2c_ns.class_("I2CBus")
|
I2CBus = i2c_ns.class_("I2CBus")
|
||||||
ArduinoI2CBus = i2c_ns.class_("ArduinoI2CBus", I2CBus, cg.Component)
|
InternalI2CBus = i2c_ns.class_("InternalI2CBus", I2CBus)
|
||||||
IDFI2CBus = i2c_ns.class_("IDFI2CBus", I2CBus, cg.Component)
|
ArduinoI2CBus = i2c_ns.class_("ArduinoI2CBus", InternalI2CBus, cg.Component)
|
||||||
|
IDFI2CBus = i2c_ns.class_("IDFI2CBus", InternalI2CBus, cg.Component)
|
||||||
I2CDevice = i2c_ns.class_("I2CDevice")
|
I2CDevice = i2c_ns.class_("I2CDevice")
|
||||||
|
|
||||||
|
|
||||||
@@ -71,6 +72,7 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
@coroutine_with_priority(1.0)
|
@coroutine_with_priority(1.0)
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
cg.add_global(i2c_ns.using)
|
cg.add_global(i2c_ns.using)
|
||||||
|
cg.add_define("USE_I2C")
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <cstdint>
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -108,5 +108,12 @@ class I2CBus {
|
|||||||
bool scan_{false}; ///< Should we scan ? Can be set in the yaml
|
bool scan_{false}; ///< Should we scan ? Can be set in the yaml
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class InternalI2CBus : public I2CBus {
|
||||||
|
public:
|
||||||
|
/// @brief Returns the I2C port number.
|
||||||
|
/// @return the port number of the internal I2C bus
|
||||||
|
virtual int get_port() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace i2c
|
} // namespace i2c
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
#ifdef USE_ARDUINO
|
#ifdef USE_ARDUINO
|
||||||
|
|
||||||
#include "i2c_bus_arduino.h"
|
#include "i2c_bus_arduino.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <cstring>
|
||||||
#include "esphome/core/application.h"
|
#include "esphome/core/application.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include <Arduino.h>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace i2c {
|
namespace i2c {
|
||||||
@@ -23,6 +23,7 @@ void ArduinoI2CBus::setup() {
|
|||||||
} else {
|
} else {
|
||||||
wire_ = new TwoWire(next_bus_num); // NOLINT(cppcoreguidelines-owning-memory)
|
wire_ = new TwoWire(next_bus_num); // NOLINT(cppcoreguidelines-owning-memory)
|
||||||
}
|
}
|
||||||
|
this->port_ = next_bus_num;
|
||||||
next_bus_num++;
|
next_bus_num++;
|
||||||
#elif defined(USE_ESP8266)
|
#elif defined(USE_ESP8266)
|
||||||
wire_ = new TwoWire(); // NOLINT(cppcoreguidelines-owning-memory)
|
wire_ = new TwoWire(); // NOLINT(cppcoreguidelines-owning-memory)
|
||||||
@@ -125,7 +126,7 @@ ErrorCode ArduinoI2CBus::readv(uint8_t address, ReadBuffer *buffers, size_t cnt)
|
|||||||
size_t to_request = 0;
|
size_t to_request = 0;
|
||||||
for (size_t i = 0; i < cnt; i++)
|
for (size_t i = 0; i < cnt; i++)
|
||||||
to_request += buffers[i].len;
|
to_request += buffers[i].len;
|
||||||
size_t ret = wire_->requestFrom((int) address, (int) to_request, 1);
|
size_t ret = wire_->requestFrom(address, to_request, true);
|
||||||
if (ret != to_request) {
|
if (ret != to_request) {
|
||||||
ESP_LOGVV(TAG, "RX %u from %02X failed with error %u", to_request, address, ret);
|
ESP_LOGVV(TAG, "RX %u from %02X failed with error %u", to_request, address, ret);
|
||||||
return ERROR_TIMEOUT;
|
return ERROR_TIMEOUT;
|
||||||
|
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
#ifdef USE_ARDUINO
|
#ifdef USE_ARDUINO
|
||||||
|
|
||||||
#include "i2c_bus.h"
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "i2c_bus.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace i2c {
|
namespace i2c {
|
||||||
@@ -15,7 +15,7 @@ enum RecoveryCode {
|
|||||||
RECOVERY_COMPLETED,
|
RECOVERY_COMPLETED,
|
||||||
};
|
};
|
||||||
|
|
||||||
class ArduinoI2CBus : public I2CBus, public Component {
|
class ArduinoI2CBus : public InternalI2CBus, public Component {
|
||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
@@ -29,12 +29,15 @@ class ArduinoI2CBus : public I2CBus, public Component {
|
|||||||
void set_frequency(uint32_t frequency) { frequency_ = frequency; }
|
void set_frequency(uint32_t frequency) { frequency_ = frequency; }
|
||||||
void set_timeout(uint32_t timeout) { timeout_ = timeout; }
|
void set_timeout(uint32_t timeout) { timeout_ = timeout; }
|
||||||
|
|
||||||
|
int get_port() const override { return this->port_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void recover_();
|
void recover_();
|
||||||
void set_pins_and_clock_();
|
void set_pins_and_clock_();
|
||||||
RecoveryCode recovery_result_;
|
RecoveryCode recovery_result_;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
int8_t port_{-1};
|
||||||
TwoWire *wire_;
|
TwoWire *wire_;
|
||||||
uint8_t sda_pin_;
|
uint8_t sda_pin_;
|
||||||
uint8_t scl_pin_;
|
uint8_t scl_pin_;
|
||||||
|
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
#ifdef USE_ESP_IDF
|
#ifdef USE_ESP_IDF
|
||||||
|
|
||||||
#include "i2c_bus.h"
|
|
||||||
#include "esphome/core/component.h"
|
|
||||||
#include <driver/i2c.h>
|
#include <driver/i2c.h>
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "i2c_bus.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace i2c {
|
namespace i2c {
|
||||||
@@ -15,7 +15,7 @@ enum RecoveryCode {
|
|||||||
RECOVERY_COMPLETED,
|
RECOVERY_COMPLETED,
|
||||||
};
|
};
|
||||||
|
|
||||||
class IDFI2CBus : public I2CBus, public Component {
|
class IDFI2CBus : public InternalI2CBus, public Component {
|
||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
@@ -31,6 +31,8 @@ class IDFI2CBus : public I2CBus, public Component {
|
|||||||
void set_frequency(uint32_t frequency) { frequency_ = frequency; }
|
void set_frequency(uint32_t frequency) { frequency_ = frequency; }
|
||||||
void set_timeout(uint32_t timeout) { timeout_ = timeout; }
|
void set_timeout(uint32_t timeout) { timeout_ = timeout; }
|
||||||
|
|
||||||
|
int get_port() const override { return static_cast<int>(this->port_); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void recover_();
|
void recover_();
|
||||||
RecoveryCode recovery_result_;
|
RecoveryCode recovery_result_;
|
||||||
|
@@ -9,7 +9,7 @@ namespace i2s_audio {
|
|||||||
|
|
||||||
static const char *const TAG = "i2s_audio";
|
static const char *const TAG = "i2s_audio";
|
||||||
|
|
||||||
#if defined(USE_ESP_IDF) && (ESP_IDF_VERSION_MAJOR >= 5)
|
#if ESP_IDF_VERSION_MAJOR >= 5
|
||||||
static const uint8_t I2S_NUM_MAX = SOC_I2S_NUM; // because IDF 5+ took this away :(
|
static const uint8_t I2S_NUM_MAX = SOC_I2S_NUM; // because IDF 5+ took this away :(
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ void I2SAudioComponent::setup() {
|
|||||||
|
|
||||||
static i2s_port_t next_port_num = I2S_NUM_0;
|
static i2s_port_t next_port_num = I2S_NUM_0;
|
||||||
if (next_port_num >= I2S_NUM_MAX) {
|
if (next_port_num >= I2S_NUM_MAX) {
|
||||||
ESP_LOGE(TAG, "Too many I2S Audio components");
|
ESP_LOGE(TAG, "Too many components");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@@ -114,7 +114,7 @@ async def to_code(config):
|
|||||||
cg.add(var.set_external_dac_channels(2 if config[CONF_MODE] == "stereo" else 1))
|
cg.add(var.set_external_dac_channels(2 if config[CONF_MODE] == "stereo" else 1))
|
||||||
cg.add(var.set_i2s_comm_fmt_lsb(config[CONF_I2S_COMM_FMT] == "lsb"))
|
cg.add(var.set_i2s_comm_fmt_lsb(config[CONF_I2S_COMM_FMT] == "lsb"))
|
||||||
|
|
||||||
cg.add_library("WiFiClientSecure", None)
|
cg.add_library("NetworkClientSecure", None)
|
||||||
cg.add_library("HTTPClient", None)
|
cg.add_library("HTTPClient", None)
|
||||||
cg.add_library("esphome/ESP32-audioI2S", "2.2.0")
|
cg.add_library("esphome/ESP32-audioI2S", "2.3.0")
|
||||||
cg.add_build_flag("-DAUDIO_NO_SD_FS")
|
cg.add_build_flag("-DAUDIO_NO_SD_FS")
|
||||||
|
@@ -45,7 +45,7 @@ void I2SAudioMicrophone::setup() {
|
|||||||
#if SOC_I2S_SUPPORTS_ADC
|
#if SOC_I2S_SUPPORTS_ADC
|
||||||
if (this->adc_) {
|
if (this->adc_) {
|
||||||
if (this->parent_->get_port() != I2S_NUM_0) {
|
if (this->parent_->get_port() != I2S_NUM_0) {
|
||||||
ESP_LOGE(TAG, "Internal ADC only works on I2S0!");
|
ESP_LOGE(TAG, "Internal ADC only works on I2S0");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,7 @@ void I2SAudioMicrophone::setup() {
|
|||||||
{
|
{
|
||||||
if (this->pdm_) {
|
if (this->pdm_) {
|
||||||
if (this->parent_->get_port() != I2S_NUM_0) {
|
if (this->parent_->get_port() != I2S_NUM_0) {
|
||||||
ESP_LOGE(TAG, "PDM only works on I2S0!");
|
ESP_LOGE(TAG, "PDM only works on I2S0");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -64,14 +64,14 @@ void I2SAudioMicrophone::setup() {
|
|||||||
|
|
||||||
this->active_listeners_semaphore_ = xSemaphoreCreateCounting(MAX_LISTENERS, MAX_LISTENERS);
|
this->active_listeners_semaphore_ = xSemaphoreCreateCounting(MAX_LISTENERS, MAX_LISTENERS);
|
||||||
if (this->active_listeners_semaphore_ == nullptr) {
|
if (this->active_listeners_semaphore_ == nullptr) {
|
||||||
ESP_LOGE(TAG, "Failed to create semaphore");
|
ESP_LOGE(TAG, "Creating semaphore failed");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->event_group_ = xEventGroupCreate();
|
this->event_group_ = xEventGroupCreate();
|
||||||
if (this->event_group_ == nullptr) {
|
if (this->event_group_ == nullptr) {
|
||||||
ESP_LOGE(TAG, "Failed to create event group");
|
ESP_LOGE(TAG, "Creating event group failed");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -79,6 +79,15 @@ void I2SAudioMicrophone::setup() {
|
|||||||
this->configure_stream_settings_();
|
this->configure_stream_settings_();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void I2SAudioMicrophone::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG,
|
||||||
|
"Microphone:\n"
|
||||||
|
" Pin: %d\n"
|
||||||
|
" PDM: %s\n"
|
||||||
|
" DC offset correction: %s",
|
||||||
|
static_cast<int8_t>(this->din_pin_), YESNO(this->pdm_), YESNO(this->correct_dc_offset_));
|
||||||
|
}
|
||||||
|
|
||||||
void I2SAudioMicrophone::configure_stream_settings_() {
|
void I2SAudioMicrophone::configure_stream_settings_() {
|
||||||
uint8_t channel_count = 1;
|
uint8_t channel_count = 1;
|
||||||
#ifdef USE_I2S_LEGACY
|
#ifdef USE_I2S_LEGACY
|
||||||
@@ -127,6 +136,7 @@ bool I2SAudioMicrophone::start_driver_() {
|
|||||||
if (!this->parent_->try_lock()) {
|
if (!this->parent_->try_lock()) {
|
||||||
return false; // Waiting for another i2s to return lock
|
return false; // Waiting for another i2s to return lock
|
||||||
}
|
}
|
||||||
|
this->locked_driver_ = true;
|
||||||
esp_err_t err;
|
esp_err_t err;
|
||||||
|
|
||||||
#ifdef USE_I2S_LEGACY
|
#ifdef USE_I2S_LEGACY
|
||||||
@@ -151,7 +161,7 @@ bool I2SAudioMicrophone::start_driver_() {
|
|||||||
config.mode = (i2s_mode_t) (config.mode | I2S_MODE_ADC_BUILT_IN);
|
config.mode = (i2s_mode_t) (config.mode | I2S_MODE_ADC_BUILT_IN);
|
||||||
err = i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr);
|
err = i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Error installing I2S driver: %s", esp_err_to_name(err));
|
ESP_LOGE(TAG, "Error installing driver: %s", esp_err_to_name(err));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,7 +184,7 @@ bool I2SAudioMicrophone::start_driver_() {
|
|||||||
|
|
||||||
err = i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr);
|
err = i2s_driver_install(this->parent_->get_port(), &config, 0, nullptr);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Error installing I2S driver: %s", esp_err_to_name(err));
|
ESP_LOGE(TAG, "Error installing driver: %s", esp_err_to_name(err));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +193,7 @@ bool I2SAudioMicrophone::start_driver_() {
|
|||||||
|
|
||||||
err = i2s_set_pin(this->parent_->get_port(), &pin_config);
|
err = i2s_set_pin(this->parent_->get_port(), &pin_config);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Error setting I2S pin: %s", esp_err_to_name(err));
|
ESP_LOGE(TAG, "Error setting pin: %s", esp_err_to_name(err));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -198,7 +208,7 @@ bool I2SAudioMicrophone::start_driver_() {
|
|||||||
/* Allocate a new RX channel and get the handle of this channel */
|
/* Allocate a new RX channel and get the handle of this channel */
|
||||||
err = i2s_new_channel(&chan_cfg, NULL, &this->rx_handle_);
|
err = i2s_new_channel(&chan_cfg, NULL, &this->rx_handle_);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Error creating new I2S channel: %s", esp_err_to_name(err));
|
ESP_LOGE(TAG, "Error creating channel: %s", esp_err_to_name(err));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,14 +280,14 @@ bool I2SAudioMicrophone::start_driver_() {
|
|||||||
err = i2s_channel_init_std_mode(this->rx_handle_, &std_cfg);
|
err = i2s_channel_init_std_mode(this->rx_handle_, &std_cfg);
|
||||||
}
|
}
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Error initializing I2S channel: %s", esp_err_to_name(err));
|
ESP_LOGE(TAG, "Error initializing channel: %s", esp_err_to_name(err));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Before reading data, start the RX channel first */
|
/* Before reading data, start the RX channel first */
|
||||||
i2s_channel_enable(this->rx_handle_);
|
i2s_channel_enable(this->rx_handle_);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Error enabling I2S Microphone: %s", esp_err_to_name(err));
|
ESP_LOGE(TAG, "Enabling failed: %s", esp_err_to_name(err));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -304,31 +314,37 @@ void I2SAudioMicrophone::stop_driver_() {
|
|||||||
if (this->adc_) {
|
if (this->adc_) {
|
||||||
err = i2s_adc_disable(this->parent_->get_port());
|
err = i2s_adc_disable(this->parent_->get_port());
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGW(TAG, "Error disabling ADC - it may not have started: %s", esp_err_to_name(err));
|
ESP_LOGW(TAG, "Error disabling ADC: %s", esp_err_to_name(err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
err = i2s_stop(this->parent_->get_port());
|
err = i2s_stop(this->parent_->get_port());
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGW(TAG, "Error stopping I2S microphone - it may not have started: %s", esp_err_to_name(err));
|
ESP_LOGW(TAG, "Error stopping: %s", esp_err_to_name(err));
|
||||||
}
|
}
|
||||||
err = i2s_driver_uninstall(this->parent_->get_port());
|
err = i2s_driver_uninstall(this->parent_->get_port());
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
ESP_LOGW(TAG, "Error uninstalling I2S driver - it may not have started: %s", esp_err_to_name(err));
|
ESP_LOGW(TAG, "Error uninstalling driver: %s", esp_err_to_name(err));
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
/* Have to stop the channel before deleting it */
|
if (this->rx_handle_ != nullptr) {
|
||||||
err = i2s_channel_disable(this->rx_handle_);
|
/* Have to stop the channel before deleting it */
|
||||||
if (err != ESP_OK) {
|
err = i2s_channel_disable(this->rx_handle_);
|
||||||
ESP_LOGW(TAG, "Error stopping I2S microphone - it may not have started: %s", esp_err_to_name(err));
|
if (err != ESP_OK) {
|
||||||
}
|
ESP_LOGW(TAG, "Error stopping: %s", esp_err_to_name(err));
|
||||||
/* If the handle is not needed any more, delete it to release the channel resources */
|
}
|
||||||
err = i2s_del_channel(this->rx_handle_);
|
/* If the handle is not needed any more, delete it to release the channel resources */
|
||||||
if (err != ESP_OK) {
|
err = i2s_del_channel(this->rx_handle_);
|
||||||
ESP_LOGW(TAG, "Error deleting I2S channel - it may not have started: %s", esp_err_to_name(err));
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGW(TAG, "Error deleting channel: %s", esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
this->rx_handle_ = nullptr;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
this->parent_->unlock();
|
if (this->locked_driver_) {
|
||||||
|
this->parent_->unlock();
|
||||||
|
this->locked_driver_ = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void I2SAudioMicrophone::mic_task(void *params) {
|
void I2SAudioMicrophone::mic_task(void *params) {
|
||||||
@@ -400,7 +416,7 @@ size_t I2SAudioMicrophone::read_(uint8_t *buf, size_t len, TickType_t ticks_to_w
|
|||||||
// Ignore ESP_ERR_TIMEOUT if ticks_to_wait = 0, as it will read the data on the next call
|
// Ignore ESP_ERR_TIMEOUT if ticks_to_wait = 0, as it will read the data on the next call
|
||||||
if (!this->status_has_warning()) {
|
if (!this->status_has_warning()) {
|
||||||
// Avoid spamming the logs with the error message if its repeated
|
// Avoid spamming the logs with the error message if its repeated
|
||||||
ESP_LOGW(TAG, "Error reading from I2S microphone: %s", esp_err_to_name(err));
|
ESP_LOGW(TAG, "Read error: %s", esp_err_to_name(err));
|
||||||
}
|
}
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
return 0;
|
return 0;
|
||||||
@@ -428,19 +444,19 @@ void I2SAudioMicrophone::loop() {
|
|||||||
uint32_t event_group_bits = xEventGroupGetBits(this->event_group_);
|
uint32_t event_group_bits = xEventGroupGetBits(this->event_group_);
|
||||||
|
|
||||||
if (event_group_bits & MicrophoneEventGroupBits::TASK_STARTING) {
|
if (event_group_bits & MicrophoneEventGroupBits::TASK_STARTING) {
|
||||||
ESP_LOGD(TAG, "Task started, attempting to allocate buffer");
|
ESP_LOGV(TAG, "Task started, attempting to allocate buffer");
|
||||||
xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_STARTING);
|
xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_STARTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event_group_bits & MicrophoneEventGroupBits::TASK_RUNNING) {
|
if (event_group_bits & MicrophoneEventGroupBits::TASK_RUNNING) {
|
||||||
ESP_LOGD(TAG, "Task is running and reading data");
|
ESP_LOGV(TAG, "Task is running and reading data");
|
||||||
|
|
||||||
xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_RUNNING);
|
xEventGroupClearBits(this->event_group_, MicrophoneEventGroupBits::TASK_RUNNING);
|
||||||
this->state_ = microphone::STATE_RUNNING;
|
this->state_ = microphone::STATE_RUNNING;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((event_group_bits & MicrophoneEventGroupBits::TASK_STOPPED)) {
|
if ((event_group_bits & MicrophoneEventGroupBits::TASK_STOPPED)) {
|
||||||
ESP_LOGD(TAG, "Task finished, freeing resources and uninstalling I2S driver");
|
ESP_LOGV(TAG, "Task finished, freeing resources and uninstalling driver");
|
||||||
|
|
||||||
vTaskDelete(this->task_handle_);
|
vTaskDelete(this->task_handle_);
|
||||||
this->task_handle_ = nullptr;
|
this->task_handle_ = nullptr;
|
||||||
@@ -470,7 +486,8 @@ void I2SAudioMicrophone::loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this->start_driver_()) {
|
if (!this->start_driver_()) {
|
||||||
this->status_momentary_error("I2S driver failed to start, unloading it and attempting again in 1 second", 1000);
|
ESP_LOGE(TAG, "Driver failed to start; retrying in 1 second");
|
||||||
|
this->status_momentary_error("driver_fail", 1000);
|
||||||
this->stop_driver_(); // Stop/frees whatever possibly started
|
this->stop_driver_(); // Stop/frees whatever possibly started
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -480,7 +497,8 @@ void I2SAudioMicrophone::loop() {
|
|||||||
&this->task_handle_);
|
&this->task_handle_);
|
||||||
|
|
||||||
if (this->task_handle_ == nullptr) {
|
if (this->task_handle_ == nullptr) {
|
||||||
this->status_momentary_error("Task failed to start, attempting again in 1 second", 1000);
|
ESP_LOGE(TAG, "Task failed to start, retrying in 1 second");
|
||||||
|
this->status_momentary_error("task_fail", 1000);
|
||||||
this->stop_driver_(); // Stops the driver to return the lock; will be reloaded in next attempt
|
this->stop_driver_(); // Stops the driver to return the lock; will be reloaded in next attempt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,6 +18,7 @@ namespace i2s_audio {
|
|||||||
class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, public Component {
|
class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, public Component {
|
||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
|
void dump_config() override;
|
||||||
void start() override;
|
void start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
|
|
||||||
@@ -80,6 +81,7 @@ class I2SAudioMicrophone : public I2SAudioIn, public microphone::Microphone, pub
|
|||||||
bool pdm_{false};
|
bool pdm_{false};
|
||||||
|
|
||||||
bool correct_dc_offset_;
|
bool correct_dc_offset_;
|
||||||
|
bool locked_driver_{false};
|
||||||
int32_t dc_offset_{0};
|
int32_t dc_offset_{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -110,29 +110,48 @@ void I2SAudioSpeaker::setup() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void I2SAudioSpeaker::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG,
|
||||||
|
"Speaker:\n"
|
||||||
|
" Pin: %d\n"
|
||||||
|
" Buffer duration: %" PRIu32,
|
||||||
|
static_cast<int8_t>(this->dout_pin_), this->buffer_duration_ms_);
|
||||||
|
if (this->timeout_.has_value()) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Timeout: %" PRIu32 " ms", this->timeout_.value());
|
||||||
|
}
|
||||||
|
#ifdef USE_I2S_LEGACY
|
||||||
|
#if SOC_I2S_SUPPORTS_DAC
|
||||||
|
ESP_LOGCONFIG(TAG, " Internal DAC mode: %d", static_cast<int8_t>(this->internal_dac_mode_));
|
||||||
|
#endif
|
||||||
|
ESP_LOGCONFIG(TAG, " Communication format: %d", static_cast<int8_t>(this->i2s_comm_fmt_));
|
||||||
|
#else
|
||||||
|
ESP_LOGCONFIG(TAG, " Communication format: %s", this->i2s_comm_fmt_.c_str());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void I2SAudioSpeaker::loop() {
|
void I2SAudioSpeaker::loop() {
|
||||||
uint32_t event_group_bits = xEventGroupGetBits(this->event_group_);
|
uint32_t event_group_bits = xEventGroupGetBits(this->event_group_);
|
||||||
|
|
||||||
if (event_group_bits & SpeakerEventGroupBits::STATE_STARTING) {
|
if (event_group_bits & SpeakerEventGroupBits::STATE_STARTING) {
|
||||||
ESP_LOGD(TAG, "Starting Speaker");
|
ESP_LOGD(TAG, "Starting");
|
||||||
this->state_ = speaker::STATE_STARTING;
|
this->state_ = speaker::STATE_STARTING;
|
||||||
xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STARTING);
|
xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STARTING);
|
||||||
}
|
}
|
||||||
if (event_group_bits & SpeakerEventGroupBits::STATE_RUNNING) {
|
if (event_group_bits & SpeakerEventGroupBits::STATE_RUNNING) {
|
||||||
ESP_LOGD(TAG, "Started Speaker");
|
ESP_LOGD(TAG, "Started");
|
||||||
this->state_ = speaker::STATE_RUNNING;
|
this->state_ = speaker::STATE_RUNNING;
|
||||||
xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_RUNNING);
|
xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_RUNNING);
|
||||||
this->status_clear_warning();
|
this->status_clear_warning();
|
||||||
this->status_clear_error();
|
this->status_clear_error();
|
||||||
}
|
}
|
||||||
if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPING) {
|
if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPING) {
|
||||||
ESP_LOGD(TAG, "Stopping Speaker");
|
ESP_LOGD(TAG, "Stopping");
|
||||||
this->state_ = speaker::STATE_STOPPING;
|
this->state_ = speaker::STATE_STOPPING;
|
||||||
xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STOPPING);
|
xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STOPPING);
|
||||||
}
|
}
|
||||||
if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPED) {
|
if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPED) {
|
||||||
if (!this->task_created_) {
|
if (!this->task_created_) {
|
||||||
ESP_LOGD(TAG, "Stopped Speaker");
|
ESP_LOGD(TAG, "Stopped");
|
||||||
this->state_ = speaker::STATE_STOPPED;
|
this->state_ = speaker::STATE_STOPPED;
|
||||||
xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ALL_BITS);
|
xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ALL_BITS);
|
||||||
this->speaker_task_handle_ = nullptr;
|
this->speaker_task_handle_ = nullptr;
|
||||||
@@ -140,20 +159,19 @@ void I2SAudioSpeaker::loop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (event_group_bits & SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START) {
|
if (event_group_bits & SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START) {
|
||||||
this->status_set_error("Failed to start speaker task");
|
this->status_set_error("Failed to start task");
|
||||||
xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START);
|
xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS) {
|
if (event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS) {
|
||||||
uint32_t error_bits = event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS;
|
uint32_t error_bits = event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS;
|
||||||
ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(err_bit_to_esp_err(error_bits)));
|
ESP_LOGW(TAG, "Writing failed: %s", esp_err_to_name(err_bit_to_esp_err(error_bits)));
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event_group_bits & SpeakerEventGroupBits::ERR_ESP_NOT_SUPPORTED) {
|
if (event_group_bits & SpeakerEventGroupBits::ERR_ESP_NOT_SUPPORTED) {
|
||||||
this->status_set_error("Failed to adjust I2S bus to match the incoming audio");
|
this->status_set_error("Failed to adjust bus to match incoming audio");
|
||||||
ESP_LOGE(TAG,
|
ESP_LOGE(TAG, "Incompatible audio format: sample rate = %" PRIu32 ", channels = %u, bits per sample = %u",
|
||||||
"Incompatible audio format: sample rate = %" PRIu32 ", channels = %" PRIu8 ", bits per sample = %" PRIu8,
|
|
||||||
this->audio_stream_info_.get_sample_rate(), this->audio_stream_info_.get_channels(),
|
this->audio_stream_info_.get_sample_rate(), this->audio_stream_info_.get_channels(),
|
||||||
this->audio_stream_info_.get_bits_per_sample());
|
this->audio_stream_info_.get_bits_per_sample());
|
||||||
}
|
}
|
||||||
@@ -202,7 +220,7 @@ void I2SAudioSpeaker::set_mute_state(bool mute_state) {
|
|||||||
|
|
||||||
size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) {
|
size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) {
|
||||||
if (this->is_failed()) {
|
if (this->is_failed()) {
|
||||||
ESP_LOGE(TAG, "Cannot play audio, speaker failed to setup");
|
ESP_LOGE(TAG, "Setup failed; cannot play audio");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (this->state_ != speaker::STATE_RUNNING && this->state_ != speaker::STATE_STARTING) {
|
if (this->state_ != speaker::STATE_RUNNING && this->state_ != speaker::STATE_STARTING) {
|
||||||
@@ -466,7 +484,7 @@ bool I2SAudioSpeaker::send_esp_err_to_event_group_(esp_err_t err) {
|
|||||||
esp_err_t I2SAudioSpeaker::allocate_buffers_(size_t data_buffer_size, size_t ring_buffer_size) {
|
esp_err_t I2SAudioSpeaker::allocate_buffers_(size_t data_buffer_size, size_t ring_buffer_size) {
|
||||||
if (this->data_buffer_ == nullptr) {
|
if (this->data_buffer_ == nullptr) {
|
||||||
// Allocate data buffer for temporarily storing audio from the ring buffer before writing to the I2S bus
|
// Allocate data buffer for temporarily storing audio from the ring buffer before writing to the I2S bus
|
||||||
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
RAMAllocator<uint8_t> allocator;
|
||||||
this->data_buffer_ = allocator.allocate(data_buffer_size);
|
this->data_buffer_ = allocator.allocate(data_buffer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -680,7 +698,7 @@ void I2SAudioSpeaker::delete_task_(size_t buffer_size) {
|
|||||||
this->audio_ring_buffer_.reset(); // Releases ownership of the shared_ptr
|
this->audio_ring_buffer_.reset(); // Releases ownership of the shared_ptr
|
||||||
|
|
||||||
if (this->data_buffer_ != nullptr) {
|
if (this->data_buffer_ != nullptr) {
|
||||||
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
RAMAllocator<uint8_t> allocator;
|
||||||
allocator.deallocate(this->data_buffer_, buffer_size);
|
allocator.deallocate(this->data_buffer_, buffer_size);
|
||||||
this->data_buffer_ = nullptr;
|
this->data_buffer_ = nullptr;
|
||||||
}
|
}
|
||||||
|
@@ -24,6 +24,7 @@ class I2SAudioSpeaker : public I2SAudioOut, public speaker::Speaker, public Comp
|
|||||||
float get_setup_priority() const override { return esphome::setup_priority::PROCESSOR; }
|
float get_setup_priority() const override { return esphome::setup_priority::PROCESSOR; }
|
||||||
|
|
||||||
void setup() override;
|
void setup() override;
|
||||||
|
void dump_config() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
|
||||||
void set_buffer_duration(uint32_t buffer_duration_ms) { this->buffer_duration_ms_ = buffer_duration_ms; }
|
void set_buffer_duration(uint32_t buffer_duration_ms) { this->buffer_duration_ms_ = buffer_duration_ms; }
|
||||||
|
@@ -129,6 +129,13 @@ void INA219Component::setup() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void INA219Component::on_powerdown() {
|
||||||
|
// Mode = 0 -> power down
|
||||||
|
if (!this->write_byte_16(INA219_REGISTER_CONFIG, 0)) {
|
||||||
|
ESP_LOGE(TAG, "powerdown error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void INA219Component::dump_config() {
|
void INA219Component::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "INA219:");
|
ESP_LOGCONFIG(TAG, "INA219:");
|
||||||
LOG_I2C_DEVICE(this);
|
LOG_I2C_DEVICE(this);
|
||||||
|
@@ -15,6 +15,7 @@ class INA219Component : public PollingComponent, public i2c::I2CDevice {
|
|||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
float get_setup_priority() const override;
|
float get_setup_priority() const override;
|
||||||
void update() override;
|
void update() override;
|
||||||
|
void on_powerdown() override;
|
||||||
|
|
||||||
void set_shunt_resistance_ohm(float shunt_resistance_ohm) { shunt_resistance_ohm_ = shunt_resistance_ohm; }
|
void set_shunt_resistance_ohm(float shunt_resistance_ohm) { shunt_resistance_ohm_ = shunt_resistance_ohm; }
|
||||||
void set_max_current_a(float max_current_a) { max_current_a_ = max_current_a; }
|
void set_max_current_a(float max_current_a) { max_current_a_ = max_current_a; }
|
||||||
|
@@ -57,8 +57,8 @@ void Inkplate6::setup() {
|
|||||||
* Allocate buffers. May be called after setup to re-initialise if e.g. greyscale is changed.
|
* Allocate buffers. May be called after setup to re-initialise if e.g. greyscale is changed.
|
||||||
*/
|
*/
|
||||||
void Inkplate6::initialize_() {
|
void Inkplate6::initialize_() {
|
||||||
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
|
RAMAllocator<uint8_t> allocator;
|
||||||
ExternalRAMAllocator<uint32_t> allocator32(ExternalRAMAllocator<uint32_t>::ALLOW_FAILURE);
|
RAMAllocator<uint32_t> allocator32;
|
||||||
uint32_t buffer_size = this->get_buffer_length_();
|
uint32_t buffer_size = this->get_buffer_length_();
|
||||||
if (buffer_size == 0)
|
if (buffer_size == 0)
|
||||||
return;
|
return;
|
||||||
|
@@ -19,9 +19,8 @@ void KMeterISOComponent::setup() {
|
|||||||
|
|
||||||
// Mark as not failed before initializing. Some devices will turn off sensors to save on batteries
|
// Mark as not failed before initializing. Some devices will turn off sensors to save on batteries
|
||||||
// and when they come back on, the COMPONENT_STATE_FAILED bit must be unset on the component.
|
// and when they come back on, the COMPONENT_STATE_FAILED bit must be unset on the component.
|
||||||
if ((this->component_state_ & COMPONENT_STATE_MASK) == COMPONENT_STATE_FAILED) {
|
if (this->is_failed()) {
|
||||||
this->component_state_ &= ~COMPONENT_STATE_MASK;
|
this->reset_to_construction_state();
|
||||||
this->component_state_ |= COMPONENT_STATE_CONSTRUCTION;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto err = this->bus_->writev(this->address_, nullptr, 0);
|
auto err = this->bus_->writev(this->address_, nullptr, 0);
|
||||||
|
@@ -3,6 +3,8 @@ from esphome.components import number
|
|||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
|
CONF_MOVE_THRESHOLD,
|
||||||
|
CONF_STILL_THRESHOLD,
|
||||||
CONF_TIMEOUT,
|
CONF_TIMEOUT,
|
||||||
DEVICE_CLASS_DISTANCE,
|
DEVICE_CLASS_DISTANCE,
|
||||||
DEVICE_CLASS_ILLUMINANCE,
|
DEVICE_CLASS_ILLUMINANCE,
|
||||||
@@ -24,8 +26,6 @@ MaxDistanceTimeoutNumber = ld2410_ns.class_("MaxDistanceTimeoutNumber", number.N
|
|||||||
CONF_MAX_MOVE_DISTANCE_GATE = "max_move_distance_gate"
|
CONF_MAX_MOVE_DISTANCE_GATE = "max_move_distance_gate"
|
||||||
CONF_MAX_STILL_DISTANCE_GATE = "max_still_distance_gate"
|
CONF_MAX_STILL_DISTANCE_GATE = "max_still_distance_gate"
|
||||||
CONF_LIGHT_THRESHOLD = "light_threshold"
|
CONF_LIGHT_THRESHOLD = "light_threshold"
|
||||||
CONF_STILL_THRESHOLD = "still_threshold"
|
|
||||||
CONF_MOVE_THRESHOLD = "move_threshold"
|
|
||||||
|
|
||||||
TIMEOUT_GROUP = "timeout"
|
TIMEOUT_GROUP = "timeout"
|
||||||
|
|
||||||
|
@@ -3,6 +3,7 @@ from esphome.components import sensor
|
|||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_LIGHT,
|
CONF_LIGHT,
|
||||||
|
CONF_MOVING_DISTANCE,
|
||||||
DEVICE_CLASS_DISTANCE,
|
DEVICE_CLASS_DISTANCE,
|
||||||
DEVICE_CLASS_ILLUMINANCE,
|
DEVICE_CLASS_ILLUMINANCE,
|
||||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
@@ -17,7 +18,6 @@ from esphome.const import (
|
|||||||
from . import CONF_LD2410_ID, LD2410Component
|
from . import CONF_LD2410_ID, LD2410Component
|
||||||
|
|
||||||
DEPENDENCIES = ["ld2410"]
|
DEPENDENCIES = ["ld2410"]
|
||||||
CONF_MOVING_DISTANCE = "moving_distance"
|
|
||||||
CONF_STILL_DISTANCE = "still_distance"
|
CONF_STILL_DISTANCE = "still_distance"
|
||||||
CONF_MOVING_ENERGY = "moving_energy"
|
CONF_MOVING_ENERGY = "moving_energy"
|
||||||
CONF_STILL_ENERGY = "still_energy"
|
CONF_STILL_ENERGY = "still_energy"
|
||||||
|
@@ -2,6 +2,7 @@ import esphome.codegen as cg
|
|||||||
from esphome.components import switch
|
from esphome.components import switch
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
|
CONF_BLUETOOTH,
|
||||||
DEVICE_CLASS_SWITCH,
|
DEVICE_CLASS_SWITCH,
|
||||||
ENTITY_CATEGORY_CONFIG,
|
ENTITY_CATEGORY_CONFIG,
|
||||||
ICON_BLUETOOTH,
|
ICON_BLUETOOTH,
|
||||||
@@ -14,7 +15,6 @@ BluetoothSwitch = ld2410_ns.class_("BluetoothSwitch", switch.Switch)
|
|||||||
EngineeringModeSwitch = ld2410_ns.class_("EngineeringModeSwitch", switch.Switch)
|
EngineeringModeSwitch = ld2410_ns.class_("EngineeringModeSwitch", switch.Switch)
|
||||||
|
|
||||||
CONF_ENGINEERING_MODE = "engineering_mode"
|
CONF_ENGINEERING_MODE = "engineering_mode"
|
||||||
CONF_BLUETOOTH = "bluetooth"
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = {
|
CONFIG_SCHEMA = {
|
||||||
cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component),
|
cv.GenerateID(CONF_LD2410_ID): cv.use_id(LD2410Component),
|
||||||
|
@@ -3,6 +3,8 @@ from esphome.components import number
|
|||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
|
CONF_MOVE_THRESHOLD,
|
||||||
|
CONF_STILL_THRESHOLD,
|
||||||
DEVICE_CLASS_DISTANCE,
|
DEVICE_CLASS_DISTANCE,
|
||||||
ENTITY_CATEGORY_CONFIG,
|
ENTITY_CATEGORY_CONFIG,
|
||||||
ICON_MOTION_SENSOR,
|
ICON_MOTION_SENSOR,
|
||||||
@@ -31,8 +33,6 @@ LD2420StillThresholdNumbers = ld2420_ns.class_(
|
|||||||
)
|
)
|
||||||
CONF_MIN_GATE_DISTANCE = "min_gate_distance"
|
CONF_MIN_GATE_DISTANCE = "min_gate_distance"
|
||||||
CONF_MAX_GATE_DISTANCE = "max_gate_distance"
|
CONF_MAX_GATE_DISTANCE = "max_gate_distance"
|
||||||
CONF_STILL_THRESHOLD = "still_threshold"
|
|
||||||
CONF_MOVE_THRESHOLD = "move_threshold"
|
|
||||||
CONF_GATE_MOVE_SENSITIVITY = "gate_move_sensitivity"
|
CONF_GATE_MOVE_SENSITIVITY = "gate_move_sensitivity"
|
||||||
CONF_GATE_STILL_SENSITIVITY = "gate_still_sensitivity"
|
CONF_GATE_STILL_SENSITIVITY = "gate_still_sensitivity"
|
||||||
CONF_GATE_SELECT = "gate_select"
|
CONF_GATE_SELECT = "gate_select"
|
||||||
|
@@ -1,13 +1,17 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import sensor
|
from esphome.components import sensor
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, DEVICE_CLASS_DISTANCE, UNIT_CENTIMETER
|
from esphome.const import (
|
||||||
|
CONF_ID,
|
||||||
|
CONF_MOVING_DISTANCE,
|
||||||
|
DEVICE_CLASS_DISTANCE,
|
||||||
|
UNIT_CENTIMETER,
|
||||||
|
)
|
||||||
|
|
||||||
from .. import CONF_LD2420_ID, LD2420Component, ld2420_ns
|
from .. import CONF_LD2420_ID, LD2420Component, ld2420_ns
|
||||||
|
|
||||||
LD2420Sensor = ld2420_ns.class_("LD2420Sensor", sensor.Sensor, cg.Component)
|
LD2420Sensor = ld2420_ns.class_("LD2420Sensor", sensor.Sensor, cg.Component)
|
||||||
|
|
||||||
CONF_MOVING_DISTANCE = "moving_distance"
|
|
||||||
CONF_GATE_ENERGY = "gate_energy"
|
CONF_GATE_ENERGY = "gate_energy"
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
|
@@ -2,6 +2,7 @@ import esphome.codegen as cg
|
|||||||
from esphome.components import switch
|
from esphome.components import switch
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
|
CONF_BLUETOOTH,
|
||||||
DEVICE_CLASS_SWITCH,
|
DEVICE_CLASS_SWITCH,
|
||||||
ENTITY_CATEGORY_CONFIG,
|
ENTITY_CATEGORY_CONFIG,
|
||||||
ICON_BLUETOOTH,
|
ICON_BLUETOOTH,
|
||||||
@@ -13,7 +14,6 @@ from .. import CONF_LD2450_ID, LD2450Component, ld2450_ns
|
|||||||
BluetoothSwitch = ld2450_ns.class_("BluetoothSwitch", switch.Switch)
|
BluetoothSwitch = ld2450_ns.class_("BluetoothSwitch", switch.Switch)
|
||||||
MultiTargetSwitch = ld2450_ns.class_("MultiTargetSwitch", switch.Switch)
|
MultiTargetSwitch = ld2450_ns.class_("MultiTargetSwitch", switch.Switch)
|
||||||
|
|
||||||
CONF_BLUETOOTH = "bluetooth"
|
|
||||||
CONF_MULTI_TARGET = "multi_target"
|
CONF_MULTI_TARGET = "multi_target"
|
||||||
|
|
||||||
CONFIG_SCHEMA = {
|
CONFIG_SCHEMA = {
|
||||||
|
@@ -3,28 +3,16 @@
|
|||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
#ifdef USE_ARDUINO
|
|
||||||
#include <esp32-hal-ledc.h>
|
|
||||||
#endif
|
|
||||||
#include <driver/ledc.h>
|
#include <driver/ledc.h>
|
||||||
|
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
|
||||||
#define CLOCK_FREQUENCY 80e6f
|
#define CLOCK_FREQUENCY 80e6f
|
||||||
|
|
||||||
#ifdef USE_ARDUINO
|
|
||||||
#ifdef SOC_LEDC_SUPPORT_XTAL_CLOCK
|
|
||||||
#undef CLOCK_FREQUENCY
|
|
||||||
// starting with ESP32 Arduino 2.0.2, the 40MHz crystal is used as clock by default if supported
|
|
||||||
#define CLOCK_FREQUENCY 40e6f
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#ifdef SOC_LEDC_SUPPORT_APB_CLOCK
|
#ifdef SOC_LEDC_SUPPORT_APB_CLOCK
|
||||||
#define DEFAULT_CLK LEDC_USE_APB_CLK
|
#define DEFAULT_CLK LEDC_USE_APB_CLK
|
||||||
#else
|
#else
|
||||||
#define DEFAULT_CLK LEDC_AUTO_CLK
|
#define DEFAULT_CLK LEDC_AUTO_CLK
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
|
|
||||||
static const uint8_t SETUP_ATTEMPT_COUNT_MAX = 5;
|
static const uint8_t SETUP_ATTEMPT_COUNT_MAX = 5;
|
||||||
|
|
||||||
@@ -34,7 +22,6 @@ namespace ledc {
|
|||||||
static const char *const TAG = "ledc.output";
|
static const char *const TAG = "ledc.output";
|
||||||
|
|
||||||
static const int MAX_RES_BITS = LEDC_TIMER_BIT_MAX - 1;
|
static const int MAX_RES_BITS = LEDC_TIMER_BIT_MAX - 1;
|
||||||
#ifdef USE_ESP_IDF
|
|
||||||
#if SOC_LEDC_SUPPORT_HS_MODE
|
#if SOC_LEDC_SUPPORT_HS_MODE
|
||||||
// Only ESP32 has LEDC_HIGH_SPEED_MODE
|
// Only ESP32 has LEDC_HIGH_SPEED_MODE
|
||||||
inline ledc_mode_t get_speed_mode(uint8_t channel) { return channel < 8 ? LEDC_HIGH_SPEED_MODE : LEDC_LOW_SPEED_MODE; }
|
inline ledc_mode_t get_speed_mode(uint8_t channel) { return channel < 8 ? LEDC_HIGH_SPEED_MODE : LEDC_LOW_SPEED_MODE; }
|
||||||
@@ -44,7 +31,6 @@ inline ledc_mode_t get_speed_mode(uint8_t channel) { return channel < 8 ? LEDC_H
|
|||||||
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-reference/peripherals/ledc.html#functionality-overview
|
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/api-reference/peripherals/ledc.html#functionality-overview
|
||||||
inline ledc_mode_t get_speed_mode(uint8_t) { return LEDC_LOW_SPEED_MODE; }
|
inline ledc_mode_t get_speed_mode(uint8_t) { return LEDC_LOW_SPEED_MODE; }
|
||||||
#endif
|
#endif
|
||||||
#endif
|
|
||||||
|
|
||||||
float ledc_max_frequency_for_bit_depth(uint8_t bit_depth) {
|
float ledc_max_frequency_for_bit_depth(uint8_t bit_depth) {
|
||||||
return static_cast<float>(CLOCK_FREQUENCY) / static_cast<float>(1 << bit_depth);
|
return static_cast<float>(CLOCK_FREQUENCY) / static_cast<float>(1 << bit_depth);
|
||||||
@@ -68,7 +54,6 @@ optional<uint8_t> ledc_bit_depth_for_frequency(float frequency) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_ESP_IDF
|
|
||||||
esp_err_t configure_timer_frequency(ledc_mode_t speed_mode, ledc_timer_t timer_num, ledc_channel_t chan_num,
|
esp_err_t configure_timer_frequency(ledc_mode_t speed_mode, ledc_timer_t timer_num, ledc_channel_t chan_num,
|
||||||
uint8_t channel, uint8_t &bit_depth, float frequency) {
|
uint8_t channel, uint8_t &bit_depth, float frequency) {
|
||||||
bit_depth = *ledc_bit_depth_for_frequency(frequency);
|
bit_depth = *ledc_bit_depth_for_frequency(frequency);
|
||||||
@@ -98,13 +83,10 @@ esp_err_t configure_timer_frequency(ledc_mode_t speed_mode, ledc_timer_t timer_n
|
|||||||
|
|
||||||
return init_result;
|
return init_result;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_ESP_IDF
|
|
||||||
constexpr int ledc_angle_to_htop(float angle, uint8_t bit_depth) {
|
constexpr int ledc_angle_to_htop(float angle, uint8_t bit_depth) {
|
||||||
return static_cast<int>(angle * ((1U << bit_depth) - 1) / 360.0f);
|
return static_cast<int>(angle * ((1U << bit_depth) - 1) / 360.0f);
|
||||||
}
|
}
|
||||||
#endif // USE_ESP_IDF
|
|
||||||
|
|
||||||
void LEDCOutput::write_state(float state) {
|
void LEDCOutput::write_state(float state) {
|
||||||
if (!this->initialized_) {
|
if (!this->initialized_) {
|
||||||
@@ -120,10 +102,6 @@ void LEDCOutput::write_state(float state) {
|
|||||||
const float duty_rounded = roundf(state * max_duty);
|
const float duty_rounded = roundf(state * max_duty);
|
||||||
auto duty = static_cast<uint32_t>(duty_rounded);
|
auto duty = static_cast<uint32_t>(duty_rounded);
|
||||||
ESP_LOGV(TAG, "Setting duty: %" PRIu32 " on channel %u", duty, this->channel_);
|
ESP_LOGV(TAG, "Setting duty: %" PRIu32 " on channel %u", duty, this->channel_);
|
||||||
#ifdef USE_ARDUINO
|
|
||||||
ledcWrite(this->channel_, duty);
|
|
||||||
#endif
|
|
||||||
#ifdef USE_ESP_IDF
|
|
||||||
auto speed_mode = get_speed_mode(this->channel_);
|
auto speed_mode = get_speed_mode(this->channel_);
|
||||||
auto chan_num = static_cast<ledc_channel_t>(this->channel_ % 8);
|
auto chan_num = static_cast<ledc_channel_t>(this->channel_ % 8);
|
||||||
int hpoint = ledc_angle_to_htop(this->phase_angle_, this->bit_depth_);
|
int hpoint = ledc_angle_to_htop(this->phase_angle_, this->bit_depth_);
|
||||||
@@ -135,18 +113,10 @@ void LEDCOutput::write_state(float state) {
|
|||||||
ledc_set_duty_with_hpoint(speed_mode, chan_num, duty, hpoint);
|
ledc_set_duty_with_hpoint(speed_mode, chan_num, duty, hpoint);
|
||||||
ledc_update_duty(speed_mode, chan_num);
|
ledc_update_duty(speed_mode, chan_num);
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LEDCOutput::setup() {
|
void LEDCOutput::setup() {
|
||||||
ESP_LOGCONFIG(TAG, "Running setup");
|
ESP_LOGCONFIG(TAG, "Running setup");
|
||||||
#ifdef USE_ARDUINO
|
|
||||||
this->update_frequency(this->frequency_);
|
|
||||||
this->turn_off();
|
|
||||||
// Attach pin after setting default value
|
|
||||||
ledcAttachPin(this->pin_->get_pin(), this->channel_);
|
|
||||||
#endif
|
|
||||||
#ifdef USE_ESP_IDF
|
|
||||||
auto speed_mode = get_speed_mode(this->channel_);
|
auto speed_mode = get_speed_mode(this->channel_);
|
||||||
auto timer_num = static_cast<ledc_timer_t>((this->channel_ % 8) / 2);
|
auto timer_num = static_cast<ledc_timer_t>((this->channel_ % 8) / 2);
|
||||||
auto chan_num = static_cast<ledc_channel_t>(this->channel_ % 8);
|
auto chan_num = static_cast<ledc_channel_t>(this->channel_ % 8);
|
||||||
@@ -175,7 +145,6 @@ void LEDCOutput::setup() {
|
|||||||
ledc_channel_config(&chan_conf);
|
ledc_channel_config(&chan_conf);
|
||||||
this->initialized_ = true;
|
this->initialized_ = true;
|
||||||
this->status_clear_error();
|
this->status_clear_error();
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LEDCOutput::dump_config() {
|
void LEDCOutput::dump_config() {
|
||||||
@@ -208,38 +177,7 @@ void LEDCOutput::update_frequency(float frequency) {
|
|||||||
}
|
}
|
||||||
this->bit_depth_ = bit_depth_opt.value_or(8);
|
this->bit_depth_ = bit_depth_opt.value_or(8);
|
||||||
this->frequency_ = frequency;
|
this->frequency_ = frequency;
|
||||||
#ifdef USE_ARDUINO
|
|
||||||
ESP_LOGV(TAG, "Using Arduino API - Trying to define channel, frequency and bit depth");
|
|
||||||
u_int32_t configured_frequency = 0;
|
|
||||||
|
|
||||||
// Configure LEDC channel, frequency and bit depth with fallback
|
|
||||||
int attempt_count_max = SETUP_ATTEMPT_COUNT_MAX;
|
|
||||||
while (attempt_count_max > 0 && configured_frequency == 0) {
|
|
||||||
ESP_LOGV(TAG, "Initializing channel %u with frequency %.1f and bit depth of %u", this->channel_, this->frequency_,
|
|
||||||
this->bit_depth_);
|
|
||||||
configured_frequency = ledcSetup(this->channel_, frequency, this->bit_depth_);
|
|
||||||
if (configured_frequency != 0) {
|
|
||||||
this->initialized_ = true;
|
|
||||||
this->status_clear_error();
|
|
||||||
ESP_LOGV(TAG, "Configured frequency: %u with bit depth: %u", configured_frequency, this->bit_depth_);
|
|
||||||
} else {
|
|
||||||
ESP_LOGW(TAG, "Unable to initialize channel %u with frequency %.1f and bit depth of %u", this->channel_,
|
|
||||||
this->frequency_, this->bit_depth_);
|
|
||||||
// try again with a lower bit depth
|
|
||||||
this->bit_depth_--;
|
|
||||||
}
|
|
||||||
attempt_count_max--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (configured_frequency == 0) {
|
|
||||||
ESP_LOGE(TAG, "Permanently failed to initialize channel %u with frequency %.1f and bit depth of %u", this->channel_,
|
|
||||||
this->frequency_, this->bit_depth_);
|
|
||||||
this->status_set_error();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // USE_ARDUINO
|
|
||||||
#ifdef USE_ESP_IDF
|
|
||||||
if (!this->initialized_) {
|
if (!this->initialized_) {
|
||||||
ESP_LOGW(TAG, "Not yet initialized");
|
ESP_LOGW(TAG, "Not yet initialized");
|
||||||
return;
|
return;
|
||||||
@@ -259,7 +197,7 @@ void LEDCOutput::update_frequency(float frequency) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this->status_clear_error();
|
this->status_clear_error();
|
||||||
#endif
|
|
||||||
// re-apply duty
|
// re-apply duty
|
||||||
this->write_state(this->duty_);
|
this->write_state(this->duty_);
|
||||||
}
|
}
|
||||||
|
@@ -173,9 +173,9 @@ def _notify_old_style(config):
|
|||||||
|
|
||||||
# The dev and latest branches will be at *least* this version, which is what matters.
|
# The dev and latest branches will be at *least* this version, which is what matters.
|
||||||
ARDUINO_VERSIONS = {
|
ARDUINO_VERSIONS = {
|
||||||
"dev": (cv.Version(1, 7, 0), "https://github.com/libretiny-eu/libretiny.git"),
|
"dev": (cv.Version(1, 9, 1), "https://github.com/libretiny-eu/libretiny.git"),
|
||||||
"latest": (cv.Version(1, 7, 0), "libretiny"),
|
"latest": (cv.Version(1, 9, 1), "libretiny"),
|
||||||
"recommended": (cv.Version(1, 7, 0), None),
|
"recommended": (cv.Version(1, 9, 1), None),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -264,6 +264,7 @@ async def component_to_code(config):
|
|||||||
# force using arduino framework
|
# force using arduino framework
|
||||||
cg.add_platformio_option("framework", "arduino")
|
cg.add_platformio_option("framework", "arduino")
|
||||||
cg.add_build_flag("-DUSE_ARDUINO")
|
cg.add_build_flag("-DUSE_ARDUINO")
|
||||||
|
cg.set_cpp_standard("gnu++17")
|
||||||
|
|
||||||
# disable library compatibility checks
|
# disable library compatibility checks
|
||||||
cg.add_platformio_option("lib_ldf_mode", "off")
|
cg.add_platformio_option("lib_ldf_mode", "off")
|
||||||
|
@@ -17,7 +17,7 @@ namespace light {
|
|||||||
|
|
||||||
class LightOutput;
|
class LightOutput;
|
||||||
|
|
||||||
enum LightRestoreMode {
|
enum LightRestoreMode : uint8_t {
|
||||||
LIGHT_RESTORE_DEFAULT_OFF,
|
LIGHT_RESTORE_DEFAULT_OFF,
|
||||||
LIGHT_RESTORE_DEFAULT_ON,
|
LIGHT_RESTORE_DEFAULT_ON,
|
||||||
LIGHT_ALWAYS_OFF,
|
LIGHT_ALWAYS_OFF,
|
||||||
@@ -212,12 +212,18 @@ class LightState : public EntityBase, public Component {
|
|||||||
|
|
||||||
/// Store the output to allow effects to have more access.
|
/// Store the output to allow effects to have more access.
|
||||||
LightOutput *output_;
|
LightOutput *output_;
|
||||||
/// Value for storing the index of the currently active effect. 0 if no effect is active
|
|
||||||
uint32_t active_effect_index_{};
|
|
||||||
/// The currently active transformer for this light (transition/flash).
|
/// The currently active transformer for this light (transition/flash).
|
||||||
std::unique_ptr<LightTransformer> transformer_{nullptr};
|
std::unique_ptr<LightTransformer> transformer_{nullptr};
|
||||||
/// Whether the light value should be written in the next cycle.
|
/// List of effects for this light.
|
||||||
bool next_write_{true};
|
std::vector<LightEffect *> effects_;
|
||||||
|
/// Value for storing the index of the currently active effect. 0 if no effect is active
|
||||||
|
uint32_t active_effect_index_{};
|
||||||
|
/// Default transition length for all transitions in ms.
|
||||||
|
uint32_t default_transition_length_{};
|
||||||
|
/// Transition length to use for flash transitions.
|
||||||
|
uint32_t flash_transition_length_{};
|
||||||
|
/// Gamma correction factor for the light.
|
||||||
|
float gamma_correct_{};
|
||||||
|
|
||||||
/// Object used to store the persisted values of the light.
|
/// Object used to store the persisted values of the light.
|
||||||
ESPPreferenceObject rtc_;
|
ESPPreferenceObject rtc_;
|
||||||
@@ -236,19 +242,13 @@ class LightState : public EntityBase, public Component {
|
|||||||
*/
|
*/
|
||||||
CallbackManager<void()> target_state_reached_callback_{};
|
CallbackManager<void()> target_state_reached_callback_{};
|
||||||
|
|
||||||
/// Default transition length for all transitions in ms.
|
|
||||||
uint32_t default_transition_length_{};
|
|
||||||
/// Transition length to use for flash transitions.
|
|
||||||
uint32_t flash_transition_length_{};
|
|
||||||
/// Gamma correction factor for the light.
|
|
||||||
float gamma_correct_{};
|
|
||||||
/// Restore mode of the light.
|
|
||||||
LightRestoreMode restore_mode_;
|
|
||||||
/// Initial state of the light.
|
/// Initial state of the light.
|
||||||
optional<LightStateRTCState> initial_state_{};
|
optional<LightStateRTCState> initial_state_{};
|
||||||
/// List of effects for this light.
|
|
||||||
std::vector<LightEffect *> effects_;
|
|
||||||
|
|
||||||
|
/// Restore mode of the light.
|
||||||
|
LightRestoreMode restore_mode_;
|
||||||
|
/// Whether the light value should be written in the next cycle.
|
||||||
|
bool next_write_{true};
|
||||||
// for effects, true if a transformer (transition) is active.
|
// for effects, true if a transformer (transition) is active.
|
||||||
bool is_transformer_active_ = false;
|
bool is_transformer_active_ = false;
|
||||||
};
|
};
|
||||||
|
@@ -324,7 +324,10 @@ async def to_code(config):
|
|||||||
if CORE.using_arduino:
|
if CORE.using_arduino:
|
||||||
if config[CONF_HARDWARE_UART] == USB_CDC:
|
if config[CONF_HARDWARE_UART] == USB_CDC:
|
||||||
cg.add_build_flag("-DARDUINO_USB_CDC_ON_BOOT=1")
|
cg.add_build_flag("-DARDUINO_USB_CDC_ON_BOOT=1")
|
||||||
if CORE.is_esp32 and get_esp32_variant() == VARIANT_ESP32C3:
|
if CORE.is_esp32 and get_esp32_variant() in (
|
||||||
|
VARIANT_ESP32C3,
|
||||||
|
VARIANT_ESP32C6,
|
||||||
|
):
|
||||||
cg.add_build_flag("-DARDUINO_USB_MODE=1")
|
cg.add_build_flag("-DARDUINO_USB_MODE=1")
|
||||||
|
|
||||||
if CORE.using_esp_idf:
|
if CORE.using_esp_idf:
|
||||||
|
@@ -116,7 +116,7 @@ void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStr
|
|||||||
if (this->baud_rate_ > 0) {
|
if (this->baud_rate_ > 0) {
|
||||||
this->write_msg_(this->tx_buffer_ + msg_start);
|
this->write_msg_(this->tx_buffer_ + msg_start);
|
||||||
}
|
}
|
||||||
this->call_log_callbacks_(level, tag, this->tx_buffer_ + msg_start);
|
this->log_callback_.call(level, tag, this->tx_buffer_ + msg_start);
|
||||||
|
|
||||||
global_recursion_guard_ = false;
|
global_recursion_guard_ = false;
|
||||||
}
|
}
|
||||||
@@ -129,19 +129,6 @@ inline int Logger::level_for(const char *tag) {
|
|||||||
return this->current_level_;
|
return this->current_level_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HOT Logger::call_log_callbacks_(int level, const char *tag, const char *msg) {
|
|
||||||
#ifdef USE_ESP32
|
|
||||||
// Suppress network-logging if memory constrained
|
|
||||||
// In some configurations (eg BLE enabled) there may be some transient
|
|
||||||
// memory exhaustion, and trying to log when OOM can lead to a crash. Skipping
|
|
||||||
// here usually allows the stack to recover instead.
|
|
||||||
// See issue #1234 for analysis.
|
|
||||||
if (xPortGetFreeHeapSize() < 2048)
|
|
||||||
return;
|
|
||||||
#endif
|
|
||||||
this->log_callback_.call(level, tag, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate), tx_buffer_size_(tx_buffer_size) {
|
Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate), tx_buffer_size_(tx_buffer_size) {
|
||||||
// add 1 to buffer size for null terminator
|
// add 1 to buffer size for null terminator
|
||||||
this->tx_buffer_ = new char[this->tx_buffer_size_ + 1]; // NOLINT
|
this->tx_buffer_ = new char[this->tx_buffer_size_ + 1]; // NOLINT
|
||||||
@@ -189,7 +176,7 @@ void Logger::loop() {
|
|||||||
this->tx_buffer_size_);
|
this->tx_buffer_size_);
|
||||||
this->write_footer_to_buffer_(this->tx_buffer_, &this->tx_buffer_at_, this->tx_buffer_size_);
|
this->write_footer_to_buffer_(this->tx_buffer_, &this->tx_buffer_at_, this->tx_buffer_size_);
|
||||||
this->tx_buffer_[this->tx_buffer_at_] = '\0';
|
this->tx_buffer_[this->tx_buffer_at_] = '\0';
|
||||||
this->call_log_callbacks_(message->level, message->tag, this->tx_buffer_);
|
this->log_callback_.call(message->level, message->tag, this->tx_buffer_);
|
||||||
// At this point all the data we need from message has been transferred to the tx_buffer
|
// At this point all the data we need from message has been transferred to the tx_buffer
|
||||||
// so we can release the message to allow other tasks to use it as soon as possible.
|
// so we can release the message to allow other tasks to use it as soon as possible.
|
||||||
this->log_buffer_->release_message_main_loop(received_token);
|
this->log_buffer_->release_message_main_loop(received_token);
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user