mirror of
https://github.com/esphome/esphome.git
synced 2024-12-26 15:34:53 +01:00
openthread: add text sensors
This commit is contained in:
parent
958ab7fdd8
commit
2519f87cd8
5 changed files with 381 additions and 3 deletions
0
esphome/components/openthread_info/__init__.py
Normal file
0
esphome/components/openthread_info/__init__.py
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
|
||||||
|
#include "openthread_info_text_sensor.h"
|
||||||
|
#ifdef USE_OPENTHREAD
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace openthread_info {
|
||||||
|
|
||||||
|
static const char *const TAG = "openthread_info";
|
||||||
|
|
||||||
|
void IPAddressOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "OpenThreadInfo IPAddress", this); }
|
||||||
|
void RoleOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "OpenThreadInfo Role", this); }
|
||||||
|
void ChannelOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "OpenThreadInfo Role", this); }
|
||||||
|
void Rloc16OpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "OpenThreadInfo Rloc16", this); }
|
||||||
|
void ExtAddrOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "OpenThreadInfo ExtAddr", this); }
|
||||||
|
void Eui64OpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "OpenThreadInfo Eui64", this); }
|
||||||
|
void NetworkNameOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "OpenThreadInfo Network Name", this); }
|
||||||
|
void NetworkKeyOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "OpenThreadInfo Network Key", this); }
|
||||||
|
void PanIdOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "OpenThreadInfo PAN ID", this); }
|
||||||
|
void ExtPanIdOpenThreadInfo::dump_config() { LOG_TEXT_SENSOR("", "OpenThreadInfo Extended PAN ID", this); }
|
||||||
|
|
||||||
|
} // namespace openthread_info
|
||||||
|
} // namespace esphome
|
||||||
|
#endif
|
228
esphome/components/openthread_info/openthread_info_text_sensor.h
Normal file
228
esphome/components/openthread_info/openthread_info_text_sensor.h
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/text_sensor/text_sensor.h"
|
||||||
|
#include "esphome/components/openthread/openthread.h"
|
||||||
|
#ifdef USE_OPENTHREAD
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace openthread_info {
|
||||||
|
|
||||||
|
using esphome::openthread::OpenThreadLockGuard;
|
||||||
|
|
||||||
|
class OpenThreadInstancePollingComponent : public PollingComponent {
|
||||||
|
public:
|
||||||
|
void update() override {
|
||||||
|
auto lock = OpenThreadLockGuard::try_acquire(100);
|
||||||
|
if (!lock) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->update_instance_(lock->get_instance());
|
||||||
|
}
|
||||||
|
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void update_instance_(otInstance *instance) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IPAddressOpenThreadInfo : public PollingComponent, public text_sensor::TextSensor {
|
||||||
|
public:
|
||||||
|
void update() override {
|
||||||
|
std::optional<otIp6Address> address = openthread::global_openthread_component->get_omr_address();
|
||||||
|
if (!address) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char addressAsString[40];
|
||||||
|
otIp6AddressToString(&*address, addressAsString, 40);
|
||||||
|
std::string ip = addressAsString;
|
||||||
|
|
||||||
|
if (this->last_ip_ != ip) {
|
||||||
|
this->last_ip_ = ip;
|
||||||
|
this->publish_state(this->last_ip_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
|
||||||
|
std::string unique_id() override { return get_mac_address() + "-openthreadinfo-ip"; }
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string last_ip_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RoleOpenThreadInfo : public OpenThreadInstancePollingComponent, public text_sensor::TextSensor {
|
||||||
|
public:
|
||||||
|
void update_instance_(otInstance *instance) override {
|
||||||
|
otDeviceRole role = otThreadGetDeviceRole(instance);
|
||||||
|
|
||||||
|
if (this->last_role_ != role) {
|
||||||
|
this->last_role_ = role;
|
||||||
|
this->publish_state(otThreadDeviceRoleToString(this->last_role_));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string unique_id() override { return get_mac_address() + "-openthreadinfo-role"; }
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
otDeviceRole last_role_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Rloc16OpenThreadInfo : public OpenThreadInstancePollingComponent, public text_sensor::TextSensor {
|
||||||
|
public:
|
||||||
|
void update_instance_(otInstance *instance) override {
|
||||||
|
uint16_t rloc16 = otThreadGetRloc16(instance);
|
||||||
|
if (this->last_rloc16_ != rloc16) {
|
||||||
|
this->last_rloc16_ = rloc16;
|
||||||
|
char buf[5];
|
||||||
|
snprintf(buf, sizeof(buf), "%04x", rloc16);
|
||||||
|
this->publish_state(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
|
||||||
|
std::string unique_id() override { return get_mac_address() + "-openthreadinfo-rloc16"; }
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint16_t last_rloc16_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExtAddrOpenThreadInfo : public OpenThreadInstancePollingComponent, public text_sensor::TextSensor {
|
||||||
|
public:
|
||||||
|
void update_instance_(otInstance *instance) override {
|
||||||
|
auto extaddr = otLinkGetExtendedAddress(instance);
|
||||||
|
if (!std::equal(this->last_extaddr_.begin(), this->last_extaddr_.end(), extaddr->m8)) {
|
||||||
|
std::copy(extaddr->m8, extaddr->m8 + 8, this->last_extaddr_.begin());
|
||||||
|
this->publish_state(format_hex_pretty(extaddr->m8, 8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
|
||||||
|
std::string unique_id() override { return get_mac_address() + "-openthreadinfo-extaddr"; }
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::array<uint8_t, 8> last_extaddr_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
class Eui64OpenThreadInfo : public OpenThreadInstancePollingComponent, public text_sensor::TextSensor {
|
||||||
|
public:
|
||||||
|
void update_instance_(otInstance *instance) override {
|
||||||
|
otExtAddress addr;
|
||||||
|
otLinkGetFactoryAssignedIeeeEui64(instance, &addr);
|
||||||
|
|
||||||
|
if (!std::equal(this->last_eui64_.begin(), this->last_eui64_.end(), addr.m8)) {
|
||||||
|
std::copy(addr.m8, addr.m8 + 8, this->last_eui64_.begin());
|
||||||
|
this->publish_state(format_hex_pretty(this->last_eui64_.begin(), 8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
|
||||||
|
std::string unique_id() override { return get_mac_address() + "-openthreadinfo-extaddr"; }
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::array<uint8_t, 8> last_eui64_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
class ChannelOpenThreadInfo : public OpenThreadInstancePollingComponent, public text_sensor::TextSensor {
|
||||||
|
public:
|
||||||
|
void update_instance_(otInstance *instance) override {
|
||||||
|
uint8_t channel = otLinkGetChannel(instance);
|
||||||
|
if (this->last_channel_ != channel) {
|
||||||
|
this->last_channel_ = channel;
|
||||||
|
this->publish_state(std::to_string(this->last_channel_));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
|
||||||
|
std::string unique_id() override { return get_mac_address() + "-openthreadinfo-extaddr"; }
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint8_t last_channel_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DatasetOpenThreadInfo : public OpenThreadInstancePollingComponent {
|
||||||
|
public:
|
||||||
|
void update_instance_(otInstance *instance) override {
|
||||||
|
otOperationalDataset dataset;
|
||||||
|
if (otDatasetGetActive(instance, &dataset) != OT_ERROR_NONE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->update_dataset_(&dataset);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void update_dataset_(otOperationalDataset *dataset) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetworkNameOpenThreadInfo : public DatasetOpenThreadInfo, public text_sensor::TextSensor {
|
||||||
|
public:
|
||||||
|
void update_dataset_(otOperationalDataset *dataset) override {
|
||||||
|
if (this->last_network_name_ != dataset->mNetworkName.m8) {
|
||||||
|
this->last_network_name_ = dataset->mNetworkName.m8;
|
||||||
|
this->publish_state(this->last_network_name_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
|
||||||
|
std::string unique_id() override { return get_mac_address() + "-openthreadinfo-networkname"; }
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string last_network_name_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetworkKeyOpenThreadInfo : public DatasetOpenThreadInfo, public text_sensor::TextSensor {
|
||||||
|
public:
|
||||||
|
void update_dataset_(otOperationalDataset *dataset) override {
|
||||||
|
if (!std::equal(this->last_key_.begin(), this->last_key_.end(), dataset->mNetworkKey.m8)) {
|
||||||
|
std::copy(dataset->mNetworkKey.m8, dataset->mNetworkKey.m8 + 16, this->last_key_.begin());
|
||||||
|
this->publish_state(format_hex_pretty(dataset->mNetworkKey.m8, 16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
|
||||||
|
std::string unique_id() override { return get_mac_address() + "-openthreadinfo-networkkey"; }
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::array<uint8_t, 16> last_key_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
class PanIdOpenThreadInfo : public DatasetOpenThreadInfo, public text_sensor::TextSensor {
|
||||||
|
public:
|
||||||
|
void update_dataset_(otOperationalDataset *dataset) override {
|
||||||
|
uint16_t panid = dataset->mPanId;
|
||||||
|
if (this->last_panid_ != panid) {
|
||||||
|
this->last_panid_ = panid;
|
||||||
|
char buf[5];
|
||||||
|
snprintf(buf, sizeof(buf), "%04x", panid);
|
||||||
|
this->publish_state(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
|
||||||
|
std::string unique_id() override { return get_mac_address() + "-openthreadinfo-panid"; }
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint16_t last_panid_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExtPanIdOpenThreadInfo : public DatasetOpenThreadInfo, public text_sensor::TextSensor {
|
||||||
|
public:
|
||||||
|
void update_dataset_(otOperationalDataset *dataset) override {
|
||||||
|
if (!std::equal(this->last_extpanid_.begin(), this->last_extpanid_.end(), dataset->mExtendedPanId.m8)) {
|
||||||
|
std::copy(dataset->mExtendedPanId.m8, dataset->mExtendedPanId.m8 + 8, this->last_extpanid_.begin());
|
||||||
|
this->publish_state(format_hex_pretty(this->last_extpanid_.begin(), 8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
|
||||||
|
std::string unique_id() override { return get_mac_address() + "-openthreadinfo-extpanid"; }
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::array<uint8_t, 8> last_extpanid_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace openthread_info
|
||||||
|
} // namespace esphome
|
||||||
|
#endif
|
107
esphome/components/openthread_info/text_sensor.py
Normal file
107
esphome/components/openthread_info/text_sensor.py
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import text_sensor
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import CONF_CHANNEL, CONF_IP_ADDRESS, ENTITY_CATEGORY_DIAGNOSTIC
|
||||||
|
|
||||||
|
CONF_ROLE = "role"
|
||||||
|
CONF_RLOC16 = "rloc16"
|
||||||
|
CONF_EUI64 = "eui64"
|
||||||
|
CONF_EXTADDR = "extaddr"
|
||||||
|
|
||||||
|
# TODO: Move these to const.py
|
||||||
|
CONF_NETWORK_NAME = "network_name"
|
||||||
|
CONF_NETWORK_KEY = "network_key"
|
||||||
|
CONF_PSKC = "pskc"
|
||||||
|
CONF_PANID = "panid"
|
||||||
|
CONF_EXTPANID = "extpanid"
|
||||||
|
|
||||||
|
|
||||||
|
DEPENDENCIES = ["openthread"]
|
||||||
|
|
||||||
|
openthread_info_ns = cg.esphome_ns.namespace("openthread_info")
|
||||||
|
IPAddressOpenThreadInfo = openthread_info_ns.class_(
|
||||||
|
"IPAddressOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent
|
||||||
|
)
|
||||||
|
RoleOpenThreadInfo = openthread_info_ns.class_(
|
||||||
|
"RoleOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent
|
||||||
|
)
|
||||||
|
Rloc16OpenThreadInfo = openthread_info_ns.class_(
|
||||||
|
"Rloc16OpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent
|
||||||
|
)
|
||||||
|
ExtAddrOpenThreadInfo = openthread_info_ns.class_(
|
||||||
|
"ExtAddrOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent
|
||||||
|
)
|
||||||
|
Eui64OpenThreadInfo = openthread_info_ns.class_(
|
||||||
|
"Eui64OpenThreadInfo", text_sensor.TextSensor, cg.Component
|
||||||
|
)
|
||||||
|
ChannelOpenThreadInfo = openthread_info_ns.class_(
|
||||||
|
"ChannelOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent
|
||||||
|
)
|
||||||
|
NetworkNameOpenThreadInfo = openthread_info_ns.class_(
|
||||||
|
"NetworkNameOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent
|
||||||
|
)
|
||||||
|
NetworkKeyOpenThreadInfo = openthread_info_ns.class_(
|
||||||
|
"NetworkKeyOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent
|
||||||
|
)
|
||||||
|
PanIdOpenThreadInfo = openthread_info_ns.class_(
|
||||||
|
"PanIdOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent
|
||||||
|
)
|
||||||
|
ExtPanIdOpenThreadInfo = openthread_info_ns.class_(
|
||||||
|
"ExtPanIdOpenThreadInfo", text_sensor.TextSensor, cg.PollingComponent
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_IP_ADDRESS): text_sensor.text_sensor_schema(
|
||||||
|
IPAddressOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ROLE): text_sensor.text_sensor_schema(
|
||||||
|
RoleOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||||
|
).extend(cv.polling_component_schema("1s")),
|
||||||
|
cv.Optional(CONF_RLOC16): text_sensor.text_sensor_schema(
|
||||||
|
Rloc16OpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||||
|
).extend(cv.polling_component_schema("1s")),
|
||||||
|
cv.Optional(CONF_EXTADDR): text_sensor.text_sensor_schema(
|
||||||
|
ExtAddrOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||||
|
).extend(cv.polling_component_schema("1s")),
|
||||||
|
cv.Optional(CONF_EUI64): text_sensor.text_sensor_schema(
|
||||||
|
Eui64OpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||||
|
).extend(cv.polling_component_schema("1h")),
|
||||||
|
cv.Optional(CONF_CHANNEL): text_sensor.text_sensor_schema(
|
||||||
|
ChannelOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||||
|
).extend(cv.polling_component_schema("1s")),
|
||||||
|
cv.Optional(CONF_NETWORK_NAME): text_sensor.text_sensor_schema(
|
||||||
|
NetworkNameOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||||
|
).extend(cv.polling_component_schema("1s")),
|
||||||
|
cv.Optional(CONF_NETWORK_KEY): text_sensor.text_sensor_schema(
|
||||||
|
NetworkKeyOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||||
|
).extend(cv.polling_component_schema("1s")),
|
||||||
|
cv.Optional(CONF_PANID): text_sensor.text_sensor_schema(
|
||||||
|
PanIdOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||||
|
).extend(cv.polling_component_schema("1s")),
|
||||||
|
cv.Optional(CONF_EXTPANID): text_sensor.text_sensor_schema(
|
||||||
|
ExtPanIdOpenThreadInfo, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
|
||||||
|
).extend(cv.polling_component_schema("1s")),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def setup_conf(config, key):
|
||||||
|
if key in config:
|
||||||
|
conf = config[key]
|
||||||
|
var = await text_sensor.new_text_sensor(conf)
|
||||||
|
await cg.register_component(var, conf)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
await setup_conf(config, CONF_IP_ADDRESS)
|
||||||
|
await setup_conf(config, CONF_ROLE)
|
||||||
|
await setup_conf(config, CONF_RLOC16)
|
||||||
|
await setup_conf(config, CONF_EXTADDR)
|
||||||
|
await setup_conf(config, CONF_EUI64)
|
||||||
|
await setup_conf(config, CONF_CHANNEL)
|
||||||
|
await setup_conf(config, CONF_NETWORK_NAME)
|
||||||
|
await setup_conf(config, CONF_NETWORK_KEY)
|
||||||
|
await setup_conf(config, CONF_PANID)
|
||||||
|
await setup_conf(config, CONF_EXTPANID)
|
|
@ -23,9 +23,28 @@ openthread:
|
||||||
extpanid: d63e8e3e495ebbc3
|
extpanid: d63e8e3e495ebbc3
|
||||||
pskc: c23a76e98f1a6483639b1ac1271e2e27
|
pskc: c23a76e98f1a6483639b1ac1271e2e27
|
||||||
|
|
||||||
# The web server will cause the HA integration to fail, see note in the README
|
text_sensor:
|
||||||
# web_server:
|
- platform: openthread_info
|
||||||
# port: 80
|
ip_address:
|
||||||
|
name: "Off-mesh routable IP Address"
|
||||||
|
channel:
|
||||||
|
name: "Channel"
|
||||||
|
role:
|
||||||
|
name: "Device Role"
|
||||||
|
rloc16:
|
||||||
|
name: "RLOC16"
|
||||||
|
extaddr:
|
||||||
|
name: "Extended Address"
|
||||||
|
eui64:
|
||||||
|
name: "EUI64"
|
||||||
|
network_name:
|
||||||
|
name: "Network Name"
|
||||||
|
network_key:
|
||||||
|
name: "Network Key"
|
||||||
|
panid:
|
||||||
|
name: "PAN ID"
|
||||||
|
extpanid:
|
||||||
|
name: "Extended PAN ID"
|
||||||
|
|
||||||
api:
|
api:
|
||||||
encryption:
|
encryption:
|
||||||
|
|
Loading…
Reference in a new issue