mirror of
https://github.com/esphome/esphome.git
synced 2026-06-29 12:06:13 +00:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7984349c36 | |||
| 0cbbd64577 | |||
| a618ee11b4 | |||
| 6251c26cc6 | |||
| 4fbe0d87ec | |||
| 24d8e99c50 | |||
| 14b6a0ede1 | |||
| 1793ca5eac | |||
| 62e19bcb27 | |||
| 84d1c34c28 | |||
| f78cbf9200 | |||
| eb711381d3 | |||
| 9a1daa5247 | |||
| f3d61ca3e1 | |||
| 29dfd820c6 | |||
| 8bc5b97298 | |||
| 7a64163c4f | |||
| dfe14f9c3a | |||
| 26cf373ae7 | |||
| 94ccddf176 | |||
| 2ec24505d0 | |||
| 4f7faa7712 | |||
| b3dcaac262 | |||
| ee118d384a | |||
| 8d36167e11 | |||
| 6d559a32df | |||
| bf0d31b3ab | |||
| d8ffb732b7 | |||
| 9e8261056c | |||
| 5f311d281e | |||
| a336ad6732 | |||
| 8434d54cc7 | |||
| b62f7a41c9 | |||
| 4ebecf514a | |||
| 2f32c88ae5 | |||
| 95449068e7 | |||
| 556def78aa | |||
| b7803cf9b5 | |||
| 40820287f1 | |||
| 6210dfb4d0 | |||
| 45c712b17b |
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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 \
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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(),
|
||||
)
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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,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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 ®isters) {
|
||||
return ModbusExceptionCode::ILLEGAL_FUNCTION;
|
||||
};
|
||||
virtual ServerResponseStatus on_modbus_read_input_registers(uint16_t start_address, uint16_t number_of_registers,
|
||||
RegisterValues ®isters) {
|
||||
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 ®isters) {
|
||||
return this->on_modbus_read_registers(start_address, number_of_registers, registers);
|
||||
};
|
||||
virtual ServerResponseStatus on_modbus_write_registers(uint16_t start_address, const RegisterValues ®isters) {
|
||||
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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 ®isters) {
|
||||
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 ®isters) {
|
||||
// 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,
|
||||
®isters](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, ®isters](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([®isters](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 ®isters) 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 ®isters) 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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user