mirror of
https://github.com/esphome/esphome.git
synced 2026-08-22 22:26:21 +00:00
[openthread_info] Add more sensors for Openthread info (#17276)
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
#include "openthread_info_sensor.h"
|
||||
#if defined(USE_OPENTHREAD) && defined(USE_SENSOR)
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::openthread_info {
|
||||
|
||||
static const char *const TAG = "openthread_info";
|
||||
|
||||
void ParentAverageRssiOpenThreadInfo::dump_config() { LOG_SENSOR("", "Parent Average RSSI", this); }
|
||||
void ParentLastRssiOpenThreadInfo::dump_config() { LOG_SENSOR("", "Parent Last RSSI", this); }
|
||||
void ParentLinkQualityInOpenThreadInfo::dump_config() { LOG_SENSOR("", "Parent Link Quality In", this); }
|
||||
void ParentLinkQualityOutOpenThreadInfo::dump_config() { LOG_SENSOR("", "Parent Link Quality Out", this); }
|
||||
void TxTotalOpenThreadInfo::dump_config() { LOG_SENSOR("", "TX Total", this); }
|
||||
void TxRetriesOpenThreadInfo::dump_config() { LOG_SENSOR("", "TX Retries", this); }
|
||||
void TxErrCcaOpenThreadInfo::dump_config() { LOG_SENSOR("", "TX CCA Errors", this); }
|
||||
void TxErrAbortOpenThreadInfo::dump_config() { LOG_SENSOR("", "TX Abort Errors", this); }
|
||||
void RxTotalOpenThreadInfo::dump_config() { LOG_SENSOR("", "RX Total", this); }
|
||||
void RxErrFcsOpenThreadInfo::dump_config() { LOG_SENSOR("", "RX FCS Errors", this); }
|
||||
void AttachAttemptsOpenThreadInfo::dump_config() { LOG_SENSOR("", "Attach Attempts", this); }
|
||||
void ParentChangesOpenThreadInfo::dump_config() { LOG_SENSOR("", "Parent Changes", this); }
|
||||
void PartitionIdChangesOpenThreadInfo::dump_config() { LOG_SENSOR("", "Partition ID Changes", this); }
|
||||
|
||||
} // namespace esphome::openthread_info
|
||||
#endif
|
||||
@@ -0,0 +1,131 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#if defined(USE_OPENTHREAD) && defined(USE_SENSOR)
|
||||
|
||||
#include "openthread_info_text_sensor.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
|
||||
#include <openthread/link.h>
|
||||
#include <openthread/thread.h>
|
||||
|
||||
namespace esphome::openthread_info {
|
||||
|
||||
// Parent RSSI (average) in dBm. Only valid when device is a child; skips publish otherwise.
|
||||
class ParentAverageRssiOpenThreadInfo final : public OpenThreadInstancePollingComponent, public sensor::Sensor {
|
||||
public:
|
||||
void update_instance(otInstance *instance) override {
|
||||
int8_t rssi;
|
||||
if (otThreadGetParentAverageRssi(instance, &rssi) != OT_ERROR_NONE) {
|
||||
return;
|
||||
}
|
||||
this->publish_state(rssi);
|
||||
}
|
||||
void dump_config() override;
|
||||
};
|
||||
|
||||
// Parent RSSI (last received frame) in dBm. Only valid when device is a child.
|
||||
class ParentLastRssiOpenThreadInfo final : public OpenThreadInstancePollingComponent, public sensor::Sensor {
|
||||
public:
|
||||
void update_instance(otInstance *instance) override {
|
||||
int8_t rssi;
|
||||
if (otThreadGetParentLastRssi(instance, &rssi) != OT_ERROR_NONE) {
|
||||
return;
|
||||
}
|
||||
this->publish_state(rssi);
|
||||
}
|
||||
void dump_config() override;
|
||||
};
|
||||
|
||||
// Incoming link quality from parent (0-3). Only valid when device is a child.
|
||||
class ParentLinkQualityInOpenThreadInfo final : public OpenThreadInstancePollingComponent, public sensor::Sensor {
|
||||
public:
|
||||
void update_instance(otInstance *instance) override {
|
||||
otRouterInfo parent_info;
|
||||
if (otThreadGetParentInfo(instance, &parent_info) != OT_ERROR_NONE) {
|
||||
return;
|
||||
}
|
||||
this->publish_state(parent_info.mLinkQualityIn);
|
||||
}
|
||||
void dump_config() override;
|
||||
};
|
||||
|
||||
// Outgoing link quality to parent (0-3). Only valid when device is a child.
|
||||
class ParentLinkQualityOutOpenThreadInfo final : public OpenThreadInstancePollingComponent, public sensor::Sensor {
|
||||
public:
|
||||
void update_instance(otInstance *instance) override {
|
||||
otRouterInfo parent_info;
|
||||
if (otThreadGetParentInfo(instance, &parent_info) != OT_ERROR_NONE) {
|
||||
return;
|
||||
}
|
||||
this->publish_state(parent_info.mLinkQualityOut);
|
||||
}
|
||||
void dump_config() override;
|
||||
};
|
||||
|
||||
// --- MAC counters (otLinkGetCounters) — cumulative since boot ---
|
||||
|
||||
class TxTotalOpenThreadInfo final : public OpenThreadInstancePollingComponent, public sensor::Sensor {
|
||||
public:
|
||||
void update_instance(otInstance *instance) override { this->publish_state(otLinkGetCounters(instance)->mTxTotal); }
|
||||
void dump_config() override;
|
||||
};
|
||||
|
||||
class TxRetriesOpenThreadInfo final : public OpenThreadInstancePollingComponent, public sensor::Sensor {
|
||||
public:
|
||||
void update_instance(otInstance *instance) override { this->publish_state(otLinkGetCounters(instance)->mTxRetry); }
|
||||
void dump_config() override;
|
||||
};
|
||||
|
||||
class TxErrCcaOpenThreadInfo final : public OpenThreadInstancePollingComponent, public sensor::Sensor {
|
||||
public:
|
||||
void update_instance(otInstance *instance) override { this->publish_state(otLinkGetCounters(instance)->mTxErrCca); }
|
||||
void dump_config() override;
|
||||
};
|
||||
|
||||
class TxErrAbortOpenThreadInfo final : public OpenThreadInstancePollingComponent, public sensor::Sensor {
|
||||
public:
|
||||
void update_instance(otInstance *instance) override { this->publish_state(otLinkGetCounters(instance)->mTxErrAbort); }
|
||||
void dump_config() override;
|
||||
};
|
||||
|
||||
class RxTotalOpenThreadInfo final : public OpenThreadInstancePollingComponent, public sensor::Sensor {
|
||||
public:
|
||||
void update_instance(otInstance *instance) override { this->publish_state(otLinkGetCounters(instance)->mRxTotal); }
|
||||
void dump_config() override;
|
||||
};
|
||||
|
||||
class RxErrFcsOpenThreadInfo final : public OpenThreadInstancePollingComponent, public sensor::Sensor {
|
||||
public:
|
||||
void update_instance(otInstance *instance) override { this->publish_state(otLinkGetCounters(instance)->mRxErrFcs); }
|
||||
void dump_config() override;
|
||||
};
|
||||
|
||||
// --- MLE stability counters (otThreadGetMleCounters) — cumulative since boot ---
|
||||
|
||||
class AttachAttemptsOpenThreadInfo final : public OpenThreadInstancePollingComponent, public sensor::Sensor {
|
||||
public:
|
||||
void update_instance(otInstance *instance) override {
|
||||
this->publish_state(otThreadGetMleCounters(instance)->mAttachAttempts);
|
||||
}
|
||||
void dump_config() override;
|
||||
};
|
||||
|
||||
class ParentChangesOpenThreadInfo final : public OpenThreadInstancePollingComponent, public sensor::Sensor {
|
||||
public:
|
||||
void update_instance(otInstance *instance) override {
|
||||
this->publish_state(otThreadGetMleCounters(instance)->mParentChanges);
|
||||
}
|
||||
void dump_config() override;
|
||||
};
|
||||
|
||||
class PartitionIdChangesOpenThreadInfo final : public OpenThreadInstancePollingComponent, public sensor::Sensor {
|
||||
public:
|
||||
void update_instance(otInstance *instance) override {
|
||||
this->publish_state(otThreadGetMleCounters(instance)->mPartitionIdChanges);
|
||||
}
|
||||
void dump_config() override;
|
||||
};
|
||||
|
||||
} // namespace esphome::openthread_info
|
||||
#endif
|
||||
@@ -0,0 +1,188 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
DEVICE_CLASS_SIGNAL_STRENGTH,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
STATE_CLASS_TOTAL_INCREASING,
|
||||
UNIT_DECIBEL_MILLIWATT,
|
||||
UNIT_EMPTY,
|
||||
)
|
||||
|
||||
CONF_PARENT_AVERAGE_RSSI = "parent_average_rssi"
|
||||
CONF_PARENT_LAST_RSSI = "parent_last_rssi"
|
||||
CONF_PARENT_LINK_QUALITY_IN = "parent_link_quality_in"
|
||||
CONF_PARENT_LINK_QUALITY_OUT = "parent_link_quality_out"
|
||||
CONF_TX_TOTAL = "tx_total"
|
||||
CONF_TX_RETRIES = "tx_retries"
|
||||
CONF_TX_ERR_CCA = "tx_err_cca"
|
||||
CONF_TX_ERR_ABORT = "tx_err_abort"
|
||||
CONF_RX_TOTAL = "rx_total"
|
||||
CONF_RX_ERR_FCS = "rx_err_fcs"
|
||||
CONF_ATTACH_ATTEMPTS = "attach_attempts"
|
||||
CONF_PARENT_CHANGES = "parent_changes"
|
||||
CONF_PARTITION_ID_CHANGES = "partition_id_changes"
|
||||
|
||||
DEPENDENCIES = ["openthread"]
|
||||
|
||||
openthread_info_ns = cg.esphome_ns.namespace("openthread_info")
|
||||
ParentAverageRssiOpenThreadInfo = openthread_info_ns.class_(
|
||||
"ParentAverageRssiOpenThreadInfo", sensor.Sensor, cg.PollingComponent
|
||||
)
|
||||
ParentLastRssiOpenThreadInfo = openthread_info_ns.class_(
|
||||
"ParentLastRssiOpenThreadInfo", sensor.Sensor, cg.PollingComponent
|
||||
)
|
||||
ParentLinkQualityInOpenThreadInfo = openthread_info_ns.class_(
|
||||
"ParentLinkQualityInOpenThreadInfo", sensor.Sensor, cg.PollingComponent
|
||||
)
|
||||
ParentLinkQualityOutOpenThreadInfo = openthread_info_ns.class_(
|
||||
"ParentLinkQualityOutOpenThreadInfo", sensor.Sensor, cg.PollingComponent
|
||||
)
|
||||
TxTotalOpenThreadInfo = openthread_info_ns.class_(
|
||||
"TxTotalOpenThreadInfo", sensor.Sensor, cg.PollingComponent
|
||||
)
|
||||
TxRetriesOpenThreadInfo = openthread_info_ns.class_(
|
||||
"TxRetriesOpenThreadInfo", sensor.Sensor, cg.PollingComponent
|
||||
)
|
||||
TxErrCcaOpenThreadInfo = openthread_info_ns.class_(
|
||||
"TxErrCcaOpenThreadInfo", sensor.Sensor, cg.PollingComponent
|
||||
)
|
||||
TxErrAbortOpenThreadInfo = openthread_info_ns.class_(
|
||||
"TxErrAbortOpenThreadInfo", sensor.Sensor, cg.PollingComponent
|
||||
)
|
||||
RxTotalOpenThreadInfo = openthread_info_ns.class_(
|
||||
"RxTotalOpenThreadInfo", sensor.Sensor, cg.PollingComponent
|
||||
)
|
||||
RxErrFcsOpenThreadInfo = openthread_info_ns.class_(
|
||||
"RxErrFcsOpenThreadInfo", sensor.Sensor, cg.PollingComponent
|
||||
)
|
||||
AttachAttemptsOpenThreadInfo = openthread_info_ns.class_(
|
||||
"AttachAttemptsOpenThreadInfo", sensor.Sensor, cg.PollingComponent
|
||||
)
|
||||
ParentChangesOpenThreadInfo = openthread_info_ns.class_(
|
||||
"ParentChangesOpenThreadInfo", sensor.Sensor, cg.PollingComponent
|
||||
)
|
||||
PartitionIdChangesOpenThreadInfo = openthread_info_ns.class_(
|
||||
"PartitionIdChangesOpenThreadInfo", sensor.Sensor, cg.PollingComponent
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_PARENT_AVERAGE_RSSI): sensor.sensor_schema(
|
||||
ParentAverageRssiOpenThreadInfo,
|
||||
unit_of_measurement=UNIT_DECIBEL_MILLIWATT,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
).extend(cv.polling_component_schema("5s")),
|
||||
cv.Optional(CONF_PARENT_LAST_RSSI): sensor.sensor_schema(
|
||||
ParentLastRssiOpenThreadInfo,
|
||||
unit_of_measurement=UNIT_DECIBEL_MILLIWATT,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
).extend(cv.polling_component_schema("5s")),
|
||||
cv.Optional(CONF_PARENT_LINK_QUALITY_IN): sensor.sensor_schema(
|
||||
ParentLinkQualityInOpenThreadInfo,
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
).extend(cv.polling_component_schema("5s")),
|
||||
cv.Optional(CONF_PARENT_LINK_QUALITY_OUT): sensor.sensor_schema(
|
||||
ParentLinkQualityOutOpenThreadInfo,
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
).extend(cv.polling_component_schema("5s")),
|
||||
cv.Optional(CONF_TX_TOTAL): sensor.sensor_schema(
|
||||
TxTotalOpenThreadInfo,
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
).extend(cv.polling_component_schema("30s")),
|
||||
cv.Optional(CONF_TX_RETRIES): sensor.sensor_schema(
|
||||
TxRetriesOpenThreadInfo,
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
).extend(cv.polling_component_schema("30s")),
|
||||
cv.Optional(CONF_TX_ERR_CCA): sensor.sensor_schema(
|
||||
TxErrCcaOpenThreadInfo,
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
).extend(cv.polling_component_schema("30s")),
|
||||
cv.Optional(CONF_TX_ERR_ABORT): sensor.sensor_schema(
|
||||
TxErrAbortOpenThreadInfo,
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
).extend(cv.polling_component_schema("30s")),
|
||||
cv.Optional(CONF_RX_TOTAL): sensor.sensor_schema(
|
||||
RxTotalOpenThreadInfo,
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
).extend(cv.polling_component_schema("30s")),
|
||||
cv.Optional(CONF_RX_ERR_FCS): sensor.sensor_schema(
|
||||
RxErrFcsOpenThreadInfo,
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
).extend(cv.polling_component_schema("30s")),
|
||||
cv.Optional(CONF_ATTACH_ATTEMPTS): sensor.sensor_schema(
|
||||
AttachAttemptsOpenThreadInfo,
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
).extend(cv.polling_component_schema("30s")),
|
||||
cv.Optional(CONF_PARENT_CHANGES): sensor.sensor_schema(
|
||||
ParentChangesOpenThreadInfo,
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
).extend(cv.polling_component_schema("30s")),
|
||||
cv.Optional(CONF_PARTITION_ID_CHANGES): sensor.sensor_schema(
|
||||
PartitionIdChangesOpenThreadInfo,
|
||||
unit_of_measurement=UNIT_EMPTY,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_TOTAL_INCREASING,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
).extend(cv.polling_component_schema("30s")),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def setup_conf(config: dict, key: str):
|
||||
if conf := config.get(key):
|
||||
var = await sensor.new_sensor(conf)
|
||||
await cg.register_component(var, conf)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
await setup_conf(config, CONF_PARENT_AVERAGE_RSSI)
|
||||
await setup_conf(config, CONF_PARENT_LAST_RSSI)
|
||||
await setup_conf(config, CONF_PARENT_LINK_QUALITY_IN)
|
||||
await setup_conf(config, CONF_PARENT_LINK_QUALITY_OUT)
|
||||
await setup_conf(config, CONF_TX_TOTAL)
|
||||
await setup_conf(config, CONF_TX_RETRIES)
|
||||
await setup_conf(config, CONF_TX_ERR_CCA)
|
||||
await setup_conf(config, CONF_TX_ERR_ABORT)
|
||||
await setup_conf(config, CONF_RX_TOTAL)
|
||||
await setup_conf(config, CONF_RX_ERR_FCS)
|
||||
await setup_conf(config, CONF_ATTACH_ATTEMPTS)
|
||||
await setup_conf(config, CONF_PARENT_CHANGES)
|
||||
await setup_conf(config, CONF_PARTITION_ID_CHANGES)
|
||||
@@ -28,3 +28,32 @@ text_sensor:
|
||||
name: "PAN ID"
|
||||
ext_pan_id:
|
||||
name: "Extended PAN ID"
|
||||
|
||||
sensor:
|
||||
- platform: openthread_info
|
||||
parent_average_rssi:
|
||||
name: "Parent Average RSSI"
|
||||
parent_last_rssi:
|
||||
name: "Parent Last RSSI"
|
||||
parent_link_quality_in:
|
||||
name: "Parent Link Quality In"
|
||||
parent_link_quality_out:
|
||||
name: "Parent Link Quality Out"
|
||||
tx_total:
|
||||
name: "TX Total"
|
||||
tx_retries:
|
||||
name: "TX Retries"
|
||||
tx_err_cca:
|
||||
name: "TX CCA Errors"
|
||||
tx_err_abort:
|
||||
name: "TX Abort Errors"
|
||||
rx_total:
|
||||
name: "RX Total"
|
||||
rx_err_fcs:
|
||||
name: "RX FCS Errors"
|
||||
attach_attempts:
|
||||
name: "Attach Attempts"
|
||||
parent_changes:
|
||||
name: "Parent Changes"
|
||||
partition_id_changes:
|
||||
name: "Partition ID Changes"
|
||||
|
||||
Reference in New Issue
Block a user