From 66019feb8f7e9d4e0e82d4d9dc08b29452a62bd1 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Fri, 24 Apr 2026 09:27:37 -0400 Subject: [PATCH] add year and track number sensors (will remove them from text_sensor in a future PR) --- .../components/sendspin/sensor/__init__.py | 72 ++++++++++++++----- .../sendspin/sensor/sendspin_sensor.cpp | 51 ++++++++++--- .../sendspin/sensor/sendspin_sensor.h | 12 +++- tests/components/sendspin/common-sensor.yaml | 6 ++ 4 files changed, 113 insertions(+), 28 deletions(-) diff --git a/esphome/components/sendspin/sensor/__init__.py b/esphome/components/sendspin/sensor/__init__.py index cdf1c0da354..dc9b86c2a36 100644 --- a/esphome/components/sendspin/sensor/__init__.py +++ b/esphome/components/sendspin/sensor/__init__.py @@ -1,7 +1,13 @@ import esphome.codegen as cg from esphome.components import sensor import esphome.config_validation as cv -from esphome.const import CONF_ID, CONF_TYPE, STATE_CLASS_MEASUREMENT, UNIT_MILLISECOND +from esphome.const import ( + CONF_ID, + CONF_TYPE, + CONF_YEAR, + STATE_CLASS_MEASUREMENT, + UNIT_MILLISECOND, +) from esphome.types import ConfigType from .. import CONF_SENDSPIN_ID, SendspinHub, request_metadata_support, sendspin_ns @@ -9,17 +15,30 @@ from .. import CONF_SENDSPIN_ID, SendspinHub, request_metadata_support, sendspin CODEOWNERS = ["@kahrendt"] DEPENDENCIES = ["sendspin"] +CONF_TRACK = "track" +CONF_TRACK_PROGRESS = "track_progress" +CONF_TRACK_DURATION = "track_duration" + SendspinTrackProgressSensor = sendspin_ns.class_( "SendspinTrackProgressSensor", sensor.Sensor, cg.PollingComponent, ) -SendspinTrackDurationSensor = sendspin_ns.class_( - "SendspinTrackDurationSensor", +SendspinMetadataSensor = sendspin_ns.class_( + "SendspinMetadataSensor", sensor.Sensor, cg.Component, ) +SendspinNumericMetadataTypes = sendspin_ns.enum( + "SendspinNumericMetadataTypes", is_class=True +) +_METADATA_TYPE_ENUM = { + CONF_TRACK_DURATION: SendspinNumericMetadataTypes.TRACK_DURATION, + CONF_YEAR: SendspinNumericMetadataTypes.YEAR, + CONF_TRACK: SendspinNumericMetadataTypes.TRACK, +} + def _request_roles(config: ConfigType) -> ConfigType: """Request the necessary Sendspin roles for the sensor.""" @@ -28,25 +47,39 @@ def _request_roles(config: ConfigType) -> ConfigType: return config -_SENSOR_SCHEMA = sensor.sensor_schema( - accuracy_decimals=0, - state_class=STATE_CLASS_MEASUREMENT, - unit_of_measurement=UNIT_MILLISECOND, -).extend( - { - cv.GenerateID(CONF_SENDSPIN_ID): cv.use_id(SendspinHub), - } -) +_HUB_ID_SCHEMA = cv.Schema({cv.GenerateID(CONF_SENDSPIN_ID): cv.use_id(SendspinHub)}) + + +def _metadata_schema(**sensor_kwargs): + """Schema for event-driven numeric metadata sensors (duration/year/track).""" + return ( + sensor.sensor_schema( + SendspinMetadataSensor, + accuracy_decimals=0, + **sensor_kwargs, + ) + .extend(_HUB_ID_SCHEMA) + .extend(cv.COMPONENT_SCHEMA) + ) + CONFIG_SCHEMA = cv.All( cv.typed_schema( { - "track_progress": _SENSOR_SCHEMA.extend( - {cv.GenerateID(): cv.declare_id(SendspinTrackProgressSensor)} - ).extend(cv.polling_component_schema("1s")), - "track_duration": _SENSOR_SCHEMA.extend( - {cv.GenerateID(): cv.declare_id(SendspinTrackDurationSensor)} - ).extend(cv.COMPONENT_SCHEMA), + CONF_TRACK_PROGRESS: sensor.sensor_schema( + SendspinTrackProgressSensor, + accuracy_decimals=0, + state_class=STATE_CLASS_MEASUREMENT, + unit_of_measurement=UNIT_MILLISECOND, + ) + .extend(_HUB_ID_SCHEMA) + .extend(cv.polling_component_schema("1s")), + CONF_TRACK_DURATION: _metadata_schema( + state_class=STATE_CLASS_MEASUREMENT, + unit_of_measurement=UNIT_MILLISECOND, + ), + CONF_YEAR: _metadata_schema(), + CONF_TRACK: _metadata_schema(), }, key=CONF_TYPE, ), @@ -60,3 +93,6 @@ async def to_code(config: ConfigType) -> None: await cg.register_component(var, config) await cg.register_parented(var, config[CONF_SENDSPIN_ID]) await sensor.register_sensor(var, config) + + if (metadata_type := _METADATA_TYPE_ENUM.get(config[CONF_TYPE])) is not None: + cg.add(var.set_metadata_type(metadata_type)) diff --git a/esphome/components/sendspin/sensor/sendspin_sensor.cpp b/esphome/components/sendspin/sensor/sendspin_sensor.cpp index 8710d6dbe65..4efbd72a384 100644 --- a/esphome/components/sendspin/sensor/sendspin_sensor.cpp +++ b/esphome/components/sendspin/sensor/sendspin_sensor.cpp @@ -11,7 +11,7 @@ static const char *const TAG = "sendspin.sensor"; // --- SendspinTrackProgressSensor --- void SendspinTrackProgressSensor::dump_config() { - LOG_SENSOR("", "Sendspin Track Progress", this); + LOG_SENSOR("", "Track Progress", this); LOG_UPDATE_INTERVAL(this); } @@ -42,22 +42,55 @@ void SendspinTrackProgressSensor::setup() { // speed, giving us a fresh value on every poll. void SendspinTrackProgressSensor::update() { this->publish_state(this->parent_->get_track_progress_ms()); } -// --- SendspinTrackDurationSensor --- +// --- SendspinMetadataSensor --- -void SendspinTrackDurationSensor::dump_config() { LOG_SENSOR("", "Sendspin Track Duration", this); } +void SendspinMetadataSensor::dump_config() { + switch (this->metadata_type_) { + case SendspinNumericMetadataTypes::TRACK_DURATION: + LOG_SENSOR("", "Track Duration", this); + break; + case SendspinNumericMetadataTypes::YEAR: + LOG_SENSOR("", "Year", this); + break; + case SendspinNumericMetadataTypes::TRACK: + LOG_SENSOR("", "Track", this); + break; + } +} // THREAD CONTEXT: Main loop. The registered metadata callback also fires on the main loop // (SendspinHub dispatches metadata from client_->loop()). -void SendspinTrackDurationSensor::setup() { - this->parent_->add_metadata_update_callback([this](const sendspin::ServerMetadataStateObject &metadata) { - if (metadata.progress.has_value()) { - this->publish_if_changed_(metadata.progress.value().track_duration); +void SendspinMetadataSensor::setup() { + switch (this->metadata_type_) { + case SendspinNumericMetadataTypes::TRACK_DURATION: { + this->parent_->add_metadata_update_callback([this](const sendspin::ServerMetadataStateObject &metadata) { + if (metadata.progress.has_value()) { + this->publish_if_changed_(metadata.progress.value().track_duration); + } + }); + break; } - }); + case SendspinNumericMetadataTypes::YEAR: { + this->parent_->add_metadata_update_callback([this](const sendspin::ServerMetadataStateObject &metadata) { + if (metadata.year.has_value() && metadata.year.value() <= 9999) { + this->publish_if_changed_(metadata.year.value()); + } + }); + break; + } + case SendspinNumericMetadataTypes::TRACK: { + this->parent_->add_metadata_update_callback([this](const sendspin::ServerMetadataStateObject &metadata) { + if (metadata.track.has_value() && metadata.track.value() <= 9999) { + this->publish_if_changed_(metadata.track.value()); + } + }); + break; + } + } } // Dedup to avoid frontend churn; Sensor::publish_state always notifies without checking for changes. -void SendspinTrackDurationSensor::publish_if_changed_(float value) { +void SendspinMetadataSensor::publish_if_changed_(float value) { if (this->get_raw_state() != value) { this->publish_state(value); } diff --git a/esphome/components/sendspin/sensor/sendspin_sensor.h b/esphome/components/sendspin/sensor/sendspin_sensor.h index 0a9c60dee33..969c1272837 100644 --- a/esphome/components/sendspin/sensor/sendspin_sensor.h +++ b/esphome/components/sendspin/sensor/sendspin_sensor.h @@ -16,13 +16,23 @@ class SendspinTrackProgressSensor : public sensor::Sensor, public SendspinPollin void update() override; }; -class SendspinTrackDurationSensor : public sensor::Sensor, public SendspinChild { +enum class SendspinNumericMetadataTypes { + TRACK_DURATION, + YEAR, + TRACK, +}; + +class SendspinMetadataSensor : public sensor::Sensor, public SendspinChild { public: void dump_config() override; void setup() override; + void set_metadata_type(SendspinNumericMetadataTypes metadata_type) { this->metadata_type_ = metadata_type; } + protected: void publish_if_changed_(float value); + + SendspinNumericMetadataTypes metadata_type_; }; } // namespace esphome::sendspin_ diff --git a/tests/components/sendspin/common-sensor.yaml b/tests/components/sendspin/common-sensor.yaml index dc335b68421..6d9745cff94 100644 --- a/tests/components/sendspin/common-sensor.yaml +++ b/tests/components/sendspin/common-sensor.yaml @@ -7,3 +7,9 @@ sensor: - platform: sendspin name: "Sendspin Track Duration" type: track_duration + - platform: sendspin + name: "Sendspin Year" + type: year + - platform: sendspin + name: "Sendspin Track" + type: track