mirror of
https://github.com/esphome/esphome.git
synced 2024-11-23 07:28:10 +01:00
commit
d26c2b1a44
20 changed files with 353 additions and 315 deletions
|
@ -422,7 +422,7 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
|
||||||
msg.accuracy_decimals = sensor->get_accuracy_decimals();
|
msg.accuracy_decimals = sensor->get_accuracy_decimals();
|
||||||
msg.force_update = sensor->get_force_update();
|
msg.force_update = sensor->get_force_update();
|
||||||
msg.device_class = sensor->get_device_class();
|
msg.device_class = sensor->get_device_class();
|
||||||
msg.state_class = static_cast<enums::SensorStateClass>(sensor->state_class);
|
msg.state_class = static_cast<enums::SensorStateClass>(sensor->get_state_class());
|
||||||
msg.disabled_by_default = sensor->is_disabled_by_default();
|
msg.disabled_by_default = sensor->is_disabled_by_default();
|
||||||
|
|
||||||
return this->send_list_entities_sensor_response(msg);
|
return this->send_list_entities_sensor_response(msg);
|
||||||
|
@ -702,15 +702,7 @@ bool APIConnection::send_log_message(int level, const char *tag, const char *lin
|
||||||
// string message = 3;
|
// string message = 3;
|
||||||
buffer.encode_string(3, line, strlen(line));
|
buffer.encode_string(3, line, strlen(line));
|
||||||
// SubscribeLogsResponse - 29
|
// SubscribeLogsResponse - 29
|
||||||
bool success = this->send_buffer(buffer, 29);
|
|
||||||
if (!success) {
|
|
||||||
buffer = this->create_buffer();
|
|
||||||
// bool send_failed = 4;
|
|
||||||
buffer.encode_bool(4, true);
|
|
||||||
return this->send_buffer(buffer, 29);
|
return this->send_buffer(buffer, 29);
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HelloResponse APIConnection::hello(const HelloRequest &msg) {
|
HelloResponse APIConnection::hello(const HelloRequest &msg) {
|
||||||
|
@ -783,8 +775,23 @@ void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistant
|
||||||
bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) {
|
bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) {
|
||||||
if (this->remove_)
|
if (this->remove_)
|
||||||
return false;
|
return false;
|
||||||
if (!this->helper_->can_write_without_blocking())
|
if (!this->helper_->can_write_without_blocking()) {
|
||||||
|
delay(0);
|
||||||
|
APIError err = helper_->loop();
|
||||||
|
if (err != APIError::OK) {
|
||||||
|
on_fatal_error();
|
||||||
|
ESP_LOGW(TAG, "%s: Socket operation failed: %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
if (!this->helper_->can_write_without_blocking()) {
|
||||||
|
// SubscribeLogsResponse
|
||||||
|
if (message_type != 29) {
|
||||||
|
ESP_LOGV(TAG, "Cannot send message because of TCP buffer space");
|
||||||
|
}
|
||||||
|
delay(0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
APIError err = this->helper_->write_packet(message_type, buffer.get_buffer()->data(), buffer.get_buffer()->size());
|
APIError err = this->helper_->write_packet(message_type, buffer.get_buffer()->data(), buffer.get_buffer()->size());
|
||||||
if (err == APIError::WOULD_BLOCK)
|
if (err == APIError::WOULD_BLOCK)
|
||||||
|
|
|
@ -125,13 +125,6 @@ APIError APINoiseFrameHelper::init() {
|
||||||
HELPER_LOG("Setting nonblocking failed with errno %d", errno);
|
HELPER_LOG("Setting nonblocking failed with errno %d", errno);
|
||||||
return APIError::TCP_NONBLOCKING_FAILED;
|
return APIError::TCP_NONBLOCKING_FAILED;
|
||||||
}
|
}
|
||||||
int enable = 1;
|
|
||||||
err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
|
|
||||||
if (err != 0) {
|
|
||||||
state_ = State::FAILED;
|
|
||||||
HELPER_LOG("Setting nodelay failed with errno %d", errno);
|
|
||||||
return APIError::TCP_NODELAY_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
// init prologue
|
// init prologue
|
||||||
prologue_.insert(prologue_.end(), PROLOGUE_INIT, PROLOGUE_INIT + strlen(PROLOGUE_INIT));
|
prologue_.insert(prologue_.end(), PROLOGUE_INIT, PROLOGUE_INIT + strlen(PROLOGUE_INIT));
|
||||||
|
@ -494,12 +487,13 @@ APIError APINoiseFrameHelper::write_packet(uint16_t type, const uint8_t *payload
|
||||||
size_t total_len = 3 + mbuf.size;
|
size_t total_len = 3 + mbuf.size;
|
||||||
tmpbuf[1] = (uint8_t)(mbuf.size >> 8);
|
tmpbuf[1] = (uint8_t)(mbuf.size >> 8);
|
||||||
tmpbuf[2] = (uint8_t) mbuf.size;
|
tmpbuf[2] = (uint8_t) mbuf.size;
|
||||||
|
|
||||||
|
struct iovec iov;
|
||||||
|
iov.iov_base = &tmpbuf[0];
|
||||||
|
iov.iov_len = total_len;
|
||||||
|
|
||||||
// write raw to not have two packets sent if NAGLE disabled
|
// write raw to not have two packets sent if NAGLE disabled
|
||||||
aerr = write_raw_(&tmpbuf[0], total_len);
|
return write_raw_(&iov, 1);
|
||||||
if (aerr != APIError::OK) {
|
|
||||||
return aerr;
|
|
||||||
}
|
|
||||||
return APIError::OK;
|
|
||||||
}
|
}
|
||||||
APIError APINoiseFrameHelper::try_send_tx_buf_() {
|
APIError APINoiseFrameHelper::try_send_tx_buf_() {
|
||||||
// try send from tx_buf
|
// try send from tx_buf
|
||||||
|
@ -526,16 +520,19 @@ APIError APINoiseFrameHelper::try_send_tx_buf_() {
|
||||||
* @param data The data to write
|
* @param data The data to write
|
||||||
* @param len The length of data
|
* @param len The length of data
|
||||||
*/
|
*/
|
||||||
APIError APINoiseFrameHelper::write_raw_(const uint8_t *data, size_t len) {
|
APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
|
||||||
if (len == 0)
|
if (iovcnt == 0)
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
int err;
|
int err;
|
||||||
APIError aerr;
|
APIError aerr;
|
||||||
|
|
||||||
// uncomment for even more debugging
|
size_t total_write_len = 0;
|
||||||
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
#ifdef HELPER_LOG_PACKETS
|
#ifdef HELPER_LOG_PACKETS
|
||||||
ESP_LOGVV(TAG, "Sending raw: %s", hexencode(data, len).c_str());
|
ESP_LOGVV(TAG, "Sending raw: %s", hexencode(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
|
||||||
#endif
|
#endif
|
||||||
|
total_write_len += iov[i].iov_len;
|
||||||
|
}
|
||||||
|
|
||||||
if (!tx_buf_.empty()) {
|
if (!tx_buf_.empty()) {
|
||||||
// try to empty tx_buf_ first
|
// try to empty tx_buf_ first
|
||||||
|
@ -546,41 +543,56 @@ APIError APINoiseFrameHelper::write_raw_(const uint8_t *data, size_t len) {
|
||||||
|
|
||||||
if (!tx_buf_.empty()) {
|
if (!tx_buf_.empty()) {
|
||||||
// tx buf not empty, can't write now because then stream would be inconsistent
|
// tx buf not empty, can't write now because then stream would be inconsistent
|
||||||
tx_buf_.insert(tx_buf_.end(), data, data + len);
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
|
tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
|
||||||
|
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
|
||||||
|
}
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t sent = socket_->write(data, len);
|
ssize_t sent = socket_->writev(iov, iovcnt);
|
||||||
if (is_would_block(sent)) {
|
if (is_would_block(sent)) {
|
||||||
// operation would block, add buffer to tx_buf
|
// operation would block, add buffer to tx_buf
|
||||||
tx_buf_.insert(tx_buf_.end(), data, data + len);
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
|
tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
|
||||||
|
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
|
||||||
|
}
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
} else if (sent == -1) {
|
} else if (sent == -1) {
|
||||||
// an error occured
|
// an error occured
|
||||||
state_ = State::FAILED;
|
state_ = State::FAILED;
|
||||||
HELPER_LOG("Socket write failed with errno %d", errno);
|
HELPER_LOG("Socket write failed with errno %d", errno);
|
||||||
return APIError::SOCKET_WRITE_FAILED;
|
return APIError::SOCKET_WRITE_FAILED;
|
||||||
} else if (sent != len) {
|
} else if (sent != total_write_len) {
|
||||||
// partially sent, add end to tx_buf
|
// partially sent, add end to tx_buf
|
||||||
tx_buf_.insert(tx_buf_.end(), data + sent, data + len);
|
size_t to_consume = sent;
|
||||||
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
|
if (to_consume >= iov[i].iov_len) {
|
||||||
|
to_consume -= iov[i].iov_len;
|
||||||
|
} else {
|
||||||
|
tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_consume,
|
||||||
|
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
|
||||||
|
to_consume = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
}
|
}
|
||||||
// fully sent
|
// fully sent
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
}
|
}
|
||||||
APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, size_t len) {
|
APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, size_t len) {
|
||||||
APIError aerr;
|
|
||||||
|
|
||||||
uint8_t header[3];
|
uint8_t header[3];
|
||||||
header[0] = 0x01; // indicator
|
header[0] = 0x01; // indicator
|
||||||
header[1] = (uint8_t)(len >> 8);
|
header[1] = (uint8_t)(len >> 8);
|
||||||
header[2] = (uint8_t) len;
|
header[2] = (uint8_t) len;
|
||||||
|
|
||||||
aerr = write_raw_(header, 3);
|
struct iovec iov[2];
|
||||||
if (aerr != APIError::OK)
|
iov[0].iov_base = header;
|
||||||
return aerr;
|
iov[0].iov_len = 3;
|
||||||
aerr = write_raw_(data, len);
|
iov[1].iov_base = const_cast<uint8_t *>(data);
|
||||||
return aerr;
|
iov[1].iov_len = len;
|
||||||
|
|
||||||
|
return write_raw_(iov, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Initiate the data structures for the handshake.
|
/** Initiate the data structures for the handshake.
|
||||||
|
@ -709,13 +721,6 @@ APIError APIPlaintextFrameHelper::init() {
|
||||||
HELPER_LOG("Setting nonblocking failed with errno %d", errno);
|
HELPER_LOG("Setting nonblocking failed with errno %d", errno);
|
||||||
return APIError::TCP_NONBLOCKING_FAILED;
|
return APIError::TCP_NONBLOCKING_FAILED;
|
||||||
}
|
}
|
||||||
int enable = 1;
|
|
||||||
err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
|
|
||||||
if (err != 0) {
|
|
||||||
state_ = State::FAILED;
|
|
||||||
HELPER_LOG("Setting nodelay failed with errno %d", errno);
|
|
||||||
return APIError::TCP_NODELAY_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
state_ = State::DATA;
|
state_ = State::DATA;
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
|
@ -863,15 +868,13 @@ APIError APIPlaintextFrameHelper::write_packet(uint16_t type, const uint8_t *pay
|
||||||
ProtoVarInt(payload_len).encode(header);
|
ProtoVarInt(payload_len).encode(header);
|
||||||
ProtoVarInt(type).encode(header);
|
ProtoVarInt(type).encode(header);
|
||||||
|
|
||||||
aerr = write_raw_(&header[0], header.size());
|
struct iovec iov[2];
|
||||||
if (aerr != APIError::OK) {
|
iov[0].iov_base = &header[0];
|
||||||
return aerr;
|
iov[0].iov_len = header.size();
|
||||||
}
|
iov[1].iov_base = const_cast<uint8_t *>(payload);
|
||||||
aerr = write_raw_(payload, payload_len);
|
iov[1].iov_len = payload_len;
|
||||||
if (aerr != APIError::OK) {
|
|
||||||
return aerr;
|
return write_raw_(iov, 2);
|
||||||
}
|
|
||||||
return APIError::OK;
|
|
||||||
}
|
}
|
||||||
APIError APIPlaintextFrameHelper::try_send_tx_buf_() {
|
APIError APIPlaintextFrameHelper::try_send_tx_buf_() {
|
||||||
// try send from tx_buf
|
// try send from tx_buf
|
||||||
|
@ -896,16 +899,19 @@ APIError APIPlaintextFrameHelper::try_send_tx_buf_() {
|
||||||
* @param data The data to write
|
* @param data The data to write
|
||||||
* @param len The length of data
|
* @param len The length of data
|
||||||
*/
|
*/
|
||||||
APIError APIPlaintextFrameHelper::write_raw_(const uint8_t *data, size_t len) {
|
APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
|
||||||
if (len == 0)
|
if (iovcnt == 0)
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
int err;
|
int err;
|
||||||
APIError aerr;
|
APIError aerr;
|
||||||
|
|
||||||
// uncomment for even more debugging
|
size_t total_write_len = 0;
|
||||||
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
#ifdef HELPER_LOG_PACKETS
|
#ifdef HELPER_LOG_PACKETS
|
||||||
ESP_LOGVV(TAG, "Sending raw: %s", hexencode(data, len).c_str());
|
ESP_LOGVV(TAG, "Sending raw: %s", hexencode(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
|
||||||
#endif
|
#endif
|
||||||
|
total_write_len += iov[i].iov_len;
|
||||||
|
}
|
||||||
|
|
||||||
if (!tx_buf_.empty()) {
|
if (!tx_buf_.empty()) {
|
||||||
// try to empty tx_buf_ first
|
// try to empty tx_buf_ first
|
||||||
|
@ -916,23 +922,38 @@ APIError APIPlaintextFrameHelper::write_raw_(const uint8_t *data, size_t len) {
|
||||||
|
|
||||||
if (!tx_buf_.empty()) {
|
if (!tx_buf_.empty()) {
|
||||||
// tx buf not empty, can't write now because then stream would be inconsistent
|
// tx buf not empty, can't write now because then stream would be inconsistent
|
||||||
tx_buf_.insert(tx_buf_.end(), data, data + len);
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
|
tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
|
||||||
|
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
|
||||||
|
}
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t sent = socket_->write(data, len);
|
ssize_t sent = socket_->writev(iov, iovcnt);
|
||||||
if (is_would_block(sent)) {
|
if (is_would_block(sent)) {
|
||||||
// operation would block, add buffer to tx_buf
|
// operation would block, add buffer to tx_buf
|
||||||
tx_buf_.insert(tx_buf_.end(), data, data + len);
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
|
tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
|
||||||
|
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
|
||||||
|
}
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
} else if (sent == -1) {
|
} else if (sent == -1) {
|
||||||
// an error occured
|
// an error occured
|
||||||
state_ = State::FAILED;
|
state_ = State::FAILED;
|
||||||
HELPER_LOG("Socket write failed with errno %d", errno);
|
HELPER_LOG("Socket write failed with errno %d", errno);
|
||||||
return APIError::SOCKET_WRITE_FAILED;
|
return APIError::SOCKET_WRITE_FAILED;
|
||||||
} else if (sent != len) {
|
} else if (sent != total_write_len) {
|
||||||
// partially sent, add end to tx_buf
|
// partially sent, add end to tx_buf
|
||||||
tx_buf_.insert(tx_buf_.end(), data + sent, data + len);
|
size_t to_consume = sent;
|
||||||
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
|
if (to_consume >= iov[i].iov_len) {
|
||||||
|
to_consume -= iov[i].iov_len;
|
||||||
|
} else {
|
||||||
|
tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_consume,
|
||||||
|
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
|
||||||
|
to_consume = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
}
|
}
|
||||||
// fully sent
|
// fully sent
|
||||||
|
|
|
@ -58,6 +58,7 @@ const char *api_error_to_str(APIError err);
|
||||||
|
|
||||||
class APIFrameHelper {
|
class APIFrameHelper {
|
||||||
public:
|
public:
|
||||||
|
virtual ~APIFrameHelper() = default;
|
||||||
virtual APIError init() = 0;
|
virtual APIError init() = 0;
|
||||||
virtual APIError loop() = 0;
|
virtual APIError loop() = 0;
|
||||||
virtual APIError read_packet(ReadPacketBuffer *buffer) = 0;
|
virtual APIError read_packet(ReadPacketBuffer *buffer) = 0;
|
||||||
|
@ -96,7 +97,7 @@ class APINoiseFrameHelper : public APIFrameHelper {
|
||||||
APIError try_read_frame_(ParsedFrame *frame);
|
APIError try_read_frame_(ParsedFrame *frame);
|
||||||
APIError try_send_tx_buf_();
|
APIError try_send_tx_buf_();
|
||||||
APIError write_frame_(const uint8_t *data, size_t len);
|
APIError write_frame_(const uint8_t *data, size_t len);
|
||||||
APIError write_raw_(const uint8_t *data, size_t len);
|
APIError write_raw_(const struct iovec *iov, int iovcnt);
|
||||||
APIError init_handshake_();
|
APIError init_handshake_();
|
||||||
APIError check_handshake_finished_();
|
APIError check_handshake_finished_();
|
||||||
void send_explicit_handshake_reject_(const std::string &reason);
|
void send_explicit_handshake_reject_(const std::string &reason);
|
||||||
|
@ -154,7 +155,7 @@ class APIPlaintextFrameHelper : public APIFrameHelper {
|
||||||
|
|
||||||
APIError try_read_frame_(ParsedFrame *frame);
|
APIError try_read_frame_(ParsedFrame *frame);
|
||||||
APIError try_send_tx_buf_();
|
APIError try_send_tx_buf_();
|
||||||
APIError write_raw_(const uint8_t *data, size_t len);
|
APIError write_raw_(const struct iovec *iov, int iovcnt);
|
||||||
|
|
||||||
std::unique_ptr<socket::Socket> socket_;
|
std::unique_ptr<socket::Socket> socket_;
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ template<typename... Ts> class OpenAction : public Action<Ts...> {
|
||||||
public:
|
public:
|
||||||
explicit OpenAction(Cover *cover) : cover_(cover) {}
|
explicit OpenAction(Cover *cover) : cover_(cover) {}
|
||||||
|
|
||||||
void play(Ts... x) override { this->cover_->open(); }
|
void play(Ts... x) override { this->cover_->make_call().set_command_open().perform(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Cover *cover_;
|
Cover *cover_;
|
||||||
|
@ -21,7 +21,7 @@ template<typename... Ts> class CloseAction : public Action<Ts...> {
|
||||||
public:
|
public:
|
||||||
explicit CloseAction(Cover *cover) : cover_(cover) {}
|
explicit CloseAction(Cover *cover) : cover_(cover) {}
|
||||||
|
|
||||||
void play(Ts... x) override { this->cover_->close(); }
|
void play(Ts... x) override { this->cover_->make_call().set_command_close().perform(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Cover *cover_;
|
Cover *cover_;
|
||||||
|
@ -31,7 +31,7 @@ template<typename... Ts> class StopAction : public Action<Ts...> {
|
||||||
public:
|
public:
|
||||||
explicit StopAction(Cover *cover) : cover_(cover) {}
|
explicit StopAction(Cover *cover) : cover_(cover) {}
|
||||||
|
|
||||||
void play(Ts... x) override { this->cover_->stop(); }
|
void play(Ts... x) override { this->cover_->make_call().set_command_stop().perform(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Cover *cover_;
|
Cover *cover_;
|
||||||
|
|
|
@ -11,7 +11,7 @@ class DemoSensor : public sensor::Sensor, public PollingComponent {
|
||||||
public:
|
public:
|
||||||
void update() override {
|
void update() override {
|
||||||
float val = random_float();
|
float val = random_float();
|
||||||
bool increasing = this->state_class == sensor::STATE_CLASS_TOTAL_INCREASING;
|
bool increasing = this->get_state_class() == sensor::STATE_CLASS_TOTAL_INCREASING;
|
||||||
if (increasing) {
|
if (increasing) {
|
||||||
float base = isnan(this->state) ? 0.0f : this->state;
|
float base = isnan(this->state) ? 0.0f : this->state;
|
||||||
this->publish_state(base + val * 10);
|
this->publish_state(base + val * 10);
|
||||||
|
|
|
@ -27,7 +27,7 @@ std::unique_ptr<LightTransformer> AddressableLight::create_default_transition()
|
||||||
return make_unique<AddressableLightTransformer>(*this);
|
return make_unique<AddressableLightTransformer>(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Color esp_color_from_light_color_values(LightColorValues val) {
|
Color color_from_light_color_values(LightColorValues val) {
|
||||||
auto r = to_uint8_scale(val.get_color_brightness() * val.get_red());
|
auto r = to_uint8_scale(val.get_color_brightness() * val.get_red());
|
||||||
auto g = to_uint8_scale(val.get_color_brightness() * val.get_green());
|
auto g = to_uint8_scale(val.get_color_brightness() * val.get_green());
|
||||||
auto b = to_uint8_scale(val.get_color_brightness() * val.get_blue());
|
auto b = to_uint8_scale(val.get_color_brightness() * val.get_blue());
|
||||||
|
@ -44,7 +44,7 @@ void AddressableLight::update_state(LightState *state) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// don't use LightState helper, gamma correction+brightness is handled by ESPColorView
|
// don't use LightState helper, gamma correction+brightness is handled by ESPColorView
|
||||||
this->all() = esp_color_from_light_color_values(val);
|
this->all() = color_from_light_color_values(val);
|
||||||
this->schedule_show();
|
this->schedule_show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ void AddressableLightTransformer::start() {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto end_values = this->target_values_;
|
auto end_values = this->target_values_;
|
||||||
this->target_color_ = esp_color_from_light_color_values(end_values);
|
this->target_color_ = color_from_light_color_values(end_values);
|
||||||
|
|
||||||
// our transition will handle brightness, disable brightness in correction.
|
// our transition will handle brightness, disable brightness in correction.
|
||||||
this->light_.correction_.set_local_brightness(255);
|
this->light_.correction_.set_local_brightness(255);
|
||||||
|
@ -62,10 +62,13 @@ void AddressableLightTransformer::start() {
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<LightColorValues> AddressableLightTransformer::apply() {
|
optional<LightColorValues> AddressableLightTransformer::apply() {
|
||||||
// Don't try to transition over running effects, instead immediately use the target values. write_state() and the
|
float smoothed_progress = LightTransitionTransformer::smoothed_progress(this->get_progress_());
|
||||||
// effects pick up the change from current_values.
|
|
||||||
|
// When running an output-buffer modifying effect, don't try to transition individual LEDs, but instead just fade the
|
||||||
|
// LightColorValues. write_state() then picks up the change in brightness, and the color change is picked up by the
|
||||||
|
// effects which respect it.
|
||||||
if (this->light_.is_effect_active())
|
if (this->light_.is_effect_active())
|
||||||
return this->target_values_;
|
return LightColorValues::lerp(this->get_start_values(), this->get_target_values(), smoothed_progress);
|
||||||
|
|
||||||
// Use a specialized transition for addressable lights: instead of using a unified transition for
|
// Use a specialized transition for addressable lights: instead of using a unified transition for
|
||||||
// all LEDs, we use the current state of each LED as the start.
|
// all LEDs, we use the current state of each LED as the start.
|
||||||
|
@ -75,8 +78,6 @@ optional<LightColorValues> AddressableLightTransformer::apply() {
|
||||||
// Instead, we "fake" the look of the LERP by using an exponential average over time and using
|
// Instead, we "fake" the look of the LERP by using an exponential average over time and using
|
||||||
// dynamically-calculated alpha values to match the look.
|
// dynamically-calculated alpha values to match the look.
|
||||||
|
|
||||||
float smoothed_progress = LightTransitionTransformer::smoothed_progress(this->get_progress_());
|
|
||||||
|
|
||||||
float denom = (1.0f - smoothed_progress);
|
float denom = (1.0f - smoothed_progress);
|
||||||
float alpha = denom == 0.0f ? 0.0f : (smoothed_progress - this->last_transition_progress_) / denom;
|
float alpha = denom == 0.0f ? 0.0f : (smoothed_progress - this->last_transition_progress_) / denom;
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,9 @@ namespace light {
|
||||||
|
|
||||||
using ESPColor ESPDEPRECATED("esphome::light::ESPColor is deprecated, use esphome::Color instead.", "v1.21") = Color;
|
using ESPColor ESPDEPRECATED("esphome::light::ESPColor is deprecated, use esphome::Color instead.", "v1.21") = Color;
|
||||||
|
|
||||||
|
/// Convert the color information from a `LightColorValues` object to a `Color` object (does not apply brightness).
|
||||||
|
Color color_from_light_color_values(LightColorValues val);
|
||||||
|
|
||||||
class AddressableLight : public LightOutput, public Component {
|
class AddressableLight : public LightOutput, public Component {
|
||||||
public:
|
public:
|
||||||
virtual int32_t size() const = 0;
|
virtual int32_t size() const = 0;
|
||||||
|
|
|
@ -38,11 +38,8 @@ class AddressableLightEffect : public LightEffect {
|
||||||
void stop() override { this->get_addressable_()->set_effect_active(false); }
|
void stop() override { this->get_addressable_()->set_effect_active(false); }
|
||||||
virtual void apply(AddressableLight &it, const Color ¤t_color) = 0;
|
virtual void apply(AddressableLight &it, const Color ¤t_color) = 0;
|
||||||
void apply() override {
|
void apply() override {
|
||||||
LightColorValues color = this->state_->remote_values;
|
// not using any color correction etc. that will be handled by the addressable layer through ESPColorCorrection
|
||||||
// not using any color correction etc. that will be handled by the addressable layer
|
Color current_color = color_from_light_color_values(this->state_->remote_values);
|
||||||
Color current_color =
|
|
||||||
Color(static_cast<uint8_t>(color.get_red() * 255), static_cast<uint8_t>(color.get_green() * 255),
|
|
||||||
static_cast<uint8_t>(color.get_blue() * 255), static_cast<uint8_t>(color.get_white() * 255));
|
|
||||||
this->apply(*this->get_addressable_(), current_color);
|
this->apply(*this->get_addressable_(), current_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,6 +121,9 @@ void LightState::loop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->transformer_->is_finished()) {
|
if (this->transformer_->is_finished()) {
|
||||||
|
// if the transition has written directly to the output, current_values is outdated, so update it
|
||||||
|
this->current_values = this->transformer_->get_target_values();
|
||||||
|
|
||||||
this->transformer_->stop();
|
this->transformer_->stop();
|
||||||
this->transformer_ = nullptr;
|
this->transformer_ = nullptr;
|
||||||
this->target_state_reached_callback_.call();
|
this->target_state_reached_callback_.call();
|
||||||
|
|
|
@ -12,12 +12,18 @@ namespace light {
|
||||||
class LightTransitionTransformer : public LightTransformer {
|
class LightTransitionTransformer : public LightTransformer {
|
||||||
public:
|
public:
|
||||||
void start() override {
|
void start() override {
|
||||||
// When turning light on from off state, use colors from target state.
|
// When turning light on from off state, use target state and only increase brightness from zero.
|
||||||
if (!this->start_values_.is_on() && this->target_values_.is_on()) {
|
if (!this->start_values_.is_on() && this->target_values_.is_on()) {
|
||||||
this->start_values_ = LightColorValues(this->target_values_);
|
this->start_values_ = LightColorValues(this->target_values_);
|
||||||
this->start_values_.set_brightness(0.0f);
|
this->start_values_.set_brightness(0.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When turning light off from on state, use source state and only decrease brightness to zero.
|
||||||
|
if (this->start_values_.is_on() && !this->target_values_.is_on()) {
|
||||||
|
this->target_values_ = LightColorValues(this->start_values_);
|
||||||
|
this->target_values_.set_brightness(0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
// When changing color mode, go through off state, as color modes are orthogonal and there can't be two active.
|
// When changing color mode, go through off state, as color modes are orthogonal and there can't be two active.
|
||||||
if (this->start_values_.get_color_mode() != this->target_values_.get_color_mode()) {
|
if (this->start_values_.get_color_mode() != this->target_values_.get_color_mode()) {
|
||||||
this->changing_color_mode_ = true;
|
this->changing_color_mode_ = true;
|
||||||
|
|
|
@ -31,16 +31,9 @@ void MQTTSensorComponent::dump_config() {
|
||||||
std::string MQTTSensorComponent::component_type() const { return "sensor"; }
|
std::string MQTTSensorComponent::component_type() const { return "sensor"; }
|
||||||
|
|
||||||
uint32_t MQTTSensorComponent::get_expire_after() const {
|
uint32_t MQTTSensorComponent::get_expire_after() const {
|
||||||
if (this->expire_after_.has_value()) {
|
if (this->expire_after_.has_value())
|
||||||
return *this->expire_after_;
|
return *this->expire_after_;
|
||||||
} else {
|
|
||||||
#ifdef USE_DEEP_SLEEP
|
|
||||||
if (deep_sleep::global_has_deep_sleep) {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return this->sensor_->calculate_expected_filter_update_interval() * 5;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
void MQTTSensorComponent::set_expire_after(uint32_t expire_after) { this->expire_after_ = expire_after; }
|
void MQTTSensorComponent::set_expire_after(uint32_t expire_after) { this->expire_after_ = expire_after; }
|
||||||
void MQTTSensorComponent::disable_expire_after() { this->expire_after_ = 0; }
|
void MQTTSensorComponent::disable_expire_after() { this->expire_after_ = 0; }
|
||||||
|
@ -61,8 +54,8 @@ void MQTTSensorComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryCo
|
||||||
if (this->sensor_->get_force_update())
|
if (this->sensor_->get_force_update())
|
||||||
root["force_update"] = true;
|
root["force_update"] = true;
|
||||||
|
|
||||||
if (this->sensor_->state_class != STATE_CLASS_NONE)
|
if (this->sensor_->get_state_class() != STATE_CLASS_NONE)
|
||||||
root["state_class"] = state_class_to_string(this->sensor_->state_class);
|
root["state_class"] = state_class_to_string(this->sensor_->get_state_class());
|
||||||
|
|
||||||
config.command_topic = false;
|
config.command_topic = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ namespace sensor {
|
||||||
static const char *const TAG = "sensor.filter";
|
static const char *const TAG = "sensor.filter";
|
||||||
|
|
||||||
// Filter
|
// Filter
|
||||||
uint32_t Filter::expected_interval(uint32_t input) { return input; }
|
|
||||||
void Filter::input(float value) {
|
void Filter::input(float value) {
|
||||||
ESP_LOGVV(TAG, "Filter(%p)::input(%f)", this, value);
|
ESP_LOGVV(TAG, "Filter(%p)::input(%f)", this, value);
|
||||||
optional<float> out = this->new_value(value);
|
optional<float> out = this->new_value(value);
|
||||||
|
@ -29,15 +28,6 @@ void Filter::initialize(Sensor *parent, Filter *next) {
|
||||||
this->parent_ = parent;
|
this->parent_ = parent;
|
||||||
this->next_ = next;
|
this->next_ = next;
|
||||||
}
|
}
|
||||||
uint32_t Filter::calculate_remaining_interval(uint32_t input) {
|
|
||||||
uint32_t this_interval = this->expected_interval(input);
|
|
||||||
ESP_LOGVV(TAG, "Filter(%p)::calculate_remaining_interval(%u) -> %u", this, input, this_interval);
|
|
||||||
if (this->next_ == nullptr) {
|
|
||||||
return this_interval;
|
|
||||||
} else {
|
|
||||||
return this->next_->calculate_remaining_interval(this_interval);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MedianFilter
|
// MedianFilter
|
||||||
MedianFilter::MedianFilter(size_t window_size, size_t send_every, size_t send_first_at)
|
MedianFilter::MedianFilter(size_t window_size, size_t send_every, size_t send_first_at)
|
||||||
|
@ -75,8 +65,6 @@ optional<float> MedianFilter::new_value(float value) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t MedianFilter::expected_interval(uint32_t input) { return input * this->send_every_; }
|
|
||||||
|
|
||||||
// MinFilter
|
// MinFilter
|
||||||
MinFilter::MinFilter(size_t window_size, size_t send_every, size_t send_first_at)
|
MinFilter::MinFilter(size_t window_size, size_t send_every, size_t send_first_at)
|
||||||
: send_every_(send_every), send_at_(send_every - send_first_at), window_size_(window_size) {}
|
: send_every_(send_every), send_at_(send_every - send_first_at), window_size_(window_size) {}
|
||||||
|
@ -106,8 +94,6 @@ optional<float> MinFilter::new_value(float value) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t MinFilter::expected_interval(uint32_t input) { return input * this->send_every_; }
|
|
||||||
|
|
||||||
// MaxFilter
|
// MaxFilter
|
||||||
MaxFilter::MaxFilter(size_t window_size, size_t send_every, size_t send_first_at)
|
MaxFilter::MaxFilter(size_t window_size, size_t send_every, size_t send_first_at)
|
||||||
: send_every_(send_every), send_at_(send_every - send_first_at), window_size_(window_size) {}
|
: send_every_(send_every), send_at_(send_every - send_first_at), window_size_(window_size) {}
|
||||||
|
@ -137,8 +123,6 @@ optional<float> MaxFilter::new_value(float value) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t MaxFilter::expected_interval(uint32_t input) { return input * this->send_every_; }
|
|
||||||
|
|
||||||
// SlidingWindowMovingAverageFilter
|
// SlidingWindowMovingAverageFilter
|
||||||
SlidingWindowMovingAverageFilter::SlidingWindowMovingAverageFilter(size_t window_size, size_t send_every,
|
SlidingWindowMovingAverageFilter::SlidingWindowMovingAverageFilter(size_t window_size, size_t send_every,
|
||||||
size_t send_first_at)
|
size_t send_first_at)
|
||||||
|
@ -177,8 +161,6 @@ optional<float> SlidingWindowMovingAverageFilter::new_value(float value) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t SlidingWindowMovingAverageFilter::expected_interval(uint32_t input) { return input * this->send_every_; }
|
|
||||||
|
|
||||||
// ExponentialMovingAverageFilter
|
// ExponentialMovingAverageFilter
|
||||||
ExponentialMovingAverageFilter::ExponentialMovingAverageFilter(float alpha, size_t send_every)
|
ExponentialMovingAverageFilter::ExponentialMovingAverageFilter(float alpha, size_t send_every)
|
||||||
: send_every_(send_every), send_at_(send_every - 1), alpha_(alpha) {}
|
: send_every_(send_every), send_at_(send_every - 1), alpha_(alpha) {}
|
||||||
|
@ -203,7 +185,6 @@ optional<float> ExponentialMovingAverageFilter::new_value(float value) {
|
||||||
}
|
}
|
||||||
void ExponentialMovingAverageFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; }
|
void ExponentialMovingAverageFilter::set_send_every(size_t send_every) { this->send_every_ = send_every; }
|
||||||
void ExponentialMovingAverageFilter::set_alpha(float alpha) { this->alpha_ = alpha; }
|
void ExponentialMovingAverageFilter::set_alpha(float alpha) { this->alpha_ = alpha; }
|
||||||
uint32_t ExponentialMovingAverageFilter::expected_interval(uint32_t input) { return input * this->send_every_; }
|
|
||||||
|
|
||||||
// LambdaFilter
|
// LambdaFilter
|
||||||
LambdaFilter::LambdaFilter(lambda_filter_t lambda_filter) : lambda_filter_(std::move(lambda_filter)) {}
|
LambdaFilter::LambdaFilter(lambda_filter_t lambda_filter) : lambda_filter_(std::move(lambda_filter)) {}
|
||||||
|
@ -296,14 +277,6 @@ void OrFilter::initialize(Sensor *parent, Filter *next) {
|
||||||
this->phi_.initialize(parent, nullptr);
|
this->phi_.initialize(parent, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t OrFilter::expected_interval(uint32_t input) {
|
|
||||||
uint32_t min_interval = UINT32_MAX;
|
|
||||||
for (Filter *filter : this->filters_) {
|
|
||||||
min_interval = std::min(min_interval, filter->calculate_remaining_interval(input));
|
|
||||||
}
|
|
||||||
|
|
||||||
return min_interval;
|
|
||||||
}
|
|
||||||
// DebounceFilter
|
// DebounceFilter
|
||||||
optional<float> DebounceFilter::new_value(float value) {
|
optional<float> DebounceFilter::new_value(float value) {
|
||||||
this->set_timeout("debounce", this->time_period_, [this, value]() { this->output(value); });
|
this->set_timeout("debounce", this->time_period_, [this, value]() { this->output(value); });
|
||||||
|
@ -324,7 +297,6 @@ optional<float> HeartbeatFilter::new_value(float value) {
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
uint32_t HeartbeatFilter::expected_interval(uint32_t input) { return this->time_period_; }
|
|
||||||
void HeartbeatFilter::setup() {
|
void HeartbeatFilter::setup() {
|
||||||
this->set_interval("heartbeat", this->time_period_, [this]() {
|
this->set_interval("heartbeat", this->time_period_, [this]() {
|
||||||
ESP_LOGVV(TAG, "HeartbeatFilter(%p)::interval(has_value=%s, last_input=%f)", this, YESNO(this->has_value_),
|
ESP_LOGVV(TAG, "HeartbeatFilter(%p)::interval(has_value=%s, last_input=%f)", this, YESNO(this->has_value_),
|
||||||
|
|
|
@ -33,11 +33,6 @@ class Filter {
|
||||||
|
|
||||||
void input(float value);
|
void input(float value);
|
||||||
|
|
||||||
/// Return the amount of time that this filter is expected to take based on the input time interval.
|
|
||||||
virtual uint32_t expected_interval(uint32_t input);
|
|
||||||
|
|
||||||
uint32_t calculate_remaining_interval(uint32_t input);
|
|
||||||
|
|
||||||
void output(float value);
|
void output(float value);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -68,8 +63,6 @@ class MedianFilter : public Filter {
|
||||||
void set_send_every(size_t send_every);
|
void set_send_every(size_t send_every);
|
||||||
void set_window_size(size_t window_size);
|
void set_window_size(size_t window_size);
|
||||||
|
|
||||||
uint32_t expected_interval(uint32_t input) override;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::deque<float> queue_;
|
std::deque<float> queue_;
|
||||||
size_t send_every_;
|
size_t send_every_;
|
||||||
|
@ -98,8 +91,6 @@ class MinFilter : public Filter {
|
||||||
void set_send_every(size_t send_every);
|
void set_send_every(size_t send_every);
|
||||||
void set_window_size(size_t window_size);
|
void set_window_size(size_t window_size);
|
||||||
|
|
||||||
uint32_t expected_interval(uint32_t input) override;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::deque<float> queue_;
|
std::deque<float> queue_;
|
||||||
size_t send_every_;
|
size_t send_every_;
|
||||||
|
@ -128,8 +119,6 @@ class MaxFilter : public Filter {
|
||||||
void set_send_every(size_t send_every);
|
void set_send_every(size_t send_every);
|
||||||
void set_window_size(size_t window_size);
|
void set_window_size(size_t window_size);
|
||||||
|
|
||||||
uint32_t expected_interval(uint32_t input) override;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::deque<float> queue_;
|
std::deque<float> queue_;
|
||||||
size_t send_every_;
|
size_t send_every_;
|
||||||
|
@ -159,8 +148,6 @@ class SlidingWindowMovingAverageFilter : public Filter {
|
||||||
void set_send_every(size_t send_every);
|
void set_send_every(size_t send_every);
|
||||||
void set_window_size(size_t window_size);
|
void set_window_size(size_t window_size);
|
||||||
|
|
||||||
uint32_t expected_interval(uint32_t input) override;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
float sum_{0.0};
|
float sum_{0.0};
|
||||||
std::deque<float> queue_;
|
std::deque<float> queue_;
|
||||||
|
@ -183,8 +170,6 @@ class ExponentialMovingAverageFilter : public Filter {
|
||||||
void set_send_every(size_t send_every);
|
void set_send_every(size_t send_every);
|
||||||
void set_alpha(float alpha);
|
void set_alpha(float alpha);
|
||||||
|
|
||||||
uint32_t expected_interval(uint32_t input) override;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool first_value_{true};
|
bool first_value_{true};
|
||||||
float accumulator_{0.0f};
|
float accumulator_{0.0f};
|
||||||
|
@ -279,8 +264,6 @@ class HeartbeatFilter : public Filter, public Component {
|
||||||
|
|
||||||
optional<float> new_value(float value) override;
|
optional<float> new_value(float value) override;
|
||||||
|
|
||||||
uint32_t expected_interval(uint32_t input) override;
|
|
||||||
|
|
||||||
float get_setup_priority() const override;
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -306,8 +289,6 @@ class OrFilter : public Filter {
|
||||||
|
|
||||||
void initialize(Sensor *parent, Filter *next) override;
|
void initialize(Sensor *parent, Filter *next) override;
|
||||||
|
|
||||||
uint32_t expected_interval(uint32_t input) override;
|
|
||||||
|
|
||||||
optional<float> new_value(float value) override;
|
optional<float> new_value(float value) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace sensor {
|
||||||
|
|
||||||
static const char *const TAG = "sensor";
|
static const char *const TAG = "sensor";
|
||||||
|
|
||||||
const char *state_class_to_string(StateClass state_class) {
|
std::string state_class_to_string(StateClass state_class) {
|
||||||
switch (state_class) {
|
switch (state_class) {
|
||||||
case STATE_CLASS_MEASUREMENT:
|
case STATE_CLASS_MEASUREMENT:
|
||||||
return "measurement";
|
return "measurement";
|
||||||
|
@ -18,6 +18,51 @@ const char *state_class_to_string(StateClass state_class) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Sensor::Sensor(const std::string &name) : Nameable(name), state(NAN), raw_state(NAN) {}
|
||||||
|
Sensor::Sensor() : Sensor("") {}
|
||||||
|
|
||||||
|
std::string Sensor::get_unit_of_measurement() {
|
||||||
|
if (this->unit_of_measurement_.has_value())
|
||||||
|
return *this->unit_of_measurement_;
|
||||||
|
return this->unit_of_measurement();
|
||||||
|
}
|
||||||
|
void Sensor::set_unit_of_measurement(const std::string &unit_of_measurement) {
|
||||||
|
this->unit_of_measurement_ = unit_of_measurement;
|
||||||
|
}
|
||||||
|
std::string Sensor::unit_of_measurement() { return ""; }
|
||||||
|
|
||||||
|
std::string Sensor::get_icon() {
|
||||||
|
if (this->icon_.has_value())
|
||||||
|
return *this->icon_;
|
||||||
|
return this->icon();
|
||||||
|
}
|
||||||
|
void Sensor::set_icon(const std::string &icon) { this->icon_ = icon; }
|
||||||
|
std::string Sensor::icon() { return ""; }
|
||||||
|
|
||||||
|
int8_t Sensor::get_accuracy_decimals() {
|
||||||
|
if (this->accuracy_decimals_.has_value())
|
||||||
|
return *this->accuracy_decimals_;
|
||||||
|
return this->accuracy_decimals();
|
||||||
|
}
|
||||||
|
void Sensor::set_accuracy_decimals(int8_t accuracy_decimals) { this->accuracy_decimals_ = accuracy_decimals; }
|
||||||
|
int8_t Sensor::accuracy_decimals() { return 0; }
|
||||||
|
|
||||||
|
std::string Sensor::get_device_class() {
|
||||||
|
if (this->device_class_.has_value())
|
||||||
|
return *this->device_class_;
|
||||||
|
return this->device_class();
|
||||||
|
}
|
||||||
|
void Sensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
|
||||||
|
std::string Sensor::device_class() { return ""; }
|
||||||
|
|
||||||
|
void Sensor::set_state_class(StateClass state_class) { this->state_class_ = state_class; }
|
||||||
|
StateClass Sensor::get_state_class() {
|
||||||
|
if (this->state_class_.has_value())
|
||||||
|
return *this->state_class_;
|
||||||
|
return this->state_class();
|
||||||
|
}
|
||||||
|
StateClass Sensor::state_class() { return StateClass::STATE_CLASS_NONE; }
|
||||||
|
|
||||||
void Sensor::publish_state(float state) {
|
void Sensor::publish_state(float state) {
|
||||||
this->raw_state = state;
|
this->raw_state = state;
|
||||||
this->raw_callback_.call(state);
|
this->raw_callback_.call(state);
|
||||||
|
@ -30,54 +75,12 @@ void Sensor::publish_state(float state) {
|
||||||
this->filter_list_->input(state);
|
this->filter_list_->input(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::string Sensor::unit_of_measurement() { return ""; }
|
|
||||||
std::string Sensor::icon() { return ""; }
|
|
||||||
uint32_t Sensor::update_interval() { return 0; }
|
|
||||||
int8_t Sensor::accuracy_decimals() { return 0; }
|
|
||||||
Sensor::Sensor(const std::string &name) : Nameable(name), state(NAN), raw_state(NAN) {}
|
|
||||||
Sensor::Sensor() : Sensor("") {}
|
|
||||||
|
|
||||||
void Sensor::set_unit_of_measurement(const std::string &unit_of_measurement) {
|
|
||||||
this->unit_of_measurement_ = unit_of_measurement;
|
|
||||||
}
|
|
||||||
void Sensor::set_icon(const std::string &icon) { this->icon_ = icon; }
|
|
||||||
void Sensor::set_accuracy_decimals(int8_t accuracy_decimals) { this->accuracy_decimals_ = accuracy_decimals; }
|
|
||||||
void Sensor::add_on_state_callback(std::function<void(float)> &&callback) { this->callback_.add(std::move(callback)); }
|
void Sensor::add_on_state_callback(std::function<void(float)> &&callback) { this->callback_.add(std::move(callback)); }
|
||||||
void Sensor::add_on_raw_state_callback(std::function<void(float)> &&callback) {
|
void Sensor::add_on_raw_state_callback(std::function<void(float)> &&callback) {
|
||||||
this->raw_callback_.add(std::move(callback));
|
this->raw_callback_.add(std::move(callback));
|
||||||
}
|
}
|
||||||
std::string Sensor::get_icon() {
|
|
||||||
if (this->icon_.has_value())
|
|
||||||
return *this->icon_;
|
|
||||||
return this->icon();
|
|
||||||
}
|
|
||||||
void Sensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
|
|
||||||
std::string Sensor::get_device_class() {
|
|
||||||
if (this->device_class_.has_value())
|
|
||||||
return *this->device_class_;
|
|
||||||
return this->device_class();
|
|
||||||
}
|
|
||||||
std::string Sensor::device_class() { return ""; }
|
|
||||||
void Sensor::set_state_class(StateClass state_class) { this->state_class = state_class; }
|
|
||||||
void Sensor::set_state_class(const std::string &state_class) {
|
|
||||||
if (str_equals_case_insensitive(state_class, "measurement")) {
|
|
||||||
this->state_class = STATE_CLASS_MEASUREMENT;
|
|
||||||
} else if (str_equals_case_insensitive(state_class, "total_increasing")) {
|
|
||||||
this->state_class = STATE_CLASS_TOTAL_INCREASING;
|
|
||||||
} else {
|
|
||||||
ESP_LOGW(TAG, "'%s' - Unrecognized state class %s", this->get_name().c_str(), state_class.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::string Sensor::get_unit_of_measurement() {
|
|
||||||
if (this->unit_of_measurement_.has_value())
|
|
||||||
return *this->unit_of_measurement_;
|
|
||||||
return this->unit_of_measurement();
|
|
||||||
}
|
|
||||||
int8_t Sensor::get_accuracy_decimals() {
|
|
||||||
if (this->accuracy_decimals_.has_value())
|
|
||||||
return *this->accuracy_decimals_;
|
|
||||||
return this->accuracy_decimals();
|
|
||||||
}
|
|
||||||
void Sensor::add_filter(Filter *filter) {
|
void Sensor::add_filter(Filter *filter) {
|
||||||
// inefficient, but only happens once on every sensor setup and nobody's going to have massive amounts of
|
// inefficient, but only happens once on every sensor setup and nobody's going to have massive amounts of
|
||||||
// filters
|
// filters
|
||||||
|
@ -119,24 +122,7 @@ void Sensor::internal_send_state_to_frontend(float state) {
|
||||||
this->callback_.call(state);
|
this->callback_.call(state);
|
||||||
}
|
}
|
||||||
bool Sensor::has_state() const { return this->has_state_; }
|
bool Sensor::has_state() const { return this->has_state_; }
|
||||||
uint32_t Sensor::calculate_expected_filter_update_interval() {
|
|
||||||
uint32_t interval = this->update_interval();
|
|
||||||
if (interval == 4294967295UL)
|
|
||||||
// update_interval: never
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (this->filter_list_ == nullptr) {
|
|
||||||
return interval;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this->filter_list_->calculate_remaining_interval(interval);
|
|
||||||
}
|
|
||||||
uint32_t Sensor::hash_base() { return 2455723294UL; }
|
uint32_t Sensor::hash_base() { return 2455723294UL; }
|
||||||
|
|
||||||
PollingSensorComponent::PollingSensorComponent(const std::string &name, uint32_t update_interval)
|
|
||||||
: PollingComponent(update_interval), Sensor(name) {}
|
|
||||||
|
|
||||||
uint32_t PollingSensorComponent::update_interval() { return this->get_update_interval(); }
|
|
||||||
|
|
||||||
} // namespace sensor
|
} // namespace sensor
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/sensor/filter.h"
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/components/sensor/filter.h"
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace sensor {
|
namespace sensor {
|
||||||
|
@ -13,7 +13,7 @@ namespace sensor {
|
||||||
if (!(obj)->get_device_class().empty()) { \
|
if (!(obj)->get_device_class().empty()) { \
|
||||||
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \
|
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \
|
||||||
} \
|
} \
|
||||||
ESP_LOGCONFIG(TAG, "%s State Class: '%s'", prefix, state_class_to_string((obj)->state_class)); \
|
ESP_LOGCONFIG(TAG, "%s State Class: '%s'", prefix, state_class_to_string((obj)->get_state_class()).c_str()); \
|
||||||
ESP_LOGCONFIG(TAG, "%s Unit of Measurement: '%s'", prefix, (obj)->get_unit_of_measurement().c_str()); \
|
ESP_LOGCONFIG(TAG, "%s Unit of Measurement: '%s'", prefix, (obj)->get_unit_of_measurement().c_str()); \
|
||||||
ESP_LOGCONFIG(TAG, "%s Accuracy Decimals: %d", prefix, (obj)->get_accuracy_decimals()); \
|
ESP_LOGCONFIG(TAG, "%s Accuracy Decimals: %d", prefix, (obj)->get_accuracy_decimals()); \
|
||||||
if (!(obj)->get_icon().empty()) { \
|
if (!(obj)->get_icon().empty()) { \
|
||||||
|
@ -36,7 +36,7 @@ enum StateClass : uint8_t {
|
||||||
STATE_CLASS_TOTAL_INCREASING = 2,
|
STATE_CLASS_TOTAL_INCREASING = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *state_class_to_string(StateClass state_class);
|
std::string state_class_to_string(StateClass state_class);
|
||||||
|
|
||||||
/** Base-class for all sensors.
|
/** Base-class for all sensors.
|
||||||
*
|
*
|
||||||
|
@ -47,26 +47,42 @@ class Sensor : public Nameable {
|
||||||
explicit Sensor();
|
explicit Sensor();
|
||||||
explicit Sensor(const std::string &name);
|
explicit Sensor(const std::string &name);
|
||||||
|
|
||||||
/** Manually set the unit of measurement of this sensor. By default the sensor's default defined by
|
/// Get the unit of measurement, using the manual override if set.
|
||||||
* unit_of_measurement() is used.
|
std::string get_unit_of_measurement();
|
||||||
*
|
/// Manually set the unit of measurement.
|
||||||
* @param unit_of_measurement The unit of measurement, "" to disable.
|
|
||||||
*/
|
|
||||||
void set_unit_of_measurement(const std::string &unit_of_measurement);
|
void set_unit_of_measurement(const std::string &unit_of_measurement);
|
||||||
|
|
||||||
/** Manually set the icon of this sensor. By default the sensor's default defined by icon() is used.
|
/// Get the icon. Uses the manual override if specified or the default value instead.
|
||||||
*
|
std::string get_icon();
|
||||||
* @param icon The icon, for example "mdi:flash". "" to disable.
|
/// Manually set the icon, for example "mdi:flash".
|
||||||
*/
|
|
||||||
void set_icon(const std::string &icon);
|
void set_icon(const std::string &icon);
|
||||||
|
|
||||||
/** Manually set the accuracy in decimals for this sensor. By default, the sensor's default defined by
|
/// Get the accuracy in decimals, using the manual override if set.
|
||||||
* accuracy_decimals() is used.
|
int8_t get_accuracy_decimals();
|
||||||
*
|
/// Manually set the accuracy in decimals.
|
||||||
* @param accuracy_decimals The accuracy decimal that should be used.
|
|
||||||
*/
|
|
||||||
void set_accuracy_decimals(int8_t accuracy_decimals);
|
void set_accuracy_decimals(int8_t accuracy_decimals);
|
||||||
|
|
||||||
|
/// Get the device class, using the manual override if set.
|
||||||
|
std::string get_device_class();
|
||||||
|
/// Manually set the device class.
|
||||||
|
void set_device_class(const std::string &device_class);
|
||||||
|
|
||||||
|
/// Get the state class, using the manual override if set.
|
||||||
|
StateClass get_state_class();
|
||||||
|
/// Manually set the state class.
|
||||||
|
void set_state_class(StateClass state_class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get whether force update mode is enabled.
|
||||||
|
*
|
||||||
|
* If the sensor is in force_update mode, the frontend is required to save all
|
||||||
|
* state changes to the database when they are published, even if the state is the
|
||||||
|
* same as before.
|
||||||
|
*/
|
||||||
|
bool get_force_update() const { return force_update_; }
|
||||||
|
/// Set force update mode.
|
||||||
|
void set_force_update(bool force_update) { force_update_ = force_update; }
|
||||||
|
|
||||||
/// Add a filter to the filter chain. Will be appended to the back.
|
/// Add a filter to the filter chain. Will be appended to the back.
|
||||||
void add_filter(Filter *filter);
|
void add_filter(Filter *filter);
|
||||||
|
|
||||||
|
@ -93,15 +109,6 @@ class Sensor : public Nameable {
|
||||||
/// Getter-syntax for .raw_state
|
/// Getter-syntax for .raw_state
|
||||||
float get_raw_state() const;
|
float get_raw_state() const;
|
||||||
|
|
||||||
/// Get the accuracy in decimals. Uses the manual override if specified or the default value instead.
|
|
||||||
int8_t get_accuracy_decimals();
|
|
||||||
|
|
||||||
/// Get the unit of measurement. Uses the manual override if specified or the default value instead.
|
|
||||||
std::string get_unit_of_measurement();
|
|
||||||
|
|
||||||
/// Get the Home Assistant Icon. Uses the manual override if specified or the default value instead.
|
|
||||||
std::string get_icon();
|
|
||||||
|
|
||||||
/** Publish a new state to the front-end.
|
/** Publish a new state to the front-end.
|
||||||
*
|
*
|
||||||
* First, the new state will be assigned to the raw_value. Then it's passed through all filters
|
* First, the new state will be assigned to the raw_value. Then it's passed through all filters
|
||||||
|
@ -127,35 +134,15 @@ class Sensor : public Nameable {
|
||||||
*/
|
*/
|
||||||
float state;
|
float state;
|
||||||
|
|
||||||
/// Manually set the Home Assistant device class (see sensor::device_class)
|
/** This member variable stores the current raw state of the sensor, without any filters applied.
|
||||||
void set_device_class(const std::string &device_class);
|
*
|
||||||
|
* Unlike .state,this will be updated immediately when publish_state is called.
|
||||||
/// Get the device class for this sensor, using the manual override if specified.
|
|
||||||
std::string get_device_class();
|
|
||||||
|
|
||||||
/** This member variable stores the current raw state of the sensor. Unlike .state,
|
|
||||||
* this will be updated immediately when publish_state is called.
|
|
||||||
*/
|
*/
|
||||||
float raw_state;
|
float raw_state;
|
||||||
|
|
||||||
/// Return whether this sensor has gotten a full state (that passed through all filters) yet.
|
/// Return whether this sensor has gotten a full state (that passed through all filters) yet.
|
||||||
bool has_state() const;
|
bool has_state() const;
|
||||||
|
|
||||||
// The state class of this sensor state
|
|
||||||
StateClass state_class{STATE_CLASS_NONE};
|
|
||||||
|
|
||||||
/// Manually set the Home Assistant state class (see sensor::state_class)
|
|
||||||
void set_state_class(StateClass state_class);
|
|
||||||
void set_state_class(const std::string &state_class);
|
|
||||||
|
|
||||||
/** Override this to set the Home Assistant device class for this sensor.
|
|
||||||
*
|
|
||||||
* Return "" to disable this feature.
|
|
||||||
*
|
|
||||||
* @return The device class of this sensor, for example "temperature".
|
|
||||||
*/
|
|
||||||
virtual std::string device_class();
|
|
||||||
|
|
||||||
/** A unique ID for this sensor, empty for no unique id. See unique ID requirements:
|
/** A unique ID for this sensor, empty for no unique id. See unique ID requirements:
|
||||||
* https://developers.home-assistant.io/docs/en/entity_registry_index.html#unique-id-requirements
|
* https://developers.home-assistant.io/docs/en/entity_registry_index.html#unique-id-requirements
|
||||||
*
|
*
|
||||||
|
@ -163,65 +150,38 @@ class Sensor : public Nameable {
|
||||||
*/
|
*/
|
||||||
virtual std::string unique_id();
|
virtual std::string unique_id();
|
||||||
|
|
||||||
/// Return with which interval the sensor is polled. Return 0 for non-polling mode.
|
|
||||||
virtual uint32_t update_interval();
|
|
||||||
|
|
||||||
/// Calculate the expected update interval for values that pass through all filters.
|
|
||||||
uint32_t calculate_expected_filter_update_interval();
|
|
||||||
|
|
||||||
void internal_send_state_to_frontend(float state);
|
void internal_send_state_to_frontend(float state);
|
||||||
|
|
||||||
bool get_force_update() const { return force_update_; }
|
|
||||||
/** Set this sensor's force_update mode.
|
|
||||||
*
|
|
||||||
* If the sensor is in force_update mode, the frontend is required to save all
|
|
||||||
* state changes to the database when they are published, even if the state is the
|
|
||||||
* same as before.
|
|
||||||
*/
|
|
||||||
void set_force_update(bool force_update) { force_update_ = force_update; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/** Override this to set the Home Assistant unit of measurement for this sensor.
|
/// Override this to set the default unit of measurement.
|
||||||
*
|
|
||||||
* Return "" to disable this feature.
|
|
||||||
*
|
|
||||||
* @return The icon of this sensor, for example "°C".
|
|
||||||
*/
|
|
||||||
virtual std::string unit_of_measurement(); // NOLINT
|
virtual std::string unit_of_measurement(); // NOLINT
|
||||||
|
|
||||||
/** Override this to set the Home Assistant icon for this sensor.
|
/// Override this to set the default icon.
|
||||||
*
|
|
||||||
* Return "" to disable this feature.
|
|
||||||
*
|
|
||||||
* @return The icon of this sensor, for example "mdi:battery".
|
|
||||||
*/
|
|
||||||
virtual std::string icon(); // NOLINT
|
virtual std::string icon(); // NOLINT
|
||||||
|
|
||||||
/// Return the accuracy in decimals for this sensor.
|
/// Override this to set the default accuracy in decimals.
|
||||||
virtual int8_t accuracy_decimals(); // NOLINT
|
virtual int8_t accuracy_decimals(); // NOLINT
|
||||||
|
|
||||||
optional<std::string> device_class_{}; ///< Stores the override of the device class
|
/// Override this to set the default device class.
|
||||||
|
virtual std::string device_class(); // NOLINT
|
||||||
|
|
||||||
|
/// Override this to set the default state class.
|
||||||
|
virtual StateClass state_class(); // NOLINT
|
||||||
|
|
||||||
uint32_t hash_base() override;
|
uint32_t hash_base() override;
|
||||||
|
|
||||||
CallbackManager<void(float)> raw_callback_; ///< Storage for raw state callbacks.
|
CallbackManager<void(float)> raw_callback_; ///< Storage for raw state callbacks.
|
||||||
CallbackManager<void(float)> callback_; ///< Storage for filtered state callbacks.
|
CallbackManager<void(float)> callback_; ///< Storage for filtered state callbacks.
|
||||||
/// Override the unit of measurement
|
|
||||||
optional<std::string> unit_of_measurement_;
|
|
||||||
/// Override the icon advertised to Home Assistant, otherwise sensor's icon will be used.
|
|
||||||
optional<std::string> icon_;
|
|
||||||
/// Override the accuracy in decimals, otherwise the sensor's values will be used.
|
|
||||||
optional<int8_t> accuracy_decimals_;
|
|
||||||
Filter *filter_list_{nullptr}; ///< Store all active filters.
|
|
||||||
bool has_state_{false};
|
bool has_state_{false};
|
||||||
bool force_update_{false};
|
Filter *filter_list_{nullptr}; ///< Store all active filters.
|
||||||
};
|
|
||||||
|
|
||||||
class PollingSensorComponent : public PollingComponent, public Sensor {
|
optional<std::string> unit_of_measurement_; ///< Unit of measurement override
|
||||||
public:
|
optional<std::string> icon_; ///< Icon override
|
||||||
explicit PollingSensorComponent(const std::string &name, uint32_t update_interval);
|
optional<int8_t> accuracy_decimals_; ///< Accuracy in decimals override
|
||||||
|
optional<std::string> device_class_; ///< Device class override
|
||||||
uint32_t update_interval() override;
|
optional<StateClass> state_class_{STATE_CLASS_NONE}; ///< State class override
|
||||||
|
bool force_update_{false}; ///< Force update mode
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sensor
|
} // namespace sensor
|
||||||
|
|
|
@ -5,6 +5,10 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
#include <esp_idf_version.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace socket {
|
namespace socket {
|
||||||
|
|
||||||
|
@ -75,7 +79,51 @@ class BSDSocketImpl : public Socket {
|
||||||
}
|
}
|
||||||
int listen(int backlog) override { return ::listen(fd_, backlog); }
|
int listen(int backlog) override { return ::listen(fd_, backlog); }
|
||||||
ssize_t read(void *buf, size_t len) override { return ::read(fd_, buf, len); }
|
ssize_t read(void *buf, size_t len) override { return ::read(fd_, buf, len); }
|
||||||
|
ssize_t readv(const struct iovec *iov, int iovcnt) override {
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32) && ESP_IDF_VERSION_MAJOR < 4
|
||||||
|
// esp-idf v3 doesn't have readv, emulate it
|
||||||
|
ssize_t ret = 0;
|
||||||
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
|
ssize_t err = this->read(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
|
||||||
|
if (err == -1) {
|
||||||
|
if (ret != 0)
|
||||||
|
// if we already read some don't return an error
|
||||||
|
break;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
ret += err;
|
||||||
|
if (err != iov[i].iov_len)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
#else
|
||||||
|
return ::readv(fd_, iov, iovcnt);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
ssize_t write(const void *buf, size_t len) override { return ::write(fd_, buf, len); }
|
ssize_t write(const void *buf, size_t len) override { return ::write(fd_, buf, len); }
|
||||||
|
ssize_t send(void *buf, size_t len, int flags) { return ::send(fd_, buf, len, flags); }
|
||||||
|
ssize_t writev(const struct iovec *iov, int iovcnt) override {
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32) && ESP_IDF_VERSION_MAJOR < 4
|
||||||
|
// esp-idf v3 doesn't have writev, emulate it
|
||||||
|
ssize_t ret = 0;
|
||||||
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
|
ssize_t err =
|
||||||
|
this->send(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len, i == iovcnt - 1 ? 0 : MSG_MORE);
|
||||||
|
if (err == -1) {
|
||||||
|
if (ret != 0)
|
||||||
|
// if we already wrote some don't return an error
|
||||||
|
break;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
ret += err;
|
||||||
|
if (err != iov[i].iov_len)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
#else
|
||||||
|
return ::writev(fd_, iov, iovcnt);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
int setblocking(bool blocking) override {
|
int setblocking(bool blocking) override {
|
||||||
int fl = ::fcntl(fd_, F_GETFL, 0);
|
int fl = ::fcntl(fd_, F_GETFL, 0);
|
||||||
if (blocking) {
|
if (blocking) {
|
||||||
|
|
|
@ -81,6 +81,11 @@ struct sockaddr_storage {
|
||||||
};
|
};
|
||||||
typedef uint32_t socklen_t;
|
typedef uint32_t socklen_t;
|
||||||
|
|
||||||
|
struct iovec {
|
||||||
|
void *iov_base;
|
||||||
|
size_t iov_len;
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
// arduino-esp8266 declares a global vars called INADDR_NONE/ANY which are invalid with the define
|
// arduino-esp8266 declares a global vars called INADDR_NONE/ANY which are invalid with the define
|
||||||
#ifdef INADDR_ANY
|
#ifdef INADDR_ANY
|
||||||
|
@ -104,6 +109,7 @@ typedef uint32_t socklen_t;
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
|
@ -371,7 +371,23 @@ class LWIPRawImpl : public Socket {
|
||||||
|
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
ssize_t write(const void *buf, size_t len) override {
|
ssize_t readv(const struct iovec *iov, int iovcnt) override {
|
||||||
|
ssize_t ret = 0;
|
||||||
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
|
ssize_t err = read(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
|
||||||
|
if (err == -1) {
|
||||||
|
if (ret != 0)
|
||||||
|
// if we already read some don't return an error
|
||||||
|
break;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
ret += err;
|
||||||
|
if (err != iov[i].iov_len)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
ssize_t internal_write(const void *buf, size_t len) {
|
||||||
if (pcb_ == nullptr) {
|
if (pcb_ == nullptr) {
|
||||||
errno = ECONNRESET;
|
errno = ECONNRESET;
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -400,24 +416,59 @@ class LWIPRawImpl : public Socket {
|
||||||
errno = ECONNRESET;
|
errno = ECONNRESET;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (tcp_nagle_disabled(pcb_)) {
|
return to_send;
|
||||||
|
}
|
||||||
|
int internal_output() {
|
||||||
LWIP_LOG("tcp_output(%p)", pcb_);
|
LWIP_LOG("tcp_output(%p)", pcb_);
|
||||||
err = tcp_output(pcb_);
|
err_t err = tcp_output(pcb_);
|
||||||
if (err == ERR_ABRT) {
|
if (err == ERR_ABRT) {
|
||||||
LWIP_LOG(" -> err ERR_ABRT");
|
LWIP_LOG(" -> err ERR_ABRT");
|
||||||
// sometimes lwip returns ERR_ABRT for no apparent reason
|
// sometimes lwip returns ERR_ABRT for no apparent reason
|
||||||
// the connection works fine afterwards, and back with ESPAsyncTCP we
|
// the connection works fine afterwards, and back with ESPAsyncTCP we
|
||||||
// indirectly also ignored this error
|
// indirectly also ignored this error
|
||||||
// FIXME: figure out where this is returned and what it means in this context
|
// FIXME: figure out where this is returned and what it means in this context
|
||||||
return to_send;
|
return 0;
|
||||||
}
|
}
|
||||||
if (err != ERR_OK) {
|
if (err != ERR_OK) {
|
||||||
LWIP_LOG(" -> err %d", err);
|
LWIP_LOG(" -> err %d", err);
|
||||||
errno = ECONNRESET;
|
errno = ECONNRESET;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
return to_send;
|
ssize_t write(const void *buf, size_t len) override {
|
||||||
|
ssize_t written = internal_write(buf, len);
|
||||||
|
if (written == -1)
|
||||||
|
return -1;
|
||||||
|
if (written == 0)
|
||||||
|
// no need to output if nothing written
|
||||||
|
return 0;
|
||||||
|
int err = internal_output();
|
||||||
|
if (err == -1)
|
||||||
|
return -1;
|
||||||
|
return written;
|
||||||
|
}
|
||||||
|
ssize_t writev(const struct iovec *iov, int iovcnt) override {
|
||||||
|
ssize_t written = 0;
|
||||||
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
|
ssize_t err = internal_write(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len);
|
||||||
|
if (err == -1) {
|
||||||
|
if (written != 0)
|
||||||
|
// if we already read some don't return an error
|
||||||
|
break;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
written += err;
|
||||||
|
if (err != iov[i].iov_len)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (written == 0)
|
||||||
|
// no need to output if nothing written
|
||||||
|
return 0;
|
||||||
|
int err = internal_output();
|
||||||
|
if (err == -1)
|
||||||
|
return -1;
|
||||||
|
return written;
|
||||||
}
|
}
|
||||||
int setblocking(bool blocking) override {
|
int setblocking(bool blocking) override {
|
||||||
if (pcb_ == nullptr) {
|
if (pcb_ == nullptr) {
|
||||||
|
|
|
@ -31,7 +31,9 @@ class Socket {
|
||||||
virtual int setsockopt(int level, int optname, const void *optval, socklen_t optlen) = 0;
|
virtual int setsockopt(int level, int optname, const void *optval, socklen_t optlen) = 0;
|
||||||
virtual int listen(int backlog) = 0;
|
virtual int listen(int backlog) = 0;
|
||||||
virtual ssize_t read(void *buf, size_t len) = 0;
|
virtual ssize_t read(void *buf, size_t len) = 0;
|
||||||
|
virtual ssize_t readv(const struct iovec *iov, int iovcnt) = 0;
|
||||||
virtual ssize_t write(const void *buf, size_t len) = 0;
|
virtual ssize_t write(const void *buf, size_t len) = 0;
|
||||||
|
virtual ssize_t writev(const struct iovec *iov, int iovcnt) = 0;
|
||||||
virtual int setblocking(bool blocking) = 0;
|
virtual int setblocking(bool blocking) = 0;
|
||||||
virtual int loop() { return 0; };
|
virtual int loop() { return 0; };
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"""Constants used by esphome."""
|
"""Constants used by esphome."""
|
||||||
|
|
||||||
__version__ = "2021.9.0"
|
__version__ = "2021.9.1"
|
||||||
|
|
||||||
ESP_PLATFORM_ESP32 = "ESP32"
|
ESP_PLATFORM_ESP32 = "ESP32"
|
||||||
ESP_PLATFORM_ESP8266 = "ESP8266"
|
ESP_PLATFORM_ESP8266 = "ESP8266"
|
||||||
|
|
Loading…
Reference in a new issue