Compare commits

..

41 Commits

Author SHA1 Message Date
Jesse Hills 7984349c36 Merge branch 'release' into dev 2026-06-29 22:32:34 +12:00
Jesse Hills 0cbbd64577 Merge pull request #17223 from esphome/bump-2026.6.3
2026.6.3
2026-06-29 22:31:44 +12:00
Jesse Hills a618ee11b4 Bump version to 2026.6.3 2026-06-29 20:30:24 +12:00
Tom 6251c26cc6 [espnow] Fix espnow crash when send() is called without a callback (#17266) 2026-06-29 20:30:24 +12:00
Jonathan Swoboda 4fbe0d87ec [wifi] Fix crash when WiFi is enabled late alongside ESP-NOW (#17239) 2026-06-29 20:30:24 +12:00
esphome[bot] 24d8e99c50 Bump bundled esphome-device-builder to 1.0.21 (#17257)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-06-29 20:30:24 +12:00
Jonathan Swoboda 14b6a0ede1 [espnow] Don't throttle ESP-NOW RX when deep_sleep is present (#17240) 2026-06-29 20:30:24 +12:00
Jonathan Swoboda 1793ca5eac [core] Suppress unactionable legacy-redaction warning for substitutions (#17242) 2026-06-29 20:30:24 +12:00
esphome[bot] 62e19bcb27 Bump bundled esphome-device-builder to 1.0.20 (#17244) 2026-06-29 20:30:24 +12:00
Franck Nijhof 84d1c34c28 [core] Fix area saved as null in storage.json (#17219)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-29 20:30:24 +12:00
esphome[bot] f78cbf9200 Bump bundled esphome-device-builder to 1.0.19 (#17217)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-06-29 20:30:23 +12:00
esphome[bot] eb711381d3 Bump bundled esphome-device-builder to 1.0.18 (#17212)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-06-29 20:30:23 +12:00
Jonathan Swoboda 9a1daa5247 [hbridge] Fix light stuck on one polarity (#17162) 2026-06-29 20:30:18 +12:00
Clyde Stubbs f3d61ca3e1 [mipi][mipi_spi] Swap native dimensions for swap_xy hardware transform (#17201)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 20:29:44 +12:00
Jonathan Swoboda 29dfd820c6 [wifi] Report STA IP, not SoftAP IP, in wifi_info on ESP8266 (#17185) 2026-06-29 20:29:44 +12:00
Jonathan Swoboda 8bc5b97298 [network] Set IPv4 type tag on all lwIP platforms, not just esp32 (#17200) 2026-06-29 20:29:44 +12:00
Jonathan Swoboda 7a64163c4f [esp32] Accept '#' as ESP-IDF source ref separator (#17193) 2026-06-29 20:29:44 +12:00
esphome[bot] dfe14f9c3a Bump bundled esphome-device-builder to 1.0.17 (#17199) 2026-06-29 20:29:44 +12:00
esphome[bot] 26cf373ae7 Bump bundled esphome-device-builder to 1.0.16 (#17182) 2026-06-29 20:29:44 +12:00
Geoffrey Frogeye 94ccddf176 [opentherm] Support power scaling disabled (#17183)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-06-29 20:29:44 +12:00
Clyde Stubbs 2ec24505d0 [mipi_spi] Suppress sequence errors when page selection used (#17176) 2026-06-29 20:29:44 +12:00
esphome[bot] 4f7faa7712 Bump bundled esphome-device-builder to 1.0.15 (#17170) 2026-06-29 20:29:44 +12:00
Clyde Stubbs b3dcaac262 [mipi_spi] Warn on MODE3 default for display without CS pin (#17153) 2026-06-29 20:29:44 +12:00
mnewton25 ee118d384a [esp32] Use POSIX path for secure-boot signing/verification keys Fixes #17164 (#17166)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-29 20:29:44 +12:00
Jonathan Swoboda 8d36167e11 [esp32_ble_server] Fix set_value action with by-reference triggers (#17156) 2026-06-29 20:29:44 +12:00
esphome[bot] 6d559a32df Bump bundled esphome-device-builder to 1.0.14 (#17139) 2026-06-29 20:29:37 +12:00
Jonathan Swoboda bf0d31b3ab [espidf] Don't fail framework check on broken unrelated PATH tools (#17053) 2026-06-29 20:23:23 +12:00
dependabot[bot] d8ffb732b7 Bump zeroconf from 0.149.16 to 0.150.0 (#17137)
Signed-off-by: dependabot[bot] <support@github.com>
2026-06-29 20:23:23 +12:00
Tom 9e8261056c [espnow] Fix espnow crash when send() is called without a callback (#17266) 2026-06-28 23:06:12 -04:00
Clyde Stubbs 5f311d281e [esphome] Warn when a YAML merge (<<:) drops a key (#17246)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-29 07:17:01 +10:00
J. Nick Koston a336ad6732 [mcp4725] Use constexpr bit shift instead of powf for full-scale value (#17261) 2026-06-28 17:07:51 -04:00
Bonne Eggleston 8434d54cc7 [modbus] Reinstate turnaround delay after broadcasts (Revert #17209) (#17263)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
2026-06-28 17:07:25 -04:00
Jonathan Swoboda b62f7a41c9 [multiple] Single-precision float math, avoid double promotion (stragglers) (#17260) 2026-06-28 16:15:38 -04:00
Bonne Eggleston 4ebecf514a [modbus_server] Simplify server response handling (#12376)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
Co-authored-by: J. Nick Koston <nick@koston.org>
2026-06-28 19:41:47 +00:00
Jonathan Swoboda 2f32c88ae5 [wifi] Fix crash when WiFi is enabled late alongside ESP-NOW (#17239) 2026-06-28 15:16:42 -04:00
Jonathan Swoboda 95449068e7 [multiple] Single-precision float math, avoid double promotion (batch 4/4) (#17256) 2026-06-28 14:18:41 -04:00
Jonathan Swoboda 556def78aa [multiple] Single-precision float math, avoid double promotion (batch 3/4) (#17255) 2026-06-28 14:18:30 -04:00
Jonathan Swoboda b7803cf9b5 [multiple] Single-precision float math, avoid double promotion (batch 2/4) (#17254) 2026-06-28 14:18:20 -04:00
Jonathan Swoboda 40820287f1 [multiple] Single-precision float math, avoid double promotion (batch 1/4) (#17253) 2026-06-28 14:18:13 -04:00
Jonathan Swoboda 6210dfb4d0 [core] Use single-precision float math to avoid double promotion (#17252) 2026-06-28 14:18:01 -04:00
esphome[bot] 45c712b17b Bump bundled esphome-device-builder to 1.0.21 (#17257)
Co-authored-by: esphome[bot] <115708604+esphome[bot]@users.noreply.github.com>
2026-06-28 09:41:49 -07:00
188 changed files with 1293 additions and 878 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.12"
python-version: "3.11"
- name: Set up uv
# ``--system`` (below) installs into the setup-python interpreter;
# no venv is created or restored by this workflow.
+2 -2
View File
@@ -65,7 +65,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.12"
python-version: "3.11"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
@@ -149,7 +149,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.12"
python-version: "3.11"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
@@ -60,7 +60,7 @@ jobs:
if: steps.pr.outputs.skip != 'true'
uses: ./.github/actions/restore-python
with:
python-version: "3.12"
python-version: "3.11"
cache-key: ${{ hashFiles('.cache-key') }}
- name: Download memory analysis artifacts
+2 -2
View File
@@ -12,7 +12,7 @@ permissions:
contents: read # actions/checkout for all jobs; individual jobs add their own scopes when they need to write
env:
DEFAULT_PYTHON: "3.12"
DEFAULT_PYTHON: "3.11"
PYUPGRADE_TARGET: "--py311-plus"
concurrency:
@@ -203,7 +203,7 @@ jobs:
fail-fast: false
matrix:
python-version:
- "3.12"
- "3.11"
- "3.13"
- "3.14"
os:
+1 -1
View File
@@ -96,7 +96,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.12"
python-version: "3.11"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
+1 -1
View File
@@ -22,7 +22,7 @@ RUN \
-r /requirements.txt
# Install the ESPHome Device Builder dashboard.
RUN uv pip install --no-cache-dir esphome-device-builder==1.0.20
RUN uv pip install --no-cache-dir esphome-device-builder==1.0.21
RUN \
platformio settings set enable_telemetry No \
-12
View File
@@ -1,12 +0,0 @@
"""ESPHome.
ESPHome validates configuration with probatio. External (third-party) components
still ``import voluptuous`` directly, so probatio's compatibility shim is installed
here, at package import, before any submodule or component imports voluptuous. This
aliases ``voluptuous`` (and the submodules dependencies reach into) to probatio in
``sys.modules`` for the lifetime of the process.
"""
from probatio.compat import install_as_voluptuous
install_as_voluptuous()
+3 -9
View File
@@ -198,13 +198,7 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False):
try:
return cv.Schema([schema])(value)
except cv.Invalid as err2:
err2_errors = (
err2.errors if isinstance(err2, cv.MultipleInvalid) else [err2]
)
if (
any(isinstance(e, cv.ExtraKeysInvalid) for e in err2_errors)
and len(err2.path) == 2
):
if "extra keys not allowed" in str(err2) and len(err2.path) == 2:
raise err from None
if "Unable to find action" in str(err):
raise err2 from None
@@ -420,8 +414,8 @@ async def delay_action_to_code(
cv.Optional(CONF_THEN): validate_action_list,
cv.Optional(CONF_ELSE): validate_action_list,
},
cv.AtLeastOne(CONF_THEN, CONF_ELSE),
cv.AtLeastOne(CONF_CONDITION, CONF_ANY, CONF_ALL),
cv.has_at_least_one_key(CONF_THEN, CONF_ELSE),
cv.has_at_least_one_key(CONF_CONDITION, CONF_ANY, CONF_ALL),
),
synchronous=True,
)
+1 -1
View File
@@ -25,7 +25,7 @@ void A01nyubComponent::check_buffer_() {
if (this->buffer_[3] == checksum) {
float distance = (this->buffer_[1] << 8) + this->buffer_[2];
if (distance > 280) {
float meters = distance / 1000.0;
float meters = distance / 1000.0f;
ESP_LOGV(TAG, "Distance from sensor: %f mm, %f m", distance, meters);
this->publish_state(meters);
} else {
+1 -1
View File
@@ -216,7 +216,7 @@ void AcDimmer::setup() {
}
void AcDimmer::write_state(float state) {
state = std::acos(1 - (2 * state)) / std::numbers::pi; // RMS power compensation
state = std::acos(1 - (2 * state)) / std::numbers::pi_v<float>; // RMS power compensation
auto new_value = static_cast<uint16_t>(roundf(state * 65535));
if (new_value != 0 && this->store_.value == 0)
this->store_.init_cycle = this->init_with_half_cycle_;
@@ -34,7 +34,7 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_PIXEL_MAPPER): cv.returning_lambda,
}
),
cv.AtMostOne(CONF_PAGES, CONF_LAMBDA),
cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA),
)
+5 -5
View File
@@ -114,13 +114,13 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
this->decoder_->decode(param->notify.value, param->notify.value_len);
if (this->decoder_->has_position()) {
this->position = ((float) this->decoder_->position_ / 100.0);
this->position = ((float) this->decoder_->position_ / 100.0f);
if (!this->invert_position_)
this->position = 1 - this->position;
if (this->position > 0.97)
this->position = 1.0;
if (this->position < 0.02)
this->position = 0.0;
if (this->position > 0.97f)
this->position = 1.0f;
if (this->position < 0.02f)
this->position = 0.0f;
this->publish_state();
}
+2 -2
View File
@@ -6,9 +6,9 @@
namespace esphome::anova {
float ftoc(float f) { return (f - 32.0) * (5.0f / 9.0f); }
float ftoc(float f) { return (f - 32.0f) * (5.0f / 9.0f); }
float ctof(float c) { return (c * 9.0f / 5.0f) + 32.0; }
float ctof(float c) { return (c * 9.0f / 5.0f) + 32.0f; }
AnovaPacket *AnovaCodec::clean_packet_() {
this->packet_.length = strlen((char *) this->packet_.data);
+2 -2
View File
@@ -221,7 +221,7 @@ ACTIONS_SCHEMA = automation.validate_automation(
),
},
cv.All(
cv.ExactlyOne(CONF_SERVICE, CONF_ACTION),
cv.has_exactly_one_key(CONF_SERVICE, CONF_ACTION),
cv.rename_key(CONF_SERVICE, CONF_ACTION),
_auto_detect_supports_response,
# Re-validate supports_response after auto-detection sets it
@@ -534,7 +534,7 @@ HOMEASSISTANT_ACTION_ACTION_SCHEMA = cv.All(
cv.Optional(CONF_ON_ERROR): automation.validate_automation(single=True),
}
),
cv.ExactlyOne(CONF_SERVICE, CONF_ACTION),
cv.has_exactly_one_key(CONF_SERVICE, CONF_ACTION),
cv.rename_key(CONF_SERVICE, CONF_ACTION),
_validate_response_config,
)
+1 -1
View File
@@ -198,7 +198,7 @@ CONFIG_SCHEMA = cv.All(
.extend(cv.COMPONENT_SCHEMA)
.extend(i2c.i2c_device_schema(0x36)),
# ensure end_position and range are mutually exclusive
cv.AtMostOne(CONF_END_POSITION, CONF_RANGE),
cv.has_at_most_one_key(CONF_END_POSITION, CONF_RANGE),
has_valid_range_config(),
)
+1 -1
View File
@@ -149,7 +149,7 @@ RADAR_SETTINGS_SCHEMA = cv.Schema(
),
}
).add_extra(
cv.AtLeastOne(
cv.has_at_least_one_key(
CONF_HW_FRONTEND_RESET,
CONF_FREQUENCY,
CONF_SENSING_DISTANCE,
+1 -1
View File
@@ -37,7 +37,7 @@ CONFIG_SCHEMA = cv.All(
}
)
.extend(cv.COMPONENT_SCHEMA),
cv.AtLeastOne(CONF_COOL_ACTION, CONF_HEAT_ACTION),
cv.has_at_least_one_key(CONF_COOL_ACTION, CONF_HEAT_ACTION),
)
@@ -112,7 +112,7 @@ float BinarySensorMap::bayesian_predicate_(bool sensor_state, float prior, float
prob_state_source_false = 1 - prob_given_false;
}
return prob_state_source_true / (prior * prob_state_source_true + (1.0 - prior) * prob_state_source_false);
return prob_state_source_true / (prior * prob_state_source_true + (1.0f - prior) * prob_state_source_false);
}
void BinarySensorMap::add_channel(binary_sensor::BinarySensor *sensor, float value) {
+1 -1
View File
@@ -205,7 +205,7 @@ void BL0906::read_data_(const uint8_t address, const float reference, sensor::Se
// Chip temperature
if (reference == BL0906_TREF) {
value = (float) to_int32_t(data_s24);
value = (value - 64) * 12.5 / 59 - 40;
value = (value - 64) * 12.5f / 59 - 40;
}
sensor->publish_state(value);
}
+1 -1
View File
@@ -120,7 +120,7 @@ float BL0940::calculate_power_reference_() {
float BL0940::calculate_energy_reference_() {
// formula: 3600000 * 4046 * RL * R1 * 1000 / (1638.4 * 256) / Vref² / (R1 + R2)
// or: power_reference_ * 3600000 / (1638.4 * 256)
return this->power_reference_cal_ * 3600000 / (1638.4 * 256);
return this->power_reference_cal_ * 3600000 / (1638.4f * 256);
}
float BL0940::calculate_calibration_value_(float state) { return (100 + state) / 100; }
+2 -2
View File
@@ -124,14 +124,14 @@ void BL0942::setup() {
// If either current or voltage references are set explicitly by the user,
// calculate the power reference from it unless that is also explicitly set.
if ((this->current_reference_set_ || this->voltage_reference_set_) && !this->power_reference_set_) {
this->power_reference_ = (this->voltage_reference_ * this->current_reference_ * 3537.0 / 305978.0) / 73989.0;
this->power_reference_ = (this->voltage_reference_ * this->current_reference_ * 3537.0f / 305978.0f) / 73989.0f;
this->power_reference_set_ = true;
}
// Similarly for energy reference, if the power reference was set by the user
// either implicitly or explicitly.
if (this->power_reference_set_ && !this->energy_reference_set_) {
this->energy_reference_ = this->power_reference_ * 3600000 / 419430.4;
this->energy_reference_ = this->power_reference_ * 3600000 / 419430.4f;
this->energy_reference_set_ = true;
}
@@ -50,7 +50,9 @@ CONFIG_SCHEMA = cv.All(
)
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
.extend(cv.COMPONENT_SCHEMA),
cv.ExactlyOne(CONF_MAC_ADDRESS, CONF_IRK, CONF_SERVICE_UUID, CONF_IBEACON_UUID),
cv.has_exactly_one_key(
CONF_MAC_ADDRESS, CONF_IRK, CONF_SERVICE_UUID, CONF_IBEACON_UUID
),
_validate,
)
+3 -1
View File
@@ -50,7 +50,9 @@ CONFIG_SCHEMA = cv.All(
)
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
.extend(cv.COMPONENT_SCHEMA),
cv.ExactlyOne(CONF_MAC_ADDRESS, CONF_IRK, CONF_SERVICE_UUID, CONF_IBEACON_UUID),
cv.has_exactly_one_key(
CONF_MAC_ADDRESS, CONF_IRK, CONF_SERVICE_UUID, CONF_IBEACON_UUID
),
_validate,
)
+1 -1
View File
@@ -115,7 +115,7 @@ CONFIG_SCHEMA = (
),
}
),
cv.AtLeastOne(CONF_TEMPERATURE, CONF_DURATION),
cv.has_at_least_one_key(CONF_TEMPERATURE, CONF_DURATION),
),
),
}
@@ -204,7 +204,7 @@ void MedianCombinationComponent::handle_new_value(float value) {
median = sensor_states[sensor_states_size / 2];
} else {
// Even number of measurements, use the average of the two middle measurements
median = (sensor_states[sensor_states_size / 2] + sensor_states[sensor_states_size / 2 - 1]) / 2.0;
median = (sensor_states[sensor_states_size / 2] + sensor_states[sensor_states_size / 2 - 1]) / 2.0f;
}
}
@@ -39,7 +39,7 @@ void CurrentBasedCover::control(const CoverCall &call) {
auto opt_pos = call.get_position();
if (opt_pos.has_value()) {
auto pos = *opt_pos;
if (fabsf(this->position - pos) < 0.01) {
if (fabsf(this->position - pos) < 0.01f) {
// already at target
} else {
auto op = pos < this->position ? COVER_OPERATION_CLOSING : COVER_OPERATION_OPENING;
+1 -1
View File
@@ -24,7 +24,7 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_CONSTANT_BRIGHTNESS, default=False): cv.boolean,
}
),
cv.AllOrNone(
cv.has_none_or_all_keys(
[CONF_COLD_WHITE_COLOR_TEMPERATURE, CONF_WARM_WHITE_COLOR_TEMPERATURE]
),
light.validate_color_temperature_channels,
+1 -1
View File
@@ -151,7 +151,7 @@ uint8_t DaikinBrcClimate::temperature_() {
// Temperature in remote is in F
if (this->fahrenheit_) {
temperature = (uint8_t) roundf(
clamp<float>(((this->target_temperature * 1.8) + 32), DAIKIN_BRC_TEMP_MIN_F, DAIKIN_BRC_TEMP_MAX_F));
clamp<float>(((this->target_temperature * 1.8f) + 32), DAIKIN_BRC_TEMP_MIN_F, DAIKIN_BRC_TEMP_MAX_F));
} else {
temperature = ((uint8_t) roundf(this->target_temperature) - 9) << 1;
}
@@ -138,7 +138,7 @@ float DallasTemperatureSensor::get_temp_c_() {
if (this->scratch_pad_[7] == 0) {
return NAN;
}
return (temp >> 1) + (this->scratch_pad_[7] - this->scratch_pad_[6]) / float(this->scratch_pad_[7]) - 0.25;
return (temp >> 1) + (this->scratch_pad_[7] - this->scratch_pad_[6]) / float(this->scratch_pad_[7]) - 0.25f;
}
switch (this->resolution_) {
case 9:
+1 -1
View File
@@ -406,7 +406,7 @@ DEEP_SLEEP_ENTER_SCHEMA = cv.All(
)
)
),
cv.AllOrNone(CONF_UNTIL, CONF_TIME_ID),
cv.has_none_or_all_keys(CONF_UNTIL, CONF_TIME_ID),
)
+1 -1
View File
@@ -15,7 +15,7 @@ class DemoSensor final : public sensor::Sensor, public PollingComponent {
float base = std::isnan(this->state) ? 0.0f : this->state;
this->publish_state(base + val * 10);
} else {
if (val < 0.1) {
if (val < 0.1f) {
this->publish_state(NAN);
} else {
this->publish_state(val * 100);
+1 -1
View File
@@ -9,7 +9,7 @@ namespace esphome::demo {
class DemoSwitch final : public switch_::Switch, public Component {
public:
void setup() override {
bool initial = random_float() < 0.5;
bool initial = random_float() < 0.5f;
this->publish_state(initial);
}
+2 -2
View File
@@ -10,9 +10,9 @@ class DemoTextSensor final : public text_sensor::TextSensor, public PollingCompo
public:
void update() override {
float val = random_float();
if (val < 0.33) {
if (val < 0.33f) {
this->publish_state("foo");
} else if (val < 0.66) {
} else if (val < 0.66f) {
this->publish_state("bar");
} else {
this->publish_state("foobar");
@@ -139,7 +139,7 @@ MMWAVE_SETTINGS_SCHEMA = cv.Schema(
cv.Optional(CONF_SENSITIVITY): cv.templatable(cv.int_range(min=0, max=9)),
}
).add_extra(
cv.AtLeastOne(
cv.has_at_least_one_key(
CONF_FACTORY_RESET,
CONF_DETECTION_SEGMENTS,
CONF_OUTPUT_LATENCY,
+27 -27
View File
@@ -121,51 +121,51 @@ DetRangeCfgCommand::DetRangeCfgCommand(float min1, float max1, float min2, float
this->cmd_ = "detRangeCfg -1 0 0";
} else if (min2 < 0 || max2 < 0) {
this->min1_ = min1 = round(min1 / 0.15) * 0.15;
this->max1_ = max1 = round(max1 / 0.15) * 0.15;
this->min1_ = min1 = roundf(min1 / 0.15f) * 0.15f;
this->max1_ = max1 = roundf(max1 / 0.15f) * 0.15f;
this->min2_ = min2 = this->max2_ = max2 = this->min3_ = min3 = this->max3_ = max3 = this->min4_ = min4 =
this->max4_ = max4 = -1;
char buf[72]; // max 72: "detRangeCfg -1 "(15) + 8 * (float(5) + space(1)) + null
snprintf(buf, sizeof(buf), "detRangeCfg -1 %.0f %.0f", min1 / 0.15, max1 / 0.15);
snprintf(buf, sizeof(buf), "detRangeCfg -1 %.0f %.0f", min1 / 0.15f, max1 / 0.15f);
this->cmd_ = buf;
} else if (min3 < 0 || max3 < 0) {
this->min1_ = min1 = round(min1 / 0.15) * 0.15;
this->max1_ = max1 = round(max1 / 0.15) * 0.15;
this->min2_ = min2 = round(min2 / 0.15) * 0.15;
this->max2_ = max2 = round(max2 / 0.15) * 0.15;
this->min1_ = min1 = roundf(min1 / 0.15f) * 0.15f;
this->max1_ = max1 = roundf(max1 / 0.15f) * 0.15f;
this->min2_ = min2 = roundf(min2 / 0.15f) * 0.15f;
this->max2_ = max2 = roundf(max2 / 0.15f) * 0.15f;
this->min3_ = min3 = this->max3_ = max3 = this->min4_ = min4 = this->max4_ = max4 = -1;
char buf[72]; // max 72: "detRangeCfg -1 "(15) + 8 * (float(5) + space(1)) + null
snprintf(buf, sizeof(buf), "detRangeCfg -1 %.0f %.0f %.0f %.0f", min1 / 0.15, max1 / 0.15, min2 / 0.15,
max2 / 0.15);
snprintf(buf, sizeof(buf), "detRangeCfg -1 %.0f %.0f %.0f %.0f", min1 / 0.15f, max1 / 0.15f, min2 / 0.15f,
max2 / 0.15f);
this->cmd_ = buf;
} else if (min4 < 0 || max4 < 0) {
this->min1_ = min1 = round(min1 / 0.15) * 0.15;
this->max1_ = max1 = round(max1 / 0.15) * 0.15;
this->min2_ = min2 = round(min2 / 0.15) * 0.15;
this->max2_ = max2 = round(max2 / 0.15) * 0.15;
this->min3_ = min3 = round(min3 / 0.15) * 0.15;
this->max3_ = max3 = round(max3 / 0.15) * 0.15;
this->min1_ = min1 = roundf(min1 / 0.15f) * 0.15f;
this->max1_ = max1 = roundf(max1 / 0.15f) * 0.15f;
this->min2_ = min2 = roundf(min2 / 0.15f) * 0.15f;
this->max2_ = max2 = roundf(max2 / 0.15f) * 0.15f;
this->min3_ = min3 = roundf(min3 / 0.15f) * 0.15f;
this->max3_ = max3 = roundf(max3 / 0.15f) * 0.15f;
this->min4_ = min4 = this->max4_ = max4 = -1;
char buf[72]; // max 72: "detRangeCfg -1 "(15) + 8 * (float(5) + space(1)) + null
snprintf(buf, sizeof(buf), "detRangeCfg -1 %.0f %.0f %.0f %.0f %.0f %.0f", min1 / 0.15, max1 / 0.15, min2 / 0.15,
max2 / 0.15, min3 / 0.15, max3 / 0.15);
snprintf(buf, sizeof(buf), "detRangeCfg -1 %.0f %.0f %.0f %.0f %.0f %.0f", min1 / 0.15f, max1 / 0.15f, min2 / 0.15f,
max2 / 0.15f, min3 / 0.15f, max3 / 0.15f);
this->cmd_ = buf;
} else {
this->min1_ = min1 = round(min1 / 0.15) * 0.15;
this->max1_ = max1 = round(max1 / 0.15) * 0.15;
this->min2_ = min2 = round(min2 / 0.15) * 0.15;
this->max2_ = max2 = round(max2 / 0.15) * 0.15;
this->min3_ = min3 = round(min3 / 0.15) * 0.15;
this->max3_ = max3 = round(max3 / 0.15) * 0.15;
this->min4_ = min4 = round(min4 / 0.15) * 0.15;
this->max4_ = max4 = round(max4 / 0.15) * 0.15;
this->min1_ = min1 = roundf(min1 / 0.15f) * 0.15f;
this->max1_ = max1 = roundf(max1 / 0.15f) * 0.15f;
this->min2_ = min2 = roundf(min2 / 0.15f) * 0.15f;
this->max2_ = max2 = roundf(max2 / 0.15f) * 0.15f;
this->min3_ = min3 = roundf(min3 / 0.15f) * 0.15f;
this->max3_ = max3 = roundf(max3 / 0.15f) * 0.15f;
this->min4_ = min4 = roundf(min4 / 0.15f) * 0.15f;
this->max4_ = max4 = roundf(max4 / 0.15f) * 0.15f;
char buf[72]; // max 72: "detRangeCfg -1 "(15) + 8 * (float(5) + space(1)) + null
snprintf(buf, sizeof(buf), "detRangeCfg -1 %.0f %.0f %.0f %.0f %.0f %.0f %.0f %.0f", min1 / 0.15, max1 / 0.15,
min2 / 0.15, max2 / 0.15, min3 / 0.15, max3 / 0.15, min4 / 0.15, max4 / 0.15);
snprintf(buf, sizeof(buf), "detRangeCfg -1 %.0f %.0f %.0f %.0f %.0f %.0f %.0f %.0f", min1 / 0.15f, max1 / 0.15f,
min2 / 0.15f, max2 / 0.15f, min3 / 0.15f, max3 / 0.15f, min4 / 0.15f, max4 / 0.15f);
this->cmd_ = buf;
}
+8 -8
View File
@@ -42,10 +42,10 @@ void Display::line_at_angle(int x, int y, int angle, int length, Color color) {
void Display::line_at_angle(int x, int y, int angle, int start_radius, int stop_radius, Color color) {
// Calculate start and end points
int x1 = (start_radius * cos(angle * M_PI / 180)) + x;
int y1 = (start_radius * sin(angle * M_PI / 180)) + y;
int x2 = (stop_radius * cos(angle * M_PI / 180)) + x;
int y2 = (stop_radius * sin(angle * M_PI / 180)) + y;
int x1 = (start_radius * std::cos(angle * std::numbers::pi_v<float> / 180)) + x;
int y1 = (start_radius * std::sin(angle * std::numbers::pi_v<float> / 180)) + y;
int x2 = (stop_radius * std::cos(angle * std::numbers::pi_v<float> / 180)) + x;
int y2 = (stop_radius * std::sin(angle * std::numbers::pi_v<float> / 180)) + y;
// Draw line
this->line(x1, y1, x2, y2, color);
@@ -228,7 +228,7 @@ void Display::filled_gauge(int center_x, int center_y, int radius1, int radius2,
int e2max, e2min;
progress = std::max(0, std::min(progress, 100)); // 0..100
int draw_progress = progress > 50 ? (100 - progress) : progress;
float tan_a = (progress == 50) ? 65535 : tan(float(draw_progress) * M_PI / 100); // slope
float tan_a = (progress == 50) ? 65535 : tanf(float(draw_progress) * std::numbers::pi_v<float> / 100); // slope
do {
// outer dots
@@ -444,15 +444,15 @@ void HOT Display::get_regular_polygon_vertex(int vertex_id, int *vertex_x, int *
// hence we rotate the shape by 270° to orient the polygon up.
rotation_degrees += ROTATION_270_DEGREES;
// Convert the rotation to radians, easier to use in trigonometrical calculations
float rotation_radians = rotation_degrees * std::numbers::pi / 180;
float rotation_radians = rotation_degrees * std::numbers::pi_v<float> / 180;
// A pointy top variation means the first vertex of the polygon is at the top center of the shape, this requires no
// additional rotation of the shape.
// A flat top variation means the first point of the polygon has to be rotated so that the first edge is horizontal,
// this requires to rotate the shape by π/edges radians counter-clockwise so that the first point is located on the
// left side of the first horizontal edge.
rotation_radians -= (variation == VARIATION_FLAT_TOP) ? std::numbers::pi / edges : 0.0;
rotation_radians -= (variation == VARIATION_FLAT_TOP) ? std::numbers::pi_v<float> / edges : 0.0f;
float vertex_angle = ((float) vertex_id) / edges * 2 * std::numbers::pi + rotation_radians;
float vertex_angle = ((float) vertex_id) / edges * 2 * std::numbers::pi_v<float> + rotation_radians;
*vertex_x = (int) std::round(std::cos(vertex_angle) * radius) + center_x;
*vertex_y = (int) std::round(std::sin(vertex_angle) * radius) + center_y;
}
+1 -1
View File
@@ -12,7 +12,7 @@ class DS2484OneWireBus final : public one_wire::OneWireBus, public i2c::I2CDevic
public:
void setup() override;
void dump_config() override;
float get_setup_priority() const override { return setup_priority::BUS - 1.0; }
float get_setup_priority() const override { return setup_priority::BUS - 1.0f; }
bool reset_device();
int reset_int() override;
+1 -1
View File
@@ -62,7 +62,7 @@ CONFIG_SCHEMA = cv.All(
}
)
.extend(cv.polling_component_schema("60s")),
cv.AtMostOne(CONF_SENSOR, CONF_LAMBDA),
cv.has_at_most_one_key(CONF_SENSOR, CONF_LAMBDA),
)
+1 -1
View File
@@ -64,7 +64,7 @@ CONFIG_SCHEMA = cv.All(
)
.extend(cv.COMPONENT_SCHEMA)
.extend(i2c.i2c_device_schema(0x4C)),
cv.ExactlyOne(CONF_PWM, CONF_DAC),
cv.has_exactly_one_key(CONF_PWM, CONF_DAC),
)
+4 -4
View File
@@ -169,14 +169,14 @@ bool ES7210::configure_mic_gain_() {
uint8_t ES7210::es7210_gain_reg_value_(float mic_gain) {
// reg: 12 - 34.5dB, 13 - 36dB, 14 - 37.5dB
mic_gain += 0.5;
if (mic_gain <= 33.0) {
mic_gain += 0.5f;
if (mic_gain <= 33.0f) {
return (uint8_t) (mic_gain / 3);
}
if (mic_gain < 36.0) {
if (mic_gain < 36.0f) {
return 12;
}
if (mic_gain < 37.0) {
if (mic_gain < 37.0f) {
return 13;
}
return 14;
+4 -4
View File
@@ -105,14 +105,14 @@ bool ES7243E::configure_mic_gain_() {
uint8_t ES7243E::es7243e_gain_reg_value_(float mic_gain) {
// reg: 12 - 34.5dB, 13 - 36dB, 14 - 37.5dB
mic_gain += 0.5;
if (mic_gain <= 33.0) {
mic_gain += 0.5f;
if (mic_gain <= 33.0f) {
return (uint8_t) mic_gain / 3;
}
if (mic_gain < 36.0) {
if (mic_gain < 36.0f) {
return 12;
}
if (mic_gain < 37.0) {
if (mic_gain < 37.0f) {
return 13;
}
return 14;
+2 -2
View File
@@ -1550,7 +1550,7 @@ FRAMEWORK_SCHEMA = cv.Schema(
): cv.one_of(*SIGNING_SCHEMES, lower=True),
}
),
cv.ExactlyOne(CONF_SIGNING_KEY, CONF_VERIFICATION_KEY),
cv.has_exactly_one_key(CONF_SIGNING_KEY, CONF_VERIFICATION_KEY),
),
cv.Optional(
CONF_USE_FULL_CERTIFICATE_BUNDLE, default=False
@@ -1708,7 +1708,7 @@ CONFIG_SCHEMA = cv.All(
_set_default_framework,
_check_versions,
set_core_data,
cv.AtLeastOne(CONF_BOARD, CONF_VARIANT),
cv.has_at_least_one_key(CONF_BOARD, CONF_VARIANT),
)
+1 -1
View File
@@ -306,7 +306,7 @@ CONFIG_SCHEMA = cv.All(
}
).extend(cv.COMPONENT_SCHEMA),
validate_jpeg_quality,
cv.ExactlyOne(CONF_I2C_PINS, CONF_I2C_ID),
cv.has_exactly_one_key(CONF_I2C_PINS, CONF_I2C_ID),
)
@@ -129,7 +129,7 @@ CONFIG_SCHEMA = cv.All(
): cv.positive_time_period_nanoseconds,
}
).extend(cv.COMPONENT_SCHEMA),
cv.ExactlyOne(CONF_CHIPSET, CONF_BIT0_HIGH),
cv.has_exactly_one_key(CONF_CHIPSET, CONF_BIT0_HIGH),
)
+3 -3
View File
@@ -279,15 +279,15 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_WATERPROOF_SHIELD_DRIVER): cv.int_range(min=0, max=7),
}
).extend(cv.COMPONENT_SCHEMA),
cv.AllOrNone(CONF_DENOISE_GRADE, CONF_DENOISE_CAP_LEVEL),
cv.AllOrNone(
cv.has_none_or_all_keys(CONF_DENOISE_GRADE, CONF_DENOISE_CAP_LEVEL),
cv.has_none_or_all_keys(
CONF_DEBOUNCE_COUNT,
CONF_FILTER_MODE,
CONF_NOISE_THRESHOLD,
CONF_JITTER_STEP,
CONF_SMOOTH_MODE,
),
cv.AllOrNone(CONF_WATERPROOF_GUARD_RING, CONF_WATERPROOF_SHIELD_DRIVER),
cv.has_none_or_all_keys(CONF_WATERPROOF_GUARD_RING, CONF_WATERPROOF_SHIELD_DRIVER),
esp32.only_on_variant(
supported=[
esp32.VARIANT_ESP32,
@@ -303,7 +303,9 @@ void ESPNowComponent::loop() {
ESP_LOGV(TAG, ">>> [%s] %s", addr_buf, LOG_STR_ARG(espnow_error_to_str(packet->packet_.sent.status)));
#endif
if (this->current_send_packet_ != nullptr) {
this->current_send_packet_->callback_(packet->packet_.sent.status);
if (this->current_send_packet_->callback_ != nullptr) {
this->current_send_packet_->callback_(packet->packet_.sent.status);
}
this->send_packet_pool_.release(this->current_send_packet_);
this->current_send_packet_ = nullptr; // Reset current packet after sending
}
+1 -1
View File
@@ -90,7 +90,7 @@ CONFIG_FEEDBACK_COVER_BASE_SCHEMA = (
CONFIG_SCHEMA = cv.All(
CONFIG_FEEDBACK_COVER_BASE_SCHEMA,
cv.AllOrNone(CONF_OPEN_SENSOR, CONF_CLOSE_SENSOR),
cv.has_none_or_all_keys(CONF_OPEN_SENSOR, CONF_CLOSE_SENSOR),
validate_infer_endstop,
)
+2 -2
View File
@@ -139,7 +139,7 @@ void Graph::draw(Display *buff, uint16_t x_offset, uint16_t y_offset, Color colo
/// Draw grid
if (!std::isnan(this->gridspacing_y_)) {
for (int y = yn; y <= ym; y++) {
int16_t py = (int16_t) roundf((this->height_ - 1) * (1.0 - (float) (y - yn) / (ym - yn)));
int16_t py = (int16_t) roundf((this->height_ - 1) * (1.0f - (float) (y - yn) / (ym - yn)));
for (uint32_t x = 0; x < this->width_; x += 2) {
buff->draw_pixel_at(x_offset + x, y_offset + py, color);
}
@@ -177,7 +177,7 @@ void Graph::draw(Display *buff, uint16_t x_offset, uint16_t y_offset, Color colo
uint8_t bit = 1 << ((i % (thick * LineType::PATTERN_LENGTH)) / thick);
bool b = (trace->get_line_type() & bit) == bit;
if (b) {
int16_t y = (int16_t) roundf((this->height_ - 1) * (1.0 - v)) - thick / 2 + y_offset;
int16_t y = (int16_t) roundf((this->height_ - 1) * (1.0f - v)) - thick / 2 + y_offset;
auto draw_pixel_at = [&buff, c, y_offset, this](int16_t x, int16_t y) {
if (y >= y_offset && static_cast<uint32_t>(y) < y_offset + this->height_)
buff->draw_pixel_at(x, y, c);
@@ -122,7 +122,7 @@ void GroveMotorDriveTB6612FNG::stepper_run(StepperModeTypeT mode, int16_t steps,
rpm = clamp<uint16_t>(rpm, 1, 300);
ms_per_step = (uint16_t) (3000.0 / (float) rpm);
ms_per_step = (uint16_t) (3000.0f / (float) rpm);
buffer_[0] = mode;
buffer_[1] = cw; //(cw=1) => cw; (cw=0) => ccw
buffer_[2] = steps;
@@ -153,7 +153,7 @@ void GroveMotorDriveTB6612FNG::stepper_keep_run(StepperModeTypeT mode, uint16_t
uint16_t ms_per_step = 0;
rpm = clamp<uint16_t>(rpm, 1, 300);
ms_per_step = (uint16_t) (3000.0 / (float) rpm);
ms_per_step = (uint16_t) (3000.0f / (float) rpm);
buffer_[0] = mode;
buffer_[1] = cw; //(cw=1) => cw; (cw=0) => ccw
+1 -1
View File
@@ -607,7 +607,7 @@ haier_protocol::HaierMessage HonClimate::get_control_message() {
if (climate_control.target_temperature.has_value()) {
float target_temp = climate_control.target_temperature.value();
out_data->set_point = ((int) target_temp) - 16; // set the temperature with offset 16
out_data->half_degree = (target_temp - ((int) target_temp) >= 0.49) ? 1 : 0;
out_data->half_degree = (target_temp - ((int) target_temp) >= 0.49f) ? 1 : 0;
}
if (out_data->ac_power == 0) {
// If AC is off - no presets allowed
@@ -341,7 +341,7 @@ haier_protocol::HaierMessage Smartair2Climate::get_control_message() {
if (climate_control.target_temperature.has_value()) {
float target_temp = climate_control.target_temperature.value();
out_data->set_point = ((int) target_temp) - 16; // set the temperature with offset 16
out_data->half_degree = (target_temp - ((int) target_temp) >= 0.49) ? 1 : 0;
out_data->half_degree = (target_temp - ((int) target_temp) >= 0.49f) ? 1 : 0;
}
if (out_data->ac_power == 0) {
// If AC is off - no presets allowed
+1 -1
View File
@@ -39,7 +39,7 @@ CONFIG_SCHEMA = (
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x40))
.add_extra(cv.AtLeastOne(CONF_TEMPERATURE, CONF_HUMIDITY))
.add_extra(cv.has_at_least_one_key(CONF_TEMPERATURE, CONF_HUMIDITY))
)
+3 -1
View File
@@ -2,6 +2,8 @@
#include "esphome/core/log.h"
#include "esphome/core/application.h"
#include <numbers>
namespace esphome::hmc5883l {
static const char *const TAG = "hmc5883l";
@@ -126,7 +128,7 @@ void HMC5883LComponent::update() {
const float y = int16_t(raw_y) * mg_per_bit * 0.1f;
const float z = int16_t(raw_z) * mg_per_bit * 0.1f;
float heading = atan2f(0.0f - x, y) * 180.0f / M_PI;
float heading = atan2f(0.0f - x, y) * 180.0f / std::numbers::pi_v<float>;
ESP_LOGD(TAG, "Got x=%0.02fµT y=%0.02fµT z=%0.02fµT heading=%0.01f°", x, y, z, heading);
if (this->x_sensor_ != nullptr)
@@ -55,7 +55,9 @@ float HONEYWELLABPSensor::countstopressure_(const int counts, const float min_pr
// Converts a digital temperature measurement in counts to temperature in C
// This will be invalid if sensore daoes not have temperature measurement capability
float HONEYWELLABPSensor::countstotemperatures_(const int counts) { return (((float) counts / 2047.0) * 200.0) - 50.0; }
float HONEYWELLABPSensor::countstotemperatures_(const int counts) {
return (((float) counts / 2047.0f) * 200.0f) - 50.0f;
}
// Pressure value from the most recent reading in units
float HONEYWELLABPSensor::read_pressure_() {
@@ -69,9 +71,9 @@ void HONEYWELLABPSensor::update() {
ESP_LOGV(TAG, "Update Honeywell ABP Sensor");
if (readsensor_() == 0) {
if (this->pressure_sensor_ != nullptr)
this->pressure_sensor_->publish_state(read_pressure_() * 1.0);
this->pressure_sensor_->publish_state(read_pressure_() * 1.0f);
if (this->temperature_sensor_ != nullptr)
this->temperature_sensor_->publish_state(read_temperature_() * 1.0);
this->temperature_sensor_->publish_state(read_temperature_() * 1.0f);
}
}
@@ -55,7 +55,7 @@ void HrxlMaxsonarWrComponent::check_buffer_() {
millimeters = millimeters * 10;
}
float meters = float(millimeters) / 1000.0;
float meters = float(millimeters) / 1000.0f;
ESP_LOGV(TAG, "Distance from sensor: %d mm, %f m", millimeters, meters);
this->publish_state(meters);
} else {
@@ -62,7 +62,7 @@ OTA_HTTP_REQUEST_FLASH_ACTION_SCHEMA = cv.All(
cv.Required(CONF_URL): cv.templatable(cv.url),
}
),
cv.ExactlyOne(CONF_MD5, CONF_MD5_URL),
cv.has_exactly_one_key(CONF_MD5, CONF_MD5_URL),
)
@@ -139,7 +139,7 @@ void I2SAudioSpeakerBase::set_volume(float volume) {
this->volume_ = volume;
#ifdef USE_AUDIO_DAC
if (this->audio_dac_ != nullptr) {
if (volume > 0.0) {
if (volume > 0.0f) {
this->audio_dac_->set_mute_off();
}
this->audio_dac_->set_volume(volume);
+1 -1
View File
@@ -191,7 +191,7 @@ CONFIG_SCHEMA = cv.All(
)
.extend(cv.polling_component_schema("1s"))
.extend(spi.spi_device_schema(False, "40MHz")),
cv.AtMostOne(CONF_PAGES, CONF_LAMBDA),
cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA),
_validate,
)
+1 -1
View File
@@ -119,7 +119,7 @@ void INA219Component::setup() {
}
this->calibration_lsb_ = lsb;
auto calibration = uint32_t(0.04096f / (0.000001 * lsb * this->shunt_resistance_ohm_));
auto calibration = uint32_t(0.04096f / (0.000001f * lsb * this->shunt_resistance_ohm_));
ESP_LOGV(TAG, " Using LSB=%" PRIu32 " calibration=%" PRIu32, lsb, calibration);
if (!this->write_byte_16(INA219_REGISTER_CALIBRATION, calibration)) {
this->mark_failed();
+1 -1
View File
@@ -70,7 +70,7 @@ void INA226Component::setup() {
this->calibration_lsb_ = lsb;
auto calibration = uint32_t(0.00512 / (lsb * this->shunt_resistance_ohm_ / 1000000.0f));
auto calibration = uint32_t(0.00512f / (lsb * this->shunt_resistance_ohm_ / 1000000.0f));
ESP_LOGV(TAG, " Using LSB=%" PRIu32 " calibration=%" PRIu32, lsb, calibration);
+1 -1
View File
@@ -141,7 +141,7 @@ CONFIG_SCHEMA = cv.All(
)
.extend(cv.polling_component_schema("5s"))
.extend(i2c.i2c_device_schema(0x48)),
cv.AtMostOne(CONF_PAGES, CONF_LAMBDA),
cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA),
_validate_custom_waveform,
)
+1 -1
View File
@@ -29,7 +29,7 @@ CONFIG_SCHEMA = cv.All(
),
}
),
cv.ExactlyOne(CONF_REMOTE_RECEIVER_ID, CONF_REMOTE_TRANSMITTER_ID),
cv.has_exactly_one_key(CONF_REMOTE_RECEIVER_ID, CONF_REMOTE_TRANSMITTER_ID),
)
@@ -26,7 +26,7 @@ CONFIG_SCHEMA = cv.All(
),
}
),
cv.ExactlyOne(CONF_REMOTE_RECEIVER_ID, CONF_REMOTE_TRANSMITTER_ID),
cv.has_exactly_one_key(CONF_REMOTE_RECEIVER_ID, CONF_REMOTE_TRANSMITTER_ID),
)
+1 -1
View File
@@ -86,7 +86,7 @@ CONFIG_SCHEMA = cv.All(
cv.Optional(CONF_ENABLE_ON_BOOT, default=True): cv.boolean,
}
),
cv.AtLeastOne(CONF_END_KEYS, CONF_MAX_LENGTH),
cv.has_at_least_one_key(CONF_END_KEYS, CONF_MAX_LENGTH),
)
+1 -1
View File
@@ -302,7 +302,7 @@ async def random_effect_to_code(config, effect_id):
): cv.positive_time_period_milliseconds,
}
),
cv.AtLeastOne(
cv.has_at_least_one_key(
CONF_STATE,
CONF_BRIGHTNESS,
CONF_COLOR_MODE,
@@ -315,14 +315,14 @@ class LightColorValues {
if (this->color_temperature_ <= 0) {
return this->color_temperature_;
}
return 1000000.0 / this->color_temperature_;
return 1000000.0f / this->color_temperature_;
}
/// Set the color temperature property of these light color values in kelvin.
void set_color_temperature_kelvin(float color_temperature) {
if (color_temperature <= 0) {
return;
}
this->color_temperature_ = 1000000.0 / color_temperature;
this->color_temperature_ = 1000000.0f / color_temperature;
}
/// Get the cold white property of these light color values. In range 0.0 to 1.0.
+1 -1
View File
@@ -47,7 +47,7 @@ class LightTransitionTransformer : public LightTransformer {
LightColorValues &start = this->changing_color_mode_ && p > 0.5f ? this->intermediate_values_ : this->start_values_;
LightColorValues &end = this->changing_color_mode_ && p < 0.5f ? this->intermediate_values_ : this->end_values_;
if (this->changing_color_mode_)
p = p < 0.5f ? p * 2 : (p - 0.5) * 2;
p = p < 0.5f ? p * 2 : (p - 0.5f) * 2;
float v = LightTransformer::smoothed_progress(p);
return LightColorValues::lerp(start, end, v);
+1 -1
View File
@@ -75,7 +75,7 @@ void LTR390Component::read_als_() {
uint32_t als = *val;
if (this->light_sensor_ != nullptr) {
float lux = ((0.6 * als) / (GAINVALUES[this->gain_als_] * RESOLUTIONVALUE[this->res_als_])) * this->wfac_;
float lux = ((0.6f * als) / (GAINVALUES[this->gain_als_] * RESOLUTIONVALUE[this->res_als_])) * this->wfac_;
this->light_sensor_->publish_state(lux);
}
+1 -1
View File
@@ -106,7 +106,7 @@ CONFIG_SCHEMA = cv.All(
)
.extend(cv.polling_component_schema("60s"))
.extend(i2c.i2c_device_schema(0x53)),
cv.AtLeastOne(CONF_LIGHT, CONF_AMBIENT_LIGHT, CONF_UV_INDEX, CONF_UV),
cv.has_at_least_one_key(CONF_LIGHT, CONF_AMBIENT_LIGHT, CONF_UV_INDEX, CONF_UV),
)
TYPES = {
+6 -6
View File
@@ -500,12 +500,12 @@ void LTRAlsPs501Component::apply_lux_calculation_(AlsReadings &data) {
// method from
// https://github.com/fards/Ainol_fire_kernel/blob/83832cf8a3082fd8e963230f4b1984479d1f1a84/customer/drivers/lightsensor/ltr501als.c#L295
if (ratio < 0.45) {
lux = 1.7743 * ch0 + 1.1059 * ch1;
} else if (ratio < 0.64) {
lux = 3.7725 * ch0 - 1.3363 * ch1;
} else if (ratio < 0.85) {
lux = 1.6903 * ch0 - 0.1693 * ch1;
if (ratio < 0.45f) {
lux = 1.7743f * ch0 + 1.1059f * ch1;
} else if (ratio < 0.64f) {
lux = 3.7725f * ch0 - 1.3363f * ch1;
} else if (ratio < 0.85f) {
lux = 1.6903f * ch0 - 0.1693f * ch1;
} else {
ESP_LOGW(TAG, "Impossible ch1/(ch0 + ch1) ratio");
lux = 0.0f;
+6 -6
View File
@@ -480,12 +480,12 @@ void LTRAlsPsComponent::apply_lux_calculation_(AlsReadings &data) {
float inv_pfactor = this->glass_attenuation_factor_;
float lux = 0.0f;
if (ratio < 0.45) {
lux = (1.7743 * ch0 + 1.1059 * ch1);
} else if (ratio < 0.64 && ratio >= 0.45) {
lux = (4.2785 * ch0 - 1.9548 * ch1);
} else if (ratio < 0.85 && ratio >= 0.64) {
lux = (0.5926 * ch0 + 0.1185 * ch1);
if (ratio < 0.45f) {
lux = (1.7743f * ch0 + 1.1059f * ch1);
} else if (ratio < 0.64f && ratio >= 0.45f) {
lux = (4.2785f * ch0 - 1.9548f * ch1);
} else if (ratio < 0.85f && ratio >= 0.64f) {
lux = (0.5926f * ch0 + 0.1185f * ch1);
} else {
ESP_LOGW(TAG, "Impossible ch1/(ch0 + ch1) ratio");
lux = 0.0f;
+1 -1
View File
@@ -650,7 +650,7 @@ LVGL_TOP_LEVEL_SCHEMA = (
LVGL_SCHEMA = cv.All(
container_schema(obj_spec, LVGL_TOP_LEVEL_SCHEMA),
cv.AtMostOne(CONF_PAGES, df.CONF_LAYOUT),
cv.has_at_most_one_key(CONF_PAGES, df.CONF_LAYOUT),
add_hello_world,
)
+4 -4
View File
@@ -146,7 +146,7 @@ INDICATOR_LINE_SCHEMA = cv.Schema(
cv.Optional(CONF_VALUE, default=0.0): lv_float,
cv.Optional(CONF_OPA, default=1.0): opacity,
}
).add_extra(cv.AtMostOne(CONF_R_MOD, CONF_LENGTH))
).add_extra(cv.has_at_most_one_key(CONF_R_MOD, CONF_LENGTH))
class ScaleType(WidgetType):
@@ -186,7 +186,7 @@ INDICATOR_ARC_SCHEMA = cv.Schema(
cv.Optional(CONF_OPA, default=1.0): opacity,
cv.Optional(CONF_ROUNDED, default=False): cv.boolean,
}
).add_extra(cv.AtMostOne(CONF_VALUE, CONF_START_VALUE))
).add_extra(cv.has_at_most_one_key(CONF_VALUE, CONF_START_VALUE))
INDICATOR_TICKS_SCHEMA = cv.Schema(
{
@@ -198,7 +198,7 @@ INDICATOR_TICKS_SCHEMA = cv.Schema(
cv.Optional(CONF_END_VALUE): lv_float,
cv.Optional(CONF_LOCAL, default=False): lv_bool,
}
).add_extra(cv.AtMostOne(CONF_VALUE, CONF_START_VALUE))
).add_extra(cv.has_at_most_one_key(CONF_VALUE, CONF_START_VALUE))
INDICATOR_SCHEMA = cv.Schema(
{
@@ -355,7 +355,7 @@ class MeterType(WidgetType):
return CONF_SCALE, CONF_LINE, CONF_IMAGE, CONF_LABEL
def validate(self, value):
return cv.AtMostOne(CONF_INDICATOR, CONF_PIVOT)(value)
return cv.has_at_most_one_key(CONF_INDICATOR, CONF_PIVOT)(value)
async def on_create(self, var: MockObj, config: dict):
# Remove theme styling from outer container
+1 -1
View File
@@ -122,7 +122,7 @@ tabview_spec = TabviewType()
cv.Optional(CONF_ANIMATED, default=False): animated,
cv.Required(CONF_INDEX): lv_int,
},
).add_extra(cv.AtLeastOne(CONF_INDEX, CONF_TAB_ID)),
).add_extra(cv.has_at_least_one_key(CONF_INDEX, CONF_TAB_ID)),
synchronous=True,
)
async def tabview_select(config, action_id, template_arg, args):
+1 -1
View File
@@ -23,7 +23,7 @@ void MAX17043Component::update() {
if (!this->read_byte_16(MAX17043_VCELL, &raw_voltage)) {
this->status_set_warning(LOG_STR("Unable to read MAX17043_VCELL"));
} else {
float voltage = (1.25 * (float) (raw_voltage >> 4)) / 1000.0;
float voltage = (1.25f * (float) (raw_voltage >> 4)) / 1000.0f;
this->voltage_sensor_->publish_state(voltage);
this->status_clear_warning();
}
+1 -1
View File
@@ -31,7 +31,7 @@ float MCP3204::read_data(uint8_t pin, bool differential) {
this->disable();
uint16_t digital_value = encode_uint16(b0, b1) >> 4;
return float(digital_value) / 4096.000 * this->reference_voltage_; // in V
return float(digital_value) / 4096.000f * this->reference_voltage_; // in V
}
} // namespace esphome::mcp3204
@@ -29,7 +29,9 @@ void Mcp4461Wiper::write_state(float state) {
}
}
float Mcp4461Wiper::read_state() { return (static_cast<float>(this->parent_->get_wiper_level_(this->wiper_)) / 256.0); }
float Mcp4461Wiper::read_state() {
return (static_cast<float>(this->parent_->get_wiper_level_(this->wiper_)) / 256.0f);
}
float Mcp4461Wiper::update_state() {
this->state_ = this->read_state();
+2 -1
View File
@@ -24,7 +24,8 @@ void MCP4725::dump_config() {
// https://learn.sparkfun.com/tutorials/mcp4725-digital-to-analog-converter-hookup-guide?_ga=2.176055202.1402343014.1607953301-893095255.1606753886
void MCP4725::write_state(float state) {
const uint16_t value = (uint16_t) round(state * (pow(2, MCP4725_RES) - 1));
constexpr uint16_t max_value = (1U << MCP4725_RES) - 1;
const uint16_t value = (uint16_t) roundf(state * max_value);
this->write_byte_16(64, value << 4);
}
+4 -3
View File
@@ -4,10 +4,11 @@
#include "esphome/core/component.h"
#include "esphome/components/i2c/i2c.h"
static const uint8_t MCP4725_ADDR = 0x60;
static const uint8_t MCP4725_RES = 12;
namespace esphome::mcp4725 {
static constexpr uint8_t MCP4725_ADDR = 0x60;
static constexpr uint8_t MCP4725_RES = 12;
class MCP4725 final : public Component, public output::FloatOutput, public i2c::I2CDevice {
public:
void setup() override;
+11 -11
View File
@@ -71,10 +71,10 @@ void MICS4514Component::update() {
float co = 0.0f;
if (red_f > 3.4f) {
co = 0.0;
} else if (red_f < 0.01) {
} else if (red_f < 0.01f) {
co = 1000.0;
} else {
co = 4.2 / pow(red_f, 1.2);
co = 4.2f / powf(red_f, 1.2f);
}
this->carbon_monoxide_sensor_->publish_state(co);
}
@@ -84,47 +84,47 @@ void MICS4514Component::update() {
if (ox_f < 0.3f) {
nitrogendioxide = 0.0;
} else {
nitrogendioxide = 0.164 * pow(ox_f, 0.975);
nitrogendioxide = 0.164f * powf(ox_f, 0.975f);
}
this->nitrogen_dioxide_sensor_->publish_state(nitrogendioxide);
}
if (this->methane_sensor_ != nullptr) {
float methane = 0.0f;
if (red_f > 0.9f || red_f < 0.5) { // outside the range->unlikely
if (red_f > 0.9f || red_f < 0.5f) { // outside the range->unlikely
methane = 0.0;
} else {
methane = 630 / pow(red_f, 4.4);
methane = 630 / powf(red_f, 4.4f);
}
this->methane_sensor_->publish_state(methane);
}
if (this->ethanol_sensor_ != nullptr) {
float ethanol = 0.0f;
if (red_f > 1.0f || red_f < 0.02) { // outside the range->unlikely
if (red_f > 1.0f || red_f < 0.02f) { // outside the range->unlikely
ethanol = 0.0;
} else {
ethanol = 1.52 / pow(red_f, 1.55);
ethanol = 1.52f / powf(red_f, 1.55f);
}
this->ethanol_sensor_->publish_state(ethanol);
}
if (this->hydrogen_sensor_ != nullptr) {
float hydrogen = 0.0f;
if (red_f > 0.9f || red_f < 0.02) { // outside the range->unlikely
if (red_f > 0.9f || red_f < 0.02f) { // outside the range->unlikely
hydrogen = 0.0;
} else {
hydrogen = 0.85 / pow(red_f, 1.75);
hydrogen = 0.85f / powf(red_f, 1.75f);
}
this->hydrogen_sensor_->publish_state(hydrogen);
}
if (this->ammonia_sensor_ != nullptr) {
float ammonia = 0.0f;
if (red_f > 0.98f || red_f < 0.2532) { // outside the ammonia range->unlikely
if (red_f > 0.98f || red_f < 0.2532f) { // outside the ammonia range->unlikely
ammonia = 0.0;
} else {
ammonia = 0.9 / pow(red_f, 4.6);
ammonia = 0.9f / powf(red_f, 4.6f);
}
this->ammonia_sensor_->publish_state(ammonia);
}
+4 -4
View File
@@ -6,7 +6,7 @@ from collections.abc import Callable
import functools
from typing import Any, Self
import probatio
import voluptuous as vol
from esphome.components.const import CONF_COLOR_DEPTH
from esphome.components.display import CONF_SHOW_TEST_CARD, display_ns
@@ -280,12 +280,12 @@ def model_schema_extractor(
names = sorted(models)
representative = next((n for n in names if n != _CUSTOM_MODEL), names[0])
schema = model_schema({CONF_MODEL: representative, **(extra or {})})
if isinstance(schema, probatio.All):
if isinstance(schema, vol.All):
schema = next(
(v for v in schema.validators if isinstance(v, probatio.Schema)),
(v for v in schema.validators if isinstance(v, vol.Schema)),
schema,
)
if isinstance(schema, probatio.Schema):
if isinstance(schema, vol.Schema):
# The resolved schema pins ``model`` to the representative; expose
# the full model list so the dumped enum offers every model.
schema = schema.extend(
+3 -1
View File
@@ -1,6 +1,8 @@
#include "mmc5603.h"
#include "esphome/core/log.h"
#include <numbers>
namespace esphome::mmc5603 {
static const char *const TAG = "mmc5603";
@@ -143,7 +145,7 @@ void MMC5603Component::update() {
const float z = 0.00625 * (raw_z - 524288);
const float heading = atan2f(0.0f - x, y) * 180.0f / M_PI;
const float heading = atan2f(0.0f - x, y) * 180.0f / std::numbers::pi_v<float>;
ESP_LOGD(TAG, "Got x=%0.02fµT y=%0.02fµT z=%0.02fµT heading=%0.01f°", x, y, z, heading);
if (this->x_sensor_ != nullptr)
-1
View File
@@ -124,7 +124,6 @@ async def register_modbus_client_device(var, config):
async def register_modbus_server_device(var, config):
parent = await cg.get_variable(config[CONF_MODBUS_ID])
cg.add(var.set_parent(parent))
cg.add(var.set_address(config[CONF_ADDRESS]))
cg.add(parent.register_device(var))
+136 -41
View File
@@ -92,14 +92,10 @@ int32_t Modbus::tx_delay_remaining() {
int32_t ModbusClientHub::tx_delay_remaining() {
const uint32_t now = millis();
// Turnaround delay only applies after a broadcast: no response is expected, so we must give listening devices
// quiet time to process it before the next request. For normal unicast request/response the received reply already
// provides the inter-frame timing, so adding turnaround there just throttles throughput.
const uint16_t turnaround = this->last_send_was_broadcast_ ? this->turnaround_delay_ms_ : 0;
return std::max(
{(int32_t) 0,
(int32_t) (this->last_send_tx_offset_ + this->frame_delay_ms_ + turnaround - (now - this->last_send_)),
(int32_t) (this->frame_delay_ms_ + turnaround - (now - this->last_modbus_byte_))});
return std::max({(int32_t) 0,
(int32_t) (this->last_send_tx_offset_ + this->frame_delay_ms_ + this->turnaround_delay_ms_ -
(now - this->last_send_)),
(int32_t) (this->frame_delay_ms_ + this->turnaround_delay_ms_ - (now - this->last_modbus_byte_))});
}
bool Modbus::tx_blocked() {
@@ -258,7 +254,7 @@ bool ModbusServerHub::parse_modbus_client_frame_() {
std::memcpy(data, this->rx_buffer_.data() + data_offset, data_len);
this->clear_rx_buffer_(LOG_STR("parse succeeded"), false, frame_length);
this->process_modbus_client_frame_(address, function_code, data, data_len);
this->process_modbus_client_frame_(address, function_code, data);
return true;
}
@@ -321,10 +317,8 @@ void ModbusClientHub::process_modbus_server_frame(uint8_t address, uint8_t funct
}
void ModbusServerHub::process_modbus_server_frame(uint8_t address, uint8_t function_code, const uint8_t *, uint16_t) {
for (auto *device : this->devices_) {
if (device->address_ == address) {
ESP_LOGE(TAG, "Unexpected response from address %" PRIu8 ", which is mapped to this device.", address);
}
if (this->find_device_(address) != nullptr) {
ESP_LOGE(TAG, "Unexpected response from address %" PRIu8 ", which is mapped to this device.", address);
}
if (this->expecting_peer_response_ == address) {
@@ -338,31 +332,124 @@ void ModbusServerHub::process_modbus_server_frame(uint8_t address, uint8_t funct
this->expecting_peer_response_ = 0;
}
void ModbusServerHub::process_modbus_client_frame_(uint8_t address, uint8_t function_code, const uint8_t *data,
uint16_t len) {
bool found = false;
ModbusServerDevice *ModbusServerHub::find_device_(uint8_t address) {
for (auto *device : this->devices_) {
if (device->address_ == address) {
found = true;
if (static_cast<ModbusFunctionCode>(function_code) == ModbusFunctionCode::READ_HOLDING_REGISTERS ||
static_cast<ModbusFunctionCode>(function_code) == ModbusFunctionCode::READ_INPUT_REGISTERS) {
device->on_modbus_read_registers(function_code, helpers::get_data<uint16_t>(data, 0),
helpers::get_data<uint16_t>(data, 2));
} else if (static_cast<ModbusFunctionCode>(function_code) == ModbusFunctionCode::WRITE_SINGLE_REGISTER ||
static_cast<ModbusFunctionCode>(function_code) == ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS) {
device->on_modbus_write_registers(function_code, std::vector<uint8_t>(data, data + len));
} else {
ESP_LOGW(TAG, "Unsupported function code %" PRIu8, function_code);
device->send_error(function_code, ModbusExceptionCode::ILLEGAL_FUNCTION);
}
if (device->get_address() == address) {
return device;
}
}
return nullptr;
}
if (!found) {
bool ModbusServerHub::check_register_range_(uint8_t address, uint8_t function_code, uint16_t start_address,
uint16_t number_of_registers) {
if ((uint32_t) start_address + number_of_registers > 0x10000u) {
ESP_LOGW(TAG, "Register address out of range - start: %" PRIu16 " num: %" PRIu16, start_address,
number_of_registers);
this->send_exception_(address, function_code, ModbusExceptionCode::ILLEGAL_DATA_ADDRESS);
return false;
}
return true;
}
void ModbusServerHub::process_modbus_client_frame_(uint8_t address, uint8_t function_code, const uint8_t *data) {
ModbusServerDevice *device = this->find_device_(address);
if (device == nullptr) {
this->expecting_peer_response_ = address;
ESP_LOGV(TAG, "Request to peer %" PRIu8 " received", address);
return;
}
ServerResponseStatus status;
uint8_t response_buffer[modbus::MAX_RAW_SIZE];
const uint8_t *response_data = response_buffer;
uint16_t response_len = 0;
switch (static_cast<ModbusFunctionCode>(function_code)) {
case ModbusFunctionCode::READ_HOLDING_REGISTERS:
case ModbusFunctionCode::READ_INPUT_REGISTERS: {
// PDU data: start address(2) + quantity(2).
uint16_t start_address = helpers::get_data<uint16_t>(data, 0);
uint16_t number_of_registers = helpers::get_data<uint16_t>(data, 2);
if (number_of_registers == 0 || number_of_registers > MAX_NUM_OF_REGISTERS_TO_READ) {
ESP_LOGW(TAG, "Invalid number of registers %" PRIu16, number_of_registers);
this->send_exception_(address, function_code, ModbusExceptionCode::ILLEGAL_DATA_VALUE);
return;
}
if (!this->check_register_range_(address, function_code, start_address, number_of_registers)) {
return;
}
RegisterValues registers;
if (static_cast<ModbusFunctionCode>(function_code) == ModbusFunctionCode::READ_HOLDING_REGISTERS) {
status = device->on_modbus_read_holding_registers(start_address, number_of_registers, registers);
} else {
status = device->on_modbus_read_input_registers(start_address, number_of_registers, registers);
}
// A handler that returns an exception leaves registers partially filled, so check the exception
// first and forward it before validating the register count on the success path.
if (status.has_value()) {
this->send_exception_(address, function_code, status.value());
return;
}
if (registers.size() != number_of_registers) {
ESP_LOGE(TAG, "Incorrect response %" PRIu16 " requested, %zu returned", number_of_registers, registers.size());
this->send_exception_(address, function_code, ModbusExceptionCode::SERVICE_DEVICE_FAILURE);
return;
}
response_buffer[response_len++] = static_cast<uint8_t>(number_of_registers * 2); // actual byte count
for (auto r : registers) {
auto register_bytes = decode_value(r);
response_buffer[response_len++] = register_bytes[0];
response_buffer[response_len++] = register_bytes[1];
}
break;
}
case ModbusFunctionCode::WRITE_SINGLE_REGISTER:
case ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS: {
// PDU data: start address(2) [+ quantity(2) + byte count(1)] + register values.
// A single-register write always targets one register; for a multiple-register write the
// quantity is in the frame and its byte count must equal quantity * 2. The register values are
// assembled into registers below so the handler doesn't have to know the request framing.
uint16_t start_address = helpers::get_data<uint16_t>(data, 0);
uint16_t number_of_registers = 1;
uint16_t values_offset = 2; // single write: values follow the 2-byte start address
if (static_cast<ModbusFunctionCode>(function_code) == ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS) {
number_of_registers = helpers::get_data<uint16_t>(data, 2);
uint8_t number_of_bytes = helpers::get_data<uint8_t>(data, 4);
values_offset = 5; // multiple write: values follow start address(2) + quantity(2) + byte count(1)
if (number_of_registers == 0 || number_of_registers > MAX_NUM_OF_REGISTERS_TO_WRITE ||
number_of_registers * 2 != number_of_bytes) {
ESP_LOGW(TAG, "Invalid number of registers %" PRIu16 " or bytes %" PRIu8, number_of_registers,
number_of_bytes);
this->send_exception_(address, function_code, ModbusExceptionCode::ILLEGAL_DATA_VALUE);
return;
}
if (!this->check_register_range_(address, function_code, start_address, number_of_registers)) {
return;
}
}
// Assemble the register values (host byte order) so the handler never sees wire framing.
RegisterValues registers;
for (uint16_t i = 0; i < number_of_registers; i++) {
registers.push_back(helpers::get_data<uint16_t>(data, values_offset + i * 2));
}
status = device->on_modbus_write_registers(start_address, registers);
response_data = data; // echo the request header per Modbus 6.6, 6.12
response_len = 4;
break;
}
default:
ESP_LOGW(TAG, "Unsupported function code %" PRIu8, function_code);
this->send_exception_(address, function_code, ModbusExceptionCode::ILLEGAL_FUNCTION);
return;
}
if (status.has_value()) {
this->send_exception_(address, function_code, status.value());
} else {
this->send_response_(address, function_code, response_data, response_len);
}
}
@@ -400,7 +487,6 @@ bool Modbus::send_frame_(const ModbusFrame &frame) {
format_hex_pretty_to(hex_buf, frame.data.get(), frame.size), now - this->last_send_,
now - this->last_modbus_byte_);
this->last_send_ = now;
this->last_send_was_broadcast_ = frame.size > 0 && frame.data[0] == 0;
return true;
}
@@ -416,8 +502,7 @@ void ModbusClientHub::send_next_frame_() {
ModbusDeviceCommand &command = this->tx_buffer_.front();
if (this->send_frame_(command.frame)) {
if (!this->last_send_was_broadcast_)
this->waiting_for_response_ = std::move(command);
this->waiting_for_response_ = std::move(command);
} else {
if (command.device)
command.device->on_modbus_not_sent();
@@ -455,17 +540,27 @@ float Modbus::get_setup_priority() const {
return setup_priority::BUS - 1.0f;
}
void ModbusServerHub::send(uint8_t address, uint8_t function_code, const std::vector<uint8_t> &payload) {
const uint16_t len = static_cast<uint16_t>(2 + payload.size());
if (len > MAX_RAW_SIZE) {
ESP_LOGE(TAG, "Server send frame too large (%" PRIu16 " bytes)", len);
void ModbusServerHub::send_response_(uint8_t address, uint8_t function_code, const uint8_t *payload,
uint16_t payload_len) {
// Build the raw frame (address + function code + payload) in a stack buffer; it's consumed
// immediately by send_raw_ and a full raw frame never exceeds MAX_RAW_SIZE.
if (payload_len + 2 > MAX_RAW_SIZE) {
ESP_LOGE(TAG, "Server response too large (%" PRIu16 " bytes)", static_cast<uint16_t>(payload_len + 2));
return;
}
uint8_t raw_frame[MAX_RAW_SIZE];
raw_frame[0] = address;
raw_frame[1] = function_code;
std::memcpy(raw_frame + 2, payload.data(), payload.size());
this->send_raw_(raw_frame, len);
std::memcpy(raw_frame + 2, payload, payload_len);
this->send_raw_(raw_frame, payload_len + 2);
}
void ModbusServerHub::send_exception_(uint8_t address, uint8_t function_code, ModbusExceptionCode exception_code) {
uint8_t raw_frame[3];
raw_frame[0] = address;
raw_frame[1] = function_code | FUNCTION_CODE_EXCEPTION_MASK;
raw_frame[2] = static_cast<uint8_t>(exception_code);
this->send_raw_(raw_frame, 3);
}
// Raw send for client: pushes to tx queue. Everything except the CRC must be contained in payload.
+33 -28
View File
@@ -63,7 +63,6 @@ class Modbus : public uart::UARTDevice, public Component {
uint32_t last_receive_check_{0};
uint32_t last_send_{0};
uint32_t last_send_tx_offset_{0};
bool last_send_was_broadcast_{false};
uint16_t frame_delay_ms_{5};
uint16_t long_rx_buffer_delay_ms_{0};
@@ -130,22 +129,22 @@ class ModbusServerHub : public Modbus {
public:
ModbusServerHub() = default;
void dump_config() override;
void send(uint8_t address, uint8_t function_code, const std::vector<uint8_t> &payload);
ESPDEPRECATED("Use ModbusServerDevice::send_raw instead. Removed in 2026.10.0", "2026.4.0")
void send_raw(const std::vector<uint8_t> &payload) {
this->send_raw_(payload.data(), static_cast<uint16_t>(payload.size()));
};
void register_device(ModbusServerDevice *device) { this->devices_.push_back(device); }
protected:
friend class ModbusServerDevice;
void parse_modbus_frames() override;
bool parse_modbus_client_frame_();
// Parsers need to handle standard (ModbusFunctionCode) and custom (uint8_t) function codes, so we use uint8_t here.
void process_modbus_server_frame(uint8_t address, uint8_t function_code, const uint8_t *data, uint16_t len) override;
void process_modbus_client_frame_(uint8_t address, uint8_t function_code, const uint8_t *data, uint16_t len);
void process_modbus_client_frame_(uint8_t address, uint8_t function_code, const uint8_t *data);
ModbusServerDevice *find_device_(uint8_t address);
// Returns true if [start_address, start_address + number_of_registers) fits in the 16-bit address space.
// On failure, logs and sends an ILLEGAL_DATA_ADDRESS exception to the client.
bool check_register_range_(uint8_t address, uint8_t function_code, uint16_t start_address,
uint16_t number_of_registers);
void send_raw_(const uint8_t *payload, uint16_t len);
void send_exception_(uint8_t address, uint8_t function_code, ModbusExceptionCode exception_code);
void send_response_(uint8_t address, uint8_t function_code, const uint8_t *payload, uint16_t payload_len);
uint8_t expecting_peer_response_{0};
std::vector<ModbusServerDevice *> devices_;
@@ -200,35 +199,41 @@ class ModbusClientDevice {
// This is for compatibility with external components using the former class name
using ModbusDevice = ModbusClientDevice;
// Result of a server register handler: std::nullopt means success, otherwise the Modbus exception code to return.
using ServerResponseStatus = std::optional<ModbusExceptionCode>;
// Register values exchanged with server handlers, in host byte order. Sized at the larger of the two protocol
// maxima (read = 125 / 0x7D, write = 123 / 0x7B); the per-direction count limit is enforced by the hub, not by
// the capacity of this type.
using RegisterValues = StaticVector<uint16_t, MAX_NUM_OF_REGISTERS_TO_READ>;
class ModbusServerDevice {
public:
ModbusServerDevice() = default;
ModbusServerDevice(ModbusServerHub *parent, uint8_t address) : parent_(parent), address_(address) {}
virtual ~ModbusServerDevice() = default;
ModbusServerDevice() = default;
// Polymorphic base: non-copyable and non-movable to prevent slicing (Rule of Five).
ModbusServerDevice(const ModbusServerDevice &) = delete;
ModbusServerDevice &operator=(const ModbusServerDevice &) = delete;
ModbusServerDevice(ModbusServerDevice &&) = delete;
ModbusServerDevice &operator=(ModbusServerDevice &&) = delete;
void set_parent(ModbusServerHub *parent) { this->parent_ = parent; }
void set_address(uint8_t address) { this->address_ = address; }
virtual void on_modbus_read_registers(uint8_t function_code, uint16_t start_address, uint16_t number_of_registers){};
virtual void on_modbus_write_registers(uint8_t function_code, const std::vector<uint8_t> &data){};
void send(uint8_t function, const std::vector<uint8_t> &payload) {
this->parent_->send(this->address_, function, payload);
}
void send_raw(const std::vector<uint8_t> &payload) {
this->parent_->send_raw_(payload.data(), static_cast<uint16_t>(payload.size()));
}
void send_error(uint8_t function_code, ModbusExceptionCode exception_code) {
uint8_t error_response[3] = {this->address_, uint8_t(function_code | FUNCTION_CODE_EXCEPTION_MASK),
static_cast<uint8_t>(exception_code)};
this->parent_->send_raw_(error_response, 3);
}
uint8_t get_address() const { return this->address_; }
virtual ServerResponseStatus on_modbus_read_registers(uint16_t start_address, uint16_t number_of_registers,
RegisterValues &registers) {
return ModbusExceptionCode::ILLEGAL_FUNCTION;
};
virtual ServerResponseStatus on_modbus_read_input_registers(uint16_t start_address, uint16_t number_of_registers,
RegisterValues &registers) {
return this->on_modbus_read_registers(start_address, number_of_registers, registers);
};
virtual ServerResponseStatus on_modbus_read_holding_registers(uint16_t start_address, uint16_t number_of_registers,
RegisterValues &registers) {
return this->on_modbus_read_registers(start_address, number_of_registers, registers);
};
virtual ServerResponseStatus on_modbus_write_registers(uint16_t start_address, const RegisterValues &registers) {
return ModbusExceptionCode::ILLEGAL_FUNCTION;
};
protected:
friend ModbusServerHub;
ModbusServerHub *parent_{nullptr};
uint8_t address_{0};
};
@@ -82,7 +82,7 @@ static constexpr uint8_t MAX_NUM_OF_REGISTERS_TO_READ = 125; // 0x7D
// Smallest possible frame is 4 bytes (custom function with no data): address(1) + function(1) + CRC(2)
static constexpr uint16_t MIN_FRAME_SIZE = 4;
static constexpr uint16_t MAX_PDU_SIZE = 253; // Max PDU size is 256 - address(1) - CRC(2) = 253
static constexpr uint16_t MAX_RAW_SIZE = 254; // Max RAW size is 255 - CRC(2) = 254
static constexpr uint16_t MAX_RAW_SIZE = 254; // Max RAW size is 256 - CRC(2) = 254
static constexpr uint16_t MAX_FRAME_SIZE = 256;
/// End of Modbus definitions
} // namespace esphome::modbus
+32 -42
View File
@@ -101,53 +101,19 @@ static size_t required_payload_size(SensorValueType sensor_value_type) {
}
}
void number_to_payload(std::vector<uint16_t> &data, int64_t value, SensorValueType value_type) {
switch (value_type) {
case SensorValueType::U_WORD:
case SensorValueType::S_WORD:
data.push_back(value & 0xFFFF);
break;
case SensorValueType::U_DWORD:
case SensorValueType::S_DWORD:
case SensorValueType::FP32:
data.push_back((value & 0xFFFF0000) >> 16);
data.push_back(value & 0xFFFF);
break;
case SensorValueType::U_DWORD_R:
case SensorValueType::S_DWORD_R:
case SensorValueType::FP32_R:
data.push_back(value & 0xFFFF);
data.push_back((value & 0xFFFF0000) >> 16);
break;
case SensorValueType::U_QWORD:
case SensorValueType::S_QWORD:
data.push_back((value & 0xFFFF000000000000) >> 48);
data.push_back((value & 0xFFFF00000000) >> 32);
data.push_back((value & 0xFFFF0000) >> 16);
data.push_back(value & 0xFFFF);
break;
case SensorValueType::U_QWORD_R:
case SensorValueType::S_QWORD_R:
data.push_back(value & 0xFFFF);
data.push_back((value & 0xFFFF0000) >> 16);
data.push_back((value & 0xFFFF00000000) >> 32);
data.push_back((value & 0xFFFF000000000000) >> 48);
break;
default:
ESP_LOGE(TAG, "Invalid data type for modbus number to payload conversion: %d", static_cast<uint16_t>(value_type));
break;
}
void log_unsupported_value_type(SensorValueType value_type) {
ESP_LOGE(TAG, "Invalid data type for modbus number to payload conversion: %d", static_cast<uint16_t>(value_type));
}
int64_t payload_to_number(const std::vector<uint8_t> &data, SensorValueType sensor_value_type, uint8_t offset,
int64_t payload_to_number(const uint8_t *data, size_t size, SensorValueType sensor_value_type, uint8_t offset,
uint32_t bitmask, bool *error_return) {
int64_t value = 0; // int64_t because it can hold signed and unsigned 32 bits
// Validate offset against the buffer for all types, including RAW/unsupported, so
// a malformed or misconfigured frame still produces an error log.
if (static_cast<size_t>(offset) > data.size()) {
if (static_cast<size_t>(offset) > size) {
ESP_LOGE(TAG, "not enough data for value type=%u offset=%u size=%zu", static_cast<unsigned int>(sensor_value_type),
static_cast<unsigned int>(offset), data.size());
static_cast<unsigned int>(offset), size);
if (error_return)
*error_return = true;
return value;
@@ -158,10 +124,9 @@ int64_t payload_to_number(const std::vector<uint8_t> &data, SensorValueType sens
return value;
}
if (data.size() - offset < required_size) {
if (size - offset < required_size) {
ESP_LOGE(TAG, "not enough data for value type=%u offset=%u size=%zu required=%zu",
static_cast<unsigned int>(sensor_value_type), static_cast<unsigned int>(offset), data.size(),
required_size);
static_cast<unsigned int>(sensor_value_type), static_cast<unsigned int>(offset), size, required_size);
if (error_return)
*error_return = true;
return value;
@@ -214,6 +179,31 @@ int64_t payload_to_number(const std::vector<uint8_t> &data, SensorValueType sens
return value;
}
int64_t registers_to_number(const uint16_t *registers, size_t count, SensorValueType sensor_value_type,
bool *error_return) {
const size_t required_size = required_payload_size(sensor_value_type);
if (required_size == 0) {
return 0; // RAW/unsupported: nothing to read
}
const size_t required_words = required_size / 2;
if (required_words > count) {
ESP_LOGE(TAG, "not enough registers for value type=%u count=%zu required=%zu",
static_cast<unsigned int>(sensor_value_type), count, required_words);
if (error_return)
*error_return = true;
return 0;
}
// Serialize the needed words back to big-endian bytes and reuse the audited byte decoder so the
// sign-extension behaviour stays identical to the wire path.
uint8_t bytes[8]; // at most 4 registers (QWORD)
for (size_t i = 0; i < required_words; i++) {
uint16_t reg = registers[i];
bytes[i * 2] = static_cast<uint8_t>(reg >> 8);
bytes[i * 2 + 1] = static_cast<uint8_t>(reg & 0xFF);
}
return payload_to_number(bytes, required_size, sensor_value_type, 0, 0xFFFFFFFF, error_return);
}
StaticVector<uint8_t, MAX_PDU_SIZE> create_client_pdu(ModbusFunctionCode function_code, uint16_t start_address,
uint16_t number_of_entities, const uint8_t *values,
size_t values_len) {
+62 -9
View File
@@ -224,24 +224,77 @@ template<typename N> N mask_and_shift_by_rightbit(N data, uint32_t mask) {
return 0;
}
/** Convert float value to vector<uint16_t> suitable for sending
* @param data target for payload
* @param value float value to convert
* @param value_type defines if 16/32 or FP32 is used
* @return vector containing the modbus register words in correct order
*/
void number_to_payload(std::vector<uint16_t> &data, int64_t value, SensorValueType value_type);
// Logs an error for an unsupported value type. Defined in the .cpp so logging stays out of headers.
void log_unsupported_value_type(SensorValueType value_type);
/** Convert vector<uint8_t> response payload to number.
/** Append the Modbus register words for value to data.
* Works with any container exposing push_back(uint16_t) (e.g. std::vector or StaticVector).
*/
template<typename Container> void number_to_payload(Container &data, int64_t value, SensorValueType value_type) {
switch (value_type) {
case SensorValueType::U_WORD:
case SensorValueType::S_WORD:
data.push_back(value & 0xFFFF);
break;
case SensorValueType::U_DWORD:
case SensorValueType::S_DWORD:
case SensorValueType::FP32:
data.push_back((value & 0xFFFF0000) >> 16);
data.push_back(value & 0xFFFF);
break;
case SensorValueType::U_DWORD_R:
case SensorValueType::S_DWORD_R:
case SensorValueType::FP32_R:
data.push_back(value & 0xFFFF);
data.push_back((value & 0xFFFF0000) >> 16);
break;
case SensorValueType::U_QWORD:
case SensorValueType::S_QWORD:
data.push_back((value & 0xFFFF000000000000) >> 48);
data.push_back((value & 0xFFFF00000000) >> 32);
data.push_back((value & 0xFFFF0000) >> 16);
data.push_back(value & 0xFFFF);
break;
case SensorValueType::U_QWORD_R:
case SensorValueType::S_QWORD_R:
data.push_back(value & 0xFFFF);
data.push_back((value & 0xFFFF0000) >> 16);
data.push_back((value & 0xFFFF00000000) >> 32);
data.push_back((value & 0xFFFF000000000000) >> 48);
break;
default:
log_unsupported_value_type(value_type);
break;
}
}
/** Convert a raw response payload to a number.
* @param data payload with the data to convert
* @param size number of bytes available in data
* @param sensor_value_type defines if 16/32/64 bits or FP32 is used
* @param offset offset to the data in data
* @param bitmask bitmask used for masking and shifting
* @return 64-bit number of the payload
*/
int64_t payload_to_number(const std::vector<uint8_t> &data, SensorValueType sensor_value_type, uint8_t offset,
int64_t payload_to_number(const uint8_t *data, size_t size, SensorValueType sensor_value_type, uint8_t offset,
uint32_t bitmask, bool *error_return = nullptr);
/** Convert vector<uint8_t> response payload to number. */
inline int64_t payload_to_number(const std::vector<uint8_t> &data, SensorValueType sensor_value_type, uint8_t offset,
uint32_t bitmask, bool *error_return = nullptr) {
return payload_to_number(data.data(), data.size(), sensor_value_type, offset, bitmask, error_return);
}
/** Reconstruct a number from register words (host byte order). Inverse of number_to_payload.
* Decodes the value at the start of the given span; advance the pointer to read successive values.
* @param registers register values in host byte order
* @param count number of registers available in registers
* @param sensor_value_type defines if 16/32/64 bits or FP32 is used
* @return 64-bit number of the registers
*/
int64_t registers_to_number(const uint16_t *registers, size_t count, SensorValueType sensor_value_type,
bool *error_return = nullptr);
/** Create a modbus clinet pdu for reading/writing single/multiple coils/register/inputs.
* @param function_code the modbus function code to use. One of:
* READ_COILS
@@ -3,26 +3,18 @@
#include "esphome/core/log.h"
namespace esphome::modbus_server {
using modbus::ModbusFunctionCode;
using modbus::ModbusExceptionCode;
using modbus::helpers::payload_to_number;
using modbus::helpers::registers_to_number;
static const char *const TAG = "modbus_server";
void ModbusServer::on_modbus_read_registers(uint8_t function_code, uint16_t start_address,
uint16_t number_of_registers) {
modbus::ServerResponseStatus ModbusServer::on_modbus_read_registers(uint16_t start_address,
uint16_t number_of_registers,
modbus::RegisterValues &registers) {
ESP_LOGV(TAG,
"Received read holding/input registers for device 0x%X. FC: 0x%X. Start address: 0x%X. Number of registers: "
"0x%X.",
this->address_, function_code, start_address, number_of_registers);
"Received read holding/input registers for device 0x%X. Start address: 0x%X. Number of registers: 0x%X.",
this->address_, start_address, number_of_registers);
if (number_of_registers == 0 || number_of_registers > modbus::MAX_NUM_OF_REGISTERS_TO_READ) {
ESP_LOGW(TAG, "Invalid number of registers %" PRIu16 ". Sending exception response.", number_of_registers);
this->send_error(function_code, ModbusExceptionCode::ILLEGAL_DATA_ADDRESS);
return;
}
std::vector<uint16_t> sixteen_bit_response;
for (uint16_t current_address = start_address; current_address < start_address + number_of_registers;) {
bool found = false;
for (auto *server_register : this->server_registers_) {
@@ -36,10 +28,7 @@ void ModbusServer::on_modbus_read_registers(uint8_t function_code, uint16_t star
server_register->address, static_cast<size_t>(server_register->value_type),
server_register->register_count, server_register->format_value(value, value_buf, sizeof(value_buf)));
std::vector<uint16_t> payload;
payload.reserve(server_register->register_count * 2);
modbus::helpers::number_to_payload(payload, value, server_register->value_type);
sixteen_bit_response.insert(sixteen_bit_response.end(), payload.cbegin(), payload.cend());
modbus::helpers::number_to_payload(registers, value, server_register->value_type);
current_address += server_register->register_count;
found = true;
break;
@@ -53,92 +42,37 @@ void ModbusServer::on_modbus_read_registers(uint8_t function_code, uint16_t star
"Could not match any register to address 0x%02X, but default allowed. "
"Returning default value: %" PRIu16 ".",
current_address, this->server_courtesy_response_.register_value);
sixteen_bit_response.push_back(this->server_courtesy_response_.register_value);
registers.push_back(this->server_courtesy_response_.register_value);
current_address += 1; // Just increment by 1, as the default response is a single register
} else {
ESP_LOGW(TAG,
"Could not match any register to address 0x%02X and default not allowed. Sending exception response.",
current_address);
this->send_error(function_code, ModbusExceptionCode::ILLEGAL_DATA_ADDRESS);
return;
return ModbusExceptionCode::ILLEGAL_DATA_ADDRESS;
}
}
}
std::vector<uint8_t> response;
if (number_of_registers != sixteen_bit_response.size())
ESP_LOGW(TAG, "Response size not matched to request register count.");
response.push_back(sixteen_bit_response.size() * 2); // actual byte count
for (auto v : sixteen_bit_response) {
auto decoded_value = decode_value(v);
response.push_back(decoded_value[0]);
response.push_back(decoded_value[1]);
}
this->send(function_code, response);
return {};
}
void ModbusServer::on_modbus_write_registers(uint8_t function_code, const std::vector<uint8_t> &data) {
uint16_t number_of_registers;
uint16_t payload_offset;
modbus::ServerResponseStatus ModbusServer::on_modbus_write_registers(uint16_t start_address,
const modbus::RegisterValues &registers) {
// registers holds the values to write in host byte order; its size is the register count.
ESP_LOGV(TAG, "Received write registers for device 0x%X. Start address: 0x%X. Number of registers: 0x%zX.",
this->address_, start_address, registers.size());
if (static_cast<ModbusFunctionCode>(function_code) == ModbusFunctionCode::WRITE_MULTIPLE_REGISTERS) {
if (data.size() < 5) {
ESP_LOGW(TAG, "Write multiple registers data too short (%zu bytes)", data.size());
this->send_error(function_code, ModbusExceptionCode::ILLEGAL_DATA_VALUE);
return;
}
number_of_registers = uint16_t(data[3]) | (uint16_t(data[2]) << 8);
if (number_of_registers == 0 || number_of_registers > modbus::MAX_NUM_OF_REGISTERS_TO_WRITE) {
ESP_LOGW(TAG, "Invalid number of registers %" PRIu16 ". Sending exception response.", number_of_registers);
this->send_error(function_code, ModbusExceptionCode::ILLEGAL_DATA_VALUE);
return;
}
uint16_t payload_size = data[4];
if (payload_size != number_of_registers * 2) {
ESP_LOGW(TAG,
"Payload size of %" PRIu16 " bytes is not 2 times the number of registers (%" PRIu16
"). Sending exception response.",
payload_size, number_of_registers);
this->send_error(function_code, ModbusExceptionCode::ILLEGAL_DATA_VALUE);
return;
}
if (data.size() < 5 + payload_size) {
ESP_LOGW(TAG, "Write multiple registers payload truncated (%zu bytes, expected %u)", data.size(),
5 + payload_size);
this->send_error(function_code, ModbusExceptionCode::ILLEGAL_DATA_VALUE);
return;
}
payload_offset = 5;
} else if (static_cast<ModbusFunctionCode>(function_code) == ModbusFunctionCode::WRITE_SINGLE_REGISTER) {
if (data.size() < 4) {
ESP_LOGW(TAG, "Write single register data too short (%zu bytes)", data.size());
this->send_error(function_code, ModbusExceptionCode::ILLEGAL_DATA_VALUE);
return;
}
number_of_registers = 1;
payload_offset = 2;
} else {
ESP_LOGW(TAG, "Invalid function code 0x%X. Sending exception response.", function_code);
this->send_error(function_code, ModbusExceptionCode::ILLEGAL_FUNCTION);
return;
}
uint16_t start_address = uint16_t(data[1]) | (uint16_t(data[0]) << 8);
ESP_LOGD(TAG,
"Received write holding registers for device 0x%X. FC: 0x%X. Start address: 0x%X. Number of registers: "
"0x%X.",
this->address_, function_code, start_address, number_of_registers);
auto for_each_register = [this, start_address, number_of_registers, payload_offset](
const std::function<bool(ServerRegister *, uint16_t offset)> &callback) -> bool {
uint16_t offset = payload_offset;
for (uint16_t current_address = start_address; current_address < start_address + number_of_registers;) {
auto for_each_register =
[this, start_address,
&registers](const std::function<bool(ServerRegister *, uint16_t register_offset)> &callback) -> bool {
uint16_t register_offset = 0;
for (uint32_t current_address = start_address; current_address < start_address + registers.size();) {
bool ok = false;
for (auto *server_register : this->server_registers_) {
if (server_register->address == current_address) {
ok = callback(server_register, offset);
ok = callback(server_register, register_offset);
current_address += server_register->register_count;
offset += server_register->register_count * sizeof(uint16_t);
register_offset += server_register->register_count;
break;
}
}
@@ -150,36 +84,41 @@ void ModbusServer::on_modbus_write_registers(uint8_t function_code, const std::v
return true;
};
// check all registers are writable before writing to any of them:
if (!for_each_register([](ServerRegister *server_register, uint16_t offset) -> bool {
return server_register->write_lambda != nullptr;
})) {
ESP_LOGW(TAG, "Invalid register address. Sending exception response.");
this->send_error(function_code, ModbusExceptionCode::ILLEGAL_DATA_ADDRESS);
return;
}
// Actually write to the registers:
if (!for_each_register([&data](ServerRegister *server_register, uint16_t offset) {
bool error = false;
int64_t number = payload_to_number(data, server_register->value_type, offset, 0xFFFFFFFF, &error);
if (error) {
return false;
} else {
return server_register->write_lambda(number);
// Pre-flight: every targeted register must be writable AND have its full value present in the request,
// so we never apply a partial write before discovering a problem. The commit pass below re-runs
// registers_to_number rather than caching the decoded values: using the same function for the check and
// the write keeps a single source of truth for the decode bound, independent of how register_count was set.
ModbusExceptionCode precheck = ModbusExceptionCode::ILLEGAL_DATA_ADDRESS; // unmatched or unwritable register
if (!for_each_register([&precheck, &registers](ServerRegister *server_register, uint16_t register_offset) -> bool {
if (server_register->write_lambda == nullptr) {
return false; // unwritable -> ILLEGAL_DATA_ADDRESS
}
bool error = false;
registers_to_number(registers.data() + register_offset, registers.size() - register_offset,
server_register->value_type, &error);
if (error) {
precheck = ModbusExceptionCode::ILLEGAL_DATA_VALUE; // request doesn't supply the full value
return false;
}
return true;
})) {
ESP_LOGW(TAG, "Could not write all registers. Sending exception response.");
this->send_error(function_code, ModbusExceptionCode::SERVICE_DEVICE_FAILURE);
return;
ESP_LOGW(TAG, "Write request rejected before applying any register. Sending exception response.");
return precheck;
}
std::vector<uint8_t> response;
response.reserve(6);
response.push_back(this->address_);
response.push_back(function_code);
response.insert(response.end(), data.begin(), data.begin() + 4);
this->send_raw(response);
// Commit: every value is known writable and decodable, so the only failure now is a user write callback
// rejecting the value at runtime -- which cannot be rolled back.
if (!for_each_register([&registers](ServerRegister *server_register, uint16_t register_offset) {
int64_t number = registers_to_number(registers.data() + register_offset, registers.size() - register_offset,
server_register->value_type);
return server_register->write_lambda(number);
})) {
ESP_LOGW(TAG, "A register write callback failed mid-sequence; earlier writes were already applied.");
return ModbusExceptionCode::SERVICE_DEVICE_FAILURE;
}
// Success: the caller builds the write response (an echo of the request header).
return {};
}
void ModbusServer::dump_config() {
@@ -98,9 +98,11 @@ class ModbusServer : public Component, public modbus::ModbusServerDevice {
/// Registers a server register with the controller. Called by esphomes code generator
void add_server_register(ServerRegister *server_register) { server_registers_.push_back(server_register); }
/// called when a modbus request (function code 0x03 or 0x04) was parsed without errors
void on_modbus_read_registers(uint8_t function_code, uint16_t start_address, uint16_t number_of_registers) final;
modbus::ServerResponseStatus on_modbus_read_registers(uint16_t start_address, uint16_t number_of_registers,
modbus::RegisterValues &registers) final;
/// called when a modbus request (function code 0x06 or 0x10) was parsed without errors
void on_modbus_write_registers(uint8_t function_code, const std::vector<uint8_t> &data) final;
modbus::ServerResponseStatus on_modbus_write_registers(uint16_t start_address,
const modbus::RegisterValues &registers) final;
/// Called by esphome generated code to set the server courtesy response object
void set_server_courtesy_response(const ServerCourtesyResponse &server_courtesy_response) {
this->server_courtesy_response_ = server_courtesy_response;
+3 -3
View File
@@ -75,16 +75,16 @@ void MPL3115A2Component::update() {
float altitude = 0, pressure = 0;
if (this->altitude_ != nullptr) {
int32_t alt = encode_uint32(buffer[0], buffer[1], buffer[2], 0);
altitude = float(alt) / 65536.0;
altitude = float(alt) / 65536.0f;
this->altitude_->publish_state(altitude);
} else {
uint32_t p = encode_uint32(0, buffer[0], buffer[1], buffer[2]);
pressure = float(p) / 6400.0;
pressure = float(p) / 6400.0f;
if (this->pressure_ != nullptr)
this->pressure_->publish_state(pressure);
}
int16_t t = encode_uint16(buffer[3], buffer[4]);
float temperature = float(t) / 256.0;
float temperature = float(t) / 256.0f;
if (this->temperature_ != nullptr)
this->temperature_->publish_state(temperature);
+2 -2
View File
@@ -115,9 +115,9 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo
// max_temp
root[MQTT_MAX_TEMP] = traits.get_visual_max_temperature();
// target_temp_step
root[MQTT_TARGET_TEMPERATURE_STEP] = roundf(traits.get_visual_target_temperature_step() * 10) * 0.1;
root[MQTT_TARGET_TEMPERATURE_STEP] = roundf(traits.get_visual_target_temperature_step() * 10) * 0.1f;
// current_temp_step
root[MQTT_CURRENT_TEMPERATURE_STEP] = roundf(traits.get_visual_current_temperature_step() * 10) * 0.1;
root[MQTT_CURRENT_TEMPERATURE_STEP] = roundf(traits.get_visual_current_temperature_step() * 10) * 0.1f;
// temperature units are always coerced to Celsius internally
root[MQTT_TEMPERATURE_UNIT] = "C";
+1 -1
View File
@@ -10,7 +10,7 @@ static const char *const TAG = "msa3xx";
const uint8_t MSA_3XX_PART_ID = 0x13;
const float GRAVITY_EARTH = 9.80665f;
const float LSB_COEFF = 1000.0f / (GRAVITY_EARTH * 3.9); // LSB to 1 LSB = 3.9mg = 0.0039g
const float LSB_COEFF = 1000.0f / (GRAVITY_EARTH * 3.9f); // LSB to 1 LSB = 3.9mg = 0.0039g
const float G_OFFSET_MIN = -4.5f; // -127...127 LSB = +- 0.4953g = +- 4.857 m/s^2 => +- 4.5 for the safe
const float G_OFFSET_MAX = 4.5f; // -127...127 LSB = +- 0.4953g = +- 4.857 m/s^2 => +- 4.5 for the safe
@@ -32,7 +32,7 @@ CONFIG_SCHEMA = cv.All(
)
.extend(CONFIG_BINARY_SENSOR_SCHEMA)
.extend(cv.polling_component_schema("never")),
cv.AtLeastOne(
cv.has_at_least_one_key(
CONF_PAGE_ID,
CONF_COMPONENT_ID,
CONF_COMPONENT_NAME,
@@ -176,7 +176,7 @@ void Nextion::goto_page(const char *page) { this->add_no_result_to_queue_with_pr
void Nextion::goto_page(uint8_t page) { this->add_no_result_to_queue_with_printf_("page", "page %i", page); }
void Nextion::set_backlight_brightness(float brightness) {
if (brightness < 0 || brightness > 1.0) {
if (brightness < 0 || brightness > 1.0f) {
ESP_LOGD(TAG, "Brightness out of bounds (0-1.0)");
return;
}
@@ -63,7 +63,7 @@ CONFIG_SCHEMA = cv.All(
)
.extend(CONFIG_SENSOR_COMPONENT_SCHEMA)
.extend(cv.polling_component_schema("never")),
cv.ExactlyOne(CONF_COMPONENT_ID, CONF_COMPONENT_NAME, CONF_VARIABLE_NAME),
cv.has_exactly_one_key(CONF_COMPONENT_ID, CONF_COMPONENT_NAME, CONF_VARIABLE_NAME),
_validate,
)
@@ -24,7 +24,7 @@ CONFIG_SCHEMA = cv.All(
switch.switch_schema(NextionSwitch)
.extend(CONFIG_SWITCH_COMPONENT_SCHEMA)
.extend(cv.polling_component_schema("never")),
cv.ExactlyOne(CONF_COMPONENT_NAME, CONF_VARIABLE_NAME),
cv.has_exactly_one_key(CONF_COMPONENT_NAME, CONF_VARIABLE_NAME),
)
@@ -52,7 +52,7 @@ CONFIG_SCHEMA = cv.All(
}
)
.extend(cv.COMPONENT_SCHEMA),
cv.ExactlyOne(CONF_NDEF_CONTAINS, CONF_TAG_ID, CONF_UID),
cv.has_exactly_one_key(CONF_NDEF_CONTAINS, CONF_TAG_ID, CONF_UID),
)
+2 -2
View File
@@ -208,7 +208,7 @@ _NUMBER_SCHEMA = (
cv.Optional(CONF_ABOVE): cv.templatable(cv.float_),
cv.Optional(CONF_BELOW): cv.templatable(cv.float_),
},
cv.AtLeastOne(CONF_ABOVE, CONF_BELOW),
cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW),
),
cv.Optional(CONF_UNIT_OF_MEASUREMENT): validate_unit_of_measurement,
cv.Optional(CONF_MODE, default="AUTO"): cv.enum(NUMBER_MODES, upper=True),
@@ -323,7 +323,7 @@ NUMBER_IN_RANGE_CONDITION_SCHEMA = cv.All(
cv.Optional(CONF_ABOVE): cv.float_,
cv.Optional(CONF_BELOW): cv.float_,
},
cv.AtLeastOne(CONF_ABOVE, CONF_BELOW),
cv.has_at_least_one_key(CONF_ABOVE, CONF_BELOW),
)
+1 -1
View File
@@ -541,7 +541,7 @@ void OpenTherm::debug_error(OpenThermError &error) const {
error.capture, error.bit_pos);
}
float OpenthermData::f88() { return ((float) this->s16()) / 256.0; }
float OpenthermData::f88() { return ((float) this->s16()) / 256.0f; }
void OpenthermData::f88(float value) { this->s16((int16_t) (value * 256)); }

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