add year and track number sensors (will remove them from text_sensor in a future PR)

This commit is contained in:
Kevin Ahrendt
2026-04-24 09:27:37 -04:00
parent a179a0de22
commit 66019feb8f
4 changed files with 113 additions and 28 deletions
+54 -18
View File
@@ -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))
@@ -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);
}
@@ -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_
@@ -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