mirror of
https://github.com/esphome/esphome.git
synced 2025-11-08 11:01:50 +00:00
Compare commits
41 Commits
2022.8.0b1
...
2022.8.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
041eb8f6cc | ||
|
|
733a84df75 | ||
|
|
acd0b50b40 | ||
|
|
60e46d485e | ||
|
|
5bf0c92318 | ||
|
|
39d493c278 | ||
|
|
d2ce62aa13 | ||
|
|
c8eb30ef27 | ||
|
|
31ad75d01b | ||
|
|
aa2eb29274 | ||
|
|
22eb4f9cb9 | ||
|
|
3acc8e7479 | ||
|
|
adc8c1aa38 | ||
|
|
3a82f500d4 | ||
|
|
7d1d4831a8 | ||
|
|
1a98e882dc | ||
|
|
c943d84036 | ||
|
|
d07a6704d5 | ||
|
|
8cfcd5904c | ||
|
|
fb8846bb45 | ||
|
|
0d0733dd94 | ||
|
|
a67d58948d | ||
|
|
84c051d097 | ||
|
|
b918abfd54 | ||
|
|
7f41b7cd93 | ||
|
|
4759b4fe2e | ||
|
|
e2c8e69d12 | ||
|
|
ab86ddcf02 | ||
|
|
bf8eddb13b | ||
|
|
e5eaf7a3fe | ||
|
|
50f32a3aa5 | ||
|
|
eb878710c1 | ||
|
|
311980e0e4 | ||
|
|
df73170e5a | ||
|
|
ccc13cc9e1 | ||
|
|
020b2c05c8 | ||
|
|
4c37c17df1 | ||
|
|
8f67acadd8 | ||
|
|
ca13c4c1a6 | ||
|
|
d0c646c721 | ||
|
|
8a055675af |
@@ -34,6 +34,7 @@ esphome/components/binary_sensor/* @esphome/core
|
||||
esphome/components/bl0939/* @ziceva
|
||||
esphome/components/bl0940/* @tobias-
|
||||
esphome/components/ble_client/* @buxtronix
|
||||
esphome/components/bluetooth_proxy/* @jesserockz
|
||||
esphome/components/bme680_bsec/* @trvrnrth
|
||||
esphome/components/bmp3xx/* @martgras
|
||||
esphome/components/button/* @esphome/core
|
||||
|
||||
@@ -27,6 +27,7 @@ service APIConnection {
|
||||
rpc subscribe_logs (SubscribeLogsRequest) returns (void) {}
|
||||
rpc subscribe_homeassistant_services (SubscribeHomeassistantServicesRequest) returns (void) {}
|
||||
rpc subscribe_home_assistant_states (SubscribeHomeAssistantStatesRequest) returns (void) {}
|
||||
rpc subscribe_bluetooth_le_advertisements (SubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
|
||||
rpc get_time (GetTimeRequest) returns (GetTimeResponse) {
|
||||
option (needs_authentication) = false;
|
||||
}
|
||||
@@ -190,6 +191,8 @@ message DeviceInfoResponse {
|
||||
string project_version = 9;
|
||||
|
||||
uint32 webserver_port = 10;
|
||||
|
||||
bool has_bluetooth_proxy = 11;
|
||||
}
|
||||
|
||||
message ListEntitiesRequest {
|
||||
@@ -473,6 +476,7 @@ enum SensorStateClass {
|
||||
STATE_CLASS_NONE = 0;
|
||||
STATE_CLASS_MEASUREMENT = 1;
|
||||
STATE_CLASS_TOTAL_INCREASING = 2;
|
||||
STATE_CLASS_TOTAL = 3;
|
||||
}
|
||||
|
||||
enum SensorLastResetType {
|
||||
@@ -1098,3 +1102,28 @@ message MediaPlayerCommandRequest {
|
||||
bool has_media_url = 6;
|
||||
string media_url = 7;
|
||||
}
|
||||
|
||||
// ==================== BLUETOOTH ====================
|
||||
message SubscribeBluetoothLEAdvertisementsRequest {
|
||||
option (id) = 66;
|
||||
option (source) = SOURCE_CLIENT;
|
||||
}
|
||||
|
||||
message BluetoothServiceData {
|
||||
string uuid = 1;
|
||||
repeated uint32 data = 2 [packed=false];
|
||||
}
|
||||
message BluetoothLEAdvertisementResponse {
|
||||
option (id) = 67;
|
||||
option (source) = SOURCE_SERVER;
|
||||
option (ifdef) = "USE_BLUETOOTH_PROXY";
|
||||
option (no_delay) = true;
|
||||
|
||||
uint64 address = 1;
|
||||
string name = 2;
|
||||
sint32 rssi = 3;
|
||||
|
||||
repeated string service_uuids = 4;
|
||||
repeated BluetoothServiceData service_data = 5;
|
||||
repeated BluetoothServiceData manufacturer_data = 6;
|
||||
}
|
||||
|
||||
@@ -886,6 +886,9 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
|
||||
#endif
|
||||
#ifdef USE_WEBSERVER
|
||||
resp.webserver_port = USE_WEBSERVER_PORT;
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
resp.has_bluetooth_proxy = true;
|
||||
#endif
|
||||
return resp;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
#include "api_server.h"
|
||||
#include "api_frame_helper.h"
|
||||
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
#include "esphome/components/bluetooth_proxy/bluetooth_proxy.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
|
||||
@@ -94,6 +98,13 @@ class APIConnection : public APIServerConnection {
|
||||
return;
|
||||
this->send_homeassistant_service_response(call);
|
||||
}
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
bool send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &call) {
|
||||
if (!this->bluetooth_le_advertisement_subscription_)
|
||||
return false;
|
||||
return this->send_bluetooth_le_advertisement_response(call);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_HOMEASSISTANT_TIME
|
||||
void send_time_request() {
|
||||
GetTimeRequest req;
|
||||
@@ -134,6 +145,9 @@ class APIConnection : public APIServerConnection {
|
||||
return {};
|
||||
}
|
||||
void execute_service(const ExecuteServiceRequest &msg) override;
|
||||
void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) override {
|
||||
this->bluetooth_le_advertisement_subscription_ = true;
|
||||
}
|
||||
bool is_authenticated() override { return this->connection_state_ == ConnectionState::AUTHENTICATED; }
|
||||
bool is_connection_setup() override {
|
||||
return this->connection_state_ == ConnectionState ::CONNECTED || this->is_authenticated();
|
||||
@@ -176,6 +190,7 @@ class APIConnection : public APIServerConnection {
|
||||
uint32_t last_traffic_;
|
||||
bool sent_ping_{false};
|
||||
bool service_call_subscription_{false};
|
||||
bool bluetooth_le_advertisement_subscription_{true};
|
||||
bool next_close_ = false;
|
||||
APIServer *parent_;
|
||||
InitialStateIterator initial_state_iterator_;
|
||||
|
||||
@@ -108,6 +108,8 @@ template<> const char *proto_enum_to_string<enums::SensorStateClass>(enums::Sens
|
||||
return "STATE_CLASS_MEASUREMENT";
|
||||
case enums::STATE_CLASS_TOTAL_INCREASING:
|
||||
return "STATE_CLASS_TOTAL_INCREASING";
|
||||
case enums::STATE_CLASS_TOTAL:
|
||||
return "STATE_CLASS_TOTAL";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
@@ -493,6 +495,10 @@ bool DeviceInfoResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
this->webserver_port = value.as_uint32();
|
||||
return true;
|
||||
}
|
||||
case 11: {
|
||||
this->has_bluetooth_proxy = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -542,6 +548,7 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(8, this->project_name);
|
||||
buffer.encode_string(9, this->project_version);
|
||||
buffer.encode_uint32(10, this->webserver_port);
|
||||
buffer.encode_bool(11, this->has_bluetooth_proxy);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void DeviceInfoResponse::dump_to(std::string &out) const {
|
||||
@@ -587,6 +594,10 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
|
||||
sprintf(buffer, "%u", this->webserver_port);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" has_bluetooth_proxy: ");
|
||||
out.append(YESNO(this->has_bluetooth_proxy));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
@@ -4852,6 +4863,143 @@ void MediaPlayerCommandRequest::dump_to(std::string &out) const {
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
void SubscribeBluetoothLEAdvertisementsRequest::encode(ProtoWriteBuffer buffer) const {}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void SubscribeBluetoothLEAdvertisementsRequest::dump_to(std::string &out) const {
|
||||
out.append("SubscribeBluetoothLEAdvertisementsRequest {}");
|
||||
}
|
||||
#endif
|
||||
bool BluetoothServiceData::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
this->data.push_back(value.as_uint32());
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool BluetoothServiceData::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->uuid = value.as_string();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void BluetoothServiceData::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(1, this->uuid);
|
||||
for (auto &it : this->data) {
|
||||
buffer.encode_uint32(2, it, true);
|
||||
}
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void BluetoothServiceData::dump_to(std::string &out) const {
|
||||
__attribute__((unused)) char buffer[64];
|
||||
out.append("BluetoothServiceData {\n");
|
||||
out.append(" uuid: ");
|
||||
out.append("'").append(this->uuid).append("'");
|
||||
out.append("\n");
|
||||
|
||||
for (const auto &it : this->data) {
|
||||
out.append(" data: ");
|
||||
sprintf(buffer, "%u", it);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
}
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool BluetoothLEAdvertisementResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->address = value.as_uint64();
|
||||
return true;
|
||||
}
|
||||
case 3: {
|
||||
this->rssi = value.as_sint32();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool BluetoothLEAdvertisementResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
this->name = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 4: {
|
||||
this->service_uuids.push_back(value.as_string());
|
||||
return true;
|
||||
}
|
||||
case 5: {
|
||||
this->service_data.push_back(value.as_message<BluetoothServiceData>());
|
||||
return true;
|
||||
}
|
||||
case 6: {
|
||||
this->manufacturer_data.push_back(value.as_message<BluetoothServiceData>());
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void BluetoothLEAdvertisementResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_uint64(1, this->address);
|
||||
buffer.encode_string(2, this->name);
|
||||
buffer.encode_sint32(3, this->rssi);
|
||||
for (auto &it : this->service_uuids) {
|
||||
buffer.encode_string(4, it, true);
|
||||
}
|
||||
for (auto &it : this->service_data) {
|
||||
buffer.encode_message<BluetoothServiceData>(5, it, true);
|
||||
}
|
||||
for (auto &it : this->manufacturer_data) {
|
||||
buffer.encode_message<BluetoothServiceData>(6, it, true);
|
||||
}
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void BluetoothLEAdvertisementResponse::dump_to(std::string &out) const {
|
||||
__attribute__((unused)) char buffer[64];
|
||||
out.append("BluetoothLEAdvertisementResponse {\n");
|
||||
out.append(" address: ");
|
||||
sprintf(buffer, "%llu", this->address);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" name: ");
|
||||
out.append("'").append(this->name).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" rssi: ");
|
||||
sprintf(buffer, "%d", this->rssi);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
for (const auto &it : this->service_uuids) {
|
||||
out.append(" service_uuids: ");
|
||||
out.append("'").append(it).append("'");
|
||||
out.append("\n");
|
||||
}
|
||||
|
||||
for (const auto &it : this->service_data) {
|
||||
out.append(" service_data: ");
|
||||
it.dump_to(out);
|
||||
out.append("\n");
|
||||
}
|
||||
|
||||
for (const auto &it : this->manufacturer_data) {
|
||||
out.append(" manufacturer_data: ");
|
||||
it.dump_to(out);
|
||||
out.append("\n");
|
||||
}
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
|
||||
@@ -53,6 +53,7 @@ enum SensorStateClass : uint32_t {
|
||||
STATE_CLASS_NONE = 0,
|
||||
STATE_CLASS_MEASUREMENT = 1,
|
||||
STATE_CLASS_TOTAL_INCREASING = 2,
|
||||
STATE_CLASS_TOTAL = 3,
|
||||
};
|
||||
enum SensorLastResetType : uint32_t {
|
||||
LAST_RESET_NONE = 0,
|
||||
@@ -262,6 +263,7 @@ class DeviceInfoResponse : public ProtoMessage {
|
||||
std::string project_name{};
|
||||
std::string project_version{};
|
||||
uint32_t webserver_port{0};
|
||||
bool has_bluetooth_proxy{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
@@ -1213,6 +1215,45 @@ class MediaPlayerCommandRequest : public ProtoMessage {
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class SubscribeBluetoothLEAdvertisementsRequest : public ProtoMessage {
|
||||
public:
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
};
|
||||
class BluetoothServiceData : public ProtoMessage {
|
||||
public:
|
||||
std::string uuid{};
|
||||
std::vector<uint32_t> data{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class BluetoothLEAdvertisementResponse : public ProtoMessage {
|
||||
public:
|
||||
uint64_t address{0};
|
||||
std::string name{};
|
||||
int32_t rssi{0};
|
||||
std::vector<std::string> service_uuids{};
|
||||
std::vector<BluetoothServiceData> service_data{};
|
||||
std::vector<BluetoothServiceData> manufacturer_data{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
|
||||
@@ -328,6 +328,14 @@ bool APIServerConnectionBase::send_media_player_state_response(const MediaPlayer
|
||||
#endif
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
#endif
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
bool APIServerConnectionBase::send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_bluetooth_le_advertisement_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<BluetoothLEAdvertisementResponse>(msg, 67);
|
||||
}
|
||||
#endif
|
||||
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
|
||||
switch (msg_type) {
|
||||
case 1: {
|
||||
@@ -595,6 +603,15 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 66: {
|
||||
SubscribeBluetoothLEAdvertisementsRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_subscribe_bluetooth_le_advertisements_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_subscribe_bluetooth_le_advertisements_request(msg);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -691,6 +708,18 @@ void APIServerConnection::on_subscribe_home_assistant_states_request(const Subsc
|
||||
}
|
||||
this->subscribe_home_assistant_states(msg);
|
||||
}
|
||||
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
|
||||
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
|
||||
if (!this->is_connection_setup()) {
|
||||
this->on_no_setup_connection();
|
||||
return;
|
||||
}
|
||||
if (!this->is_authenticated()) {
|
||||
this->on_unauthenticated_access();
|
||||
return;
|
||||
}
|
||||
this->subscribe_bluetooth_le_advertisements(msg);
|
||||
}
|
||||
void APIServerConnection::on_get_time_request(const GetTimeRequest &msg) {
|
||||
if (!this->is_connection_setup()) {
|
||||
this->on_no_setup_connection();
|
||||
|
||||
@@ -153,6 +153,11 @@ class APIServerConnectionBase : public ProtoService {
|
||||
#endif
|
||||
#ifdef USE_MEDIA_PLAYER
|
||||
virtual void on_media_player_command_request(const MediaPlayerCommandRequest &value){};
|
||||
#endif
|
||||
virtual void on_subscribe_bluetooth_le_advertisements_request(
|
||||
const SubscribeBluetoothLEAdvertisementsRequest &value){};
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
bool send_bluetooth_le_advertisement_response(const BluetoothLEAdvertisementResponse &msg);
|
||||
#endif
|
||||
protected:
|
||||
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
|
||||
@@ -170,6 +175,7 @@ class APIServerConnection : public APIServerConnectionBase {
|
||||
virtual void subscribe_logs(const SubscribeLogsRequest &msg) = 0;
|
||||
virtual void subscribe_homeassistant_services(const SubscribeHomeassistantServicesRequest &msg) = 0;
|
||||
virtual void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) = 0;
|
||||
virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
|
||||
virtual GetTimeResponse get_time(const GetTimeRequest &msg) = 0;
|
||||
virtual void execute_service(const ExecuteServiceRequest &msg) = 0;
|
||||
#ifdef USE_COVER
|
||||
@@ -216,6 +222,7 @@ class APIServerConnection : public APIServerConnectionBase {
|
||||
void on_subscribe_logs_request(const SubscribeLogsRequest &msg) override;
|
||||
void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &msg) override;
|
||||
void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) override;
|
||||
void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
||||
void on_get_time_request(const GetTimeRequest &msg) override;
|
||||
void on_execute_service_request(const ExecuteServiceRequest &msg) override;
|
||||
#ifdef USE_COVER
|
||||
|
||||
@@ -291,6 +291,13 @@ void APIServer::send_homeassistant_service_call(const HomeassistantServiceRespon
|
||||
client->send_homeassistant_service_call(call);
|
||||
}
|
||||
}
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void APIServer::send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &call) {
|
||||
for (auto &client : this->clients_) {
|
||||
client->send_bluetooth_le_advertisement(call);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
APIServer::APIServer() { global_api_server = this; }
|
||||
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(std::string)> f) {
|
||||
|
||||
@@ -73,6 +73,9 @@ class APIServer : public Component, public Controller {
|
||||
void on_media_player_update(media_player::MediaPlayer *obj) override;
|
||||
#endif
|
||||
void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
|
||||
#ifdef USE_BLUETOOTH_PROXY
|
||||
void send_bluetooth_le_advertisement(const BluetoothLEAdvertisementResponse &call);
|
||||
#endif
|
||||
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
|
||||
#ifdef USE_HOMEASSISTANT_TIME
|
||||
void request_time();
|
||||
|
||||
@@ -70,7 +70,7 @@ class ProtoVarInt {
|
||||
}
|
||||
}
|
||||
void encode(std::vector<uint8_t> &out) {
|
||||
uint32_t val = this->value_;
|
||||
uint64_t val = this->value_;
|
||||
if (val <= 0x7F) {
|
||||
out.push_back(val);
|
||||
return;
|
||||
|
||||
@@ -82,7 +82,7 @@ BLE_WRITE_ACTION_SCHEMA = cv.Schema(
|
||||
cv.Required(CONF_ID): cv.use_id(BLEClient),
|
||||
cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
|
||||
cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid,
|
||||
cv.Required(CONF_VALUE): cv.ensure_list(cv.hex_uint8_t),
|
||||
cv.Required(CONF_VALUE): cv.templatable(cv.ensure_list(cv.hex_uint8_t)),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -93,8 +93,14 @@ BLE_WRITE_ACTION_SCHEMA = cv.Schema(
|
||||
async def ble_write_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
value = config[CONF_VALUE]
|
||||
cg.add(var.set_value(value))
|
||||
if cg.is_template(value):
|
||||
templ = await cg.templatable(value, args, cg.std_vector.template(cg.uint8))
|
||||
cg.add(var.set_value_template(templ))
|
||||
else:
|
||||
cg.add(var.set_value_simple(value))
|
||||
|
||||
serv_uuid128 = esp32_ble_tracker.as_reversed_hex_array(config[CONF_SERVICE_UUID])
|
||||
cg.add(var.set_service_uuid128(serv_uuid128))
|
||||
char_uuid128 = esp32_ble_tracker.as_reversed_hex_array(
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace esphome {
|
||||
namespace ble_client {
|
||||
static const char *const TAG = "ble_client.automation";
|
||||
|
||||
void BLEWriterClientNode::write() {
|
||||
void BLEWriterClientNode::write(const std::vector<uint8_t> &value) {
|
||||
if (this->node_state != espbt::ClientState::ESTABLISHED) {
|
||||
ESP_LOGW(TAG, "Cannot write to BLE characteristic - not connected");
|
||||
return;
|
||||
@@ -29,9 +29,10 @@ void BLEWriterClientNode::write() {
|
||||
ESP_LOGE(TAG, "Characteristic %s does not allow writing", this->char_uuid_.to_string().c_str());
|
||||
return;
|
||||
}
|
||||
ESP_LOGVV(TAG, "Will write %d bytes: %s", this->value_.size(), format_hex_pretty(this->value_).c_str());
|
||||
esp_err_t err = esp_ble_gattc_write_char(this->parent()->gattc_if, this->parent()->conn_id, this->ble_char_handle_,
|
||||
value_.size(), value_.data(), write_type, ESP_GATT_AUTH_REQ_NONE);
|
||||
ESP_LOGVV(TAG, "Will write %d bytes: %s", value.size(), format_hex_pretty(value).c_str());
|
||||
esp_err_t err =
|
||||
esp_ble_gattc_write_char(this->parent()->gattc_if, this->parent()->conn_id, this->ble_char_handle_, value.size(),
|
||||
const_cast<uint8_t *>(value.data()), write_type, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error writing to characteristic: %s!", esp_err_to_name(err));
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ class BLEClientConnectTrigger : public Trigger<>, public BLEClientNode {
|
||||
void loop() override {}
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) override {
|
||||
if (event == ESP_GATTC_OPEN_EVT && param->open.status == ESP_GATT_OK)
|
||||
this->trigger();
|
||||
if (event == ESP_GATTC_SEARCH_CMPL_EVT)
|
||||
if (event == ESP_GATTC_SEARCH_CMPL_EVT) {
|
||||
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||
this->trigger();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -42,10 +42,8 @@ class BLEWriterClientNode : public BLEClientNode {
|
||||
ble_client_ = ble_client;
|
||||
}
|
||||
|
||||
void set_value(std::vector<uint8_t> value) { value_ = std::move(value); }
|
||||
|
||||
// Attempts to write the contents of value_ to char_uuid_.
|
||||
void write();
|
||||
// Attempts to write the contents of value to char_uuid_.
|
||||
void write(const std::vector<uint8_t> &value);
|
||||
|
||||
void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
|
||||
|
||||
@@ -60,14 +58,34 @@ class BLEWriterClientNode : public BLEClientNode {
|
||||
esp_gatt_char_prop_t char_props_;
|
||||
espbt::ESPBTUUID service_uuid_;
|
||||
espbt::ESPBTUUID char_uuid_;
|
||||
std::vector<uint8_t> value_;
|
||||
};
|
||||
|
||||
template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, public BLEWriterClientNode {
|
||||
public:
|
||||
BLEClientWriteAction(BLEClient *ble_client) : BLEWriterClientNode(ble_client) {}
|
||||
|
||||
void play(Ts... x) override { return write(); }
|
||||
void play(Ts... x) override {
|
||||
if (has_simple_value_) {
|
||||
return write(this->value_simple_);
|
||||
} else {
|
||||
return write(this->value_template_(x...));
|
||||
}
|
||||
}
|
||||
|
||||
void set_value_template(std::function<std::vector<uint8_t>(Ts...)> func) {
|
||||
this->value_template_ = std::move(func);
|
||||
has_simple_value_ = false;
|
||||
}
|
||||
|
||||
void set_value_simple(const std::vector<uint8_t> &value) {
|
||||
this->value_simple_ = value;
|
||||
has_simple_value_ = true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool has_simple_value_ = true;
|
||||
std::vector<uint8_t> value_simple_;
|
||||
std::function<std::vector<uint8_t>(Ts...)> value_template_{};
|
||||
};
|
||||
|
||||
} // namespace ble_client
|
||||
|
||||
@@ -54,6 +54,7 @@ bool BLEClient::parse_device(const espbt::ESPBTDevice &device) {
|
||||
this->remote_bda[3] = (addr >> 16) & 0xFF;
|
||||
this->remote_bda[4] = (addr >> 8) & 0xFF;
|
||||
this->remote_bda[5] = (addr >> 0) & 0xFF;
|
||||
this->remote_addr_type = device.get_address_type();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -83,7 +84,7 @@ void BLEClient::set_enabled(bool enabled) {
|
||||
|
||||
void BLEClient::connect() {
|
||||
ESP_LOGI(TAG, "Attempting BLE connection to %s", this->address_str().c_str());
|
||||
auto ret = esp_ble_gattc_open(this->gattc_if, this->remote_bda, BLE_ADDR_TYPE_PUBLIC, true);
|
||||
auto ret = esp_ble_gattc_open(this->gattc_if, this->remote_bda, this->remote_addr_type, true);
|
||||
if (ret) {
|
||||
ESP_LOGW(TAG, "esp_ble_gattc_open error, address=%s status=%d", this->address_str().c_str(), ret);
|
||||
this->set_states_(espbt::ClientState::IDLE);
|
||||
|
||||
@@ -115,6 +115,7 @@ class BLEClient : public espbt::ESPBTClient, public Component {
|
||||
|
||||
int gattc_if;
|
||||
esp_bd_addr_t remote_bda;
|
||||
esp_ble_addr_type_t remote_addr_type;
|
||||
uint16_t conn_id;
|
||||
uint64_t address;
|
||||
bool enabled;
|
||||
|
||||
27
esphome/components/bluetooth_proxy/__init__.py
Normal file
27
esphome/components/bluetooth_proxy/__init__.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from esphome.components import esp32_ble_tracker
|
||||
import esphome.config_validation as cv
|
||||
import esphome.codegen as cg
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
DEPENDENCIES = ["esp32", "esp32_ble_tracker"]
|
||||
CODEOWNERS = ["@jesserockz"]
|
||||
|
||||
|
||||
bluetooth_proxy_ns = cg.esphome_ns.namespace("bluetooth_proxy")
|
||||
|
||||
BluetoothProxy = bluetooth_proxy_ns.class_("BluetoothProxy", cg.Component)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BluetoothProxy),
|
||||
}
|
||||
).extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
await esp32_ble_tracker.register_ble_device(var, config)
|
||||
|
||||
cg.add_define("USE_BLUETOOTH_PROXY")
|
||||
58
esphome/components/bluetooth_proxy/bluetooth_proxy.cpp
Normal file
58
esphome/components/bluetooth_proxy/bluetooth_proxy.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "bluetooth_proxy.h"
|
||||
|
||||
#ifdef USE_API
|
||||
#include "esphome/components/api/api_pb2.h"
|
||||
#include "esphome/components/api/api_server.h"
|
||||
#endif // USE_API
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace bluetooth_proxy {
|
||||
|
||||
static const char *const TAG = "bluetooth_proxy";
|
||||
|
||||
bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||
ESP_LOGV(TAG, "Proxying packet from %s - %s. RSSI: %d dB", device.get_name().c_str(), device.address_str().c_str(),
|
||||
device.get_rssi());
|
||||
this->send_api_packet_(device);
|
||||
return true;
|
||||
}
|
||||
|
||||
void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||
#ifndef USE_API
|
||||
return;
|
||||
#else
|
||||
api::BluetoothLEAdvertisementResponse resp;
|
||||
resp.address = device.address_uint64();
|
||||
if (!device.get_name().empty())
|
||||
resp.name = device.get_name();
|
||||
resp.rssi = device.get_rssi();
|
||||
for (auto uuid : device.get_service_uuids()) {
|
||||
resp.service_uuids.push_back(uuid.to_string());
|
||||
}
|
||||
for (auto &data : device.get_service_datas()) {
|
||||
api::BluetoothServiceData service_data;
|
||||
service_data.uuid = data.uuid.to_string();
|
||||
for (auto d : data.data)
|
||||
service_data.data.push_back(d);
|
||||
resp.service_data.push_back(service_data);
|
||||
}
|
||||
for (auto &data : device.get_manufacturer_datas()) {
|
||||
api::BluetoothServiceData manufacturer_data;
|
||||
manufacturer_data.uuid = data.uuid.to_string();
|
||||
for (auto d : data.data)
|
||||
manufacturer_data.data.push_back(d);
|
||||
resp.manufacturer_data.push_back(manufacturer_data);
|
||||
}
|
||||
api::global_api_server->send_bluetooth_le_advertisement(resp);
|
||||
#endif
|
||||
}
|
||||
|
||||
void BluetoothProxy::dump_config() { ESP_LOGCONFIG(TAG, "Bluetooth Proxy:"); }
|
||||
|
||||
} // namespace bluetooth_proxy
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ESP32
|
||||
26
esphome/components/bluetooth_proxy/bluetooth_proxy.h
Normal file
26
esphome/components/bluetooth_proxy/bluetooth_proxy.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace bluetooth_proxy {
|
||||
|
||||
class BluetoothProxy : public Component, public esp32_ble_tracker::ESPBTDeviceListener {
|
||||
public:
|
||||
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
void send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device);
|
||||
};
|
||||
|
||||
} // namespace bluetooth_proxy
|
||||
} // namespace esphome
|
||||
|
||||
#endif // USE_ESP32
|
||||
@@ -1,19 +1,10 @@
|
||||
#include "climate_traits.h"
|
||||
#include <cstdio>
|
||||
|
||||
namespace esphome {
|
||||
namespace climate {
|
||||
|
||||
int8_t ClimateTraits::get_temperature_accuracy_decimals() const {
|
||||
// use printf %g to find number of digits based on temperature step
|
||||
char buf[32];
|
||||
sprintf(buf, "%.5g", this->visual_temperature_step_);
|
||||
std::string str{buf};
|
||||
size_t dot_pos = str.find('.');
|
||||
if (dot_pos == std::string::npos)
|
||||
return 0;
|
||||
|
||||
return str.length() - dot_pos - 1;
|
||||
return step_to_accuracy_decimals(this->visual_temperature_step_);
|
||||
}
|
||||
|
||||
} // namespace climate
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
class UDP;
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ void FingerprintGrowComponent::update() {
|
||||
void FingerprintGrowComponent::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up Grow Fingerprint Reader...");
|
||||
if (this->check_password_()) {
|
||||
if (this->new_password_ != nullptr) {
|
||||
if (this->new_password_ != -1) {
|
||||
if (this->set_password_())
|
||||
return;
|
||||
} else {
|
||||
@@ -202,9 +202,9 @@ bool FingerprintGrowComponent::check_password_() {
|
||||
}
|
||||
|
||||
bool FingerprintGrowComponent::set_password_() {
|
||||
ESP_LOGI(TAG, "Setting new password: %d", *this->new_password_);
|
||||
this->data_ = {SET_PASSWORD, (uint8_t)(*this->new_password_ >> 24), (uint8_t)(*this->new_password_ >> 16),
|
||||
(uint8_t)(*this->new_password_ >> 8), (uint8_t)(*this->new_password_ & 0xFF)};
|
||||
ESP_LOGI(TAG, "Setting new password: %d", this->new_password_);
|
||||
this->data_ = {SET_PASSWORD, (uint8_t)(this->new_password_ >> 24), (uint8_t)(this->new_password_ >> 16),
|
||||
(uint8_t)(this->new_password_ >> 8), (uint8_t)(this->new_password_ & 0xFF)};
|
||||
if (this->send_command_() == OK) {
|
||||
ESP_LOGI(TAG, "New password successfully set");
|
||||
ESP_LOGI(TAG, "Define the new password in your configuration and reflash now");
|
||||
|
||||
@@ -96,7 +96,7 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic
|
||||
}
|
||||
void set_sensing_pin(GPIOPin *sensing_pin) { this->sensing_pin_ = sensing_pin; }
|
||||
void set_password(uint32_t password) { this->password_ = password; }
|
||||
void set_new_password(uint32_t new_password) { this->new_password_ = &new_password; }
|
||||
void set_new_password(uint32_t new_password) { this->new_password_ = new_password; }
|
||||
void set_fingerprint_count_sensor(sensor::Sensor *fingerprint_count_sensor) {
|
||||
this->fingerprint_count_sensor_ = fingerprint_count_sensor;
|
||||
}
|
||||
@@ -153,7 +153,7 @@ class FingerprintGrowComponent : public PollingComponent, public uart::UARTDevic
|
||||
uint8_t address_[4] = {0xFF, 0xFF, 0xFF, 0xFF};
|
||||
uint16_t capacity_ = 64;
|
||||
uint32_t password_ = 0x0;
|
||||
uint32_t *new_password_{nullptr};
|
||||
uint32_t new_password_ = -1;
|
||||
GPIOPin *sensing_pin_{nullptr};
|
||||
uint8_t enrollment_image_ = 0;
|
||||
uint16_t enrollment_slot_ = 0;
|
||||
|
||||
@@ -23,6 +23,8 @@ void IntegrationSensor::setup() {
|
||||
}
|
||||
void IntegrationSensor::dump_config() { LOG_SENSOR("", "Integration Sensor", this); }
|
||||
void IntegrationSensor::process_sensor_value_(float value) {
|
||||
if (std::isnan(value))
|
||||
return;
|
||||
const uint32_t now = millis();
|
||||
const double old_value = this->last_value_;
|
||||
const double new_value = value;
|
||||
|
||||
@@ -236,7 +236,7 @@ size_t ModbusController::create_register_ranges_() {
|
||||
}
|
||||
}
|
||||
|
||||
if (curr->start_address == r.start_address) {
|
||||
if (curr->start_address == r.start_address && curr->register_type == r.register_type) {
|
||||
// use the lowest non zero value for the whole range
|
||||
// Because zero is the default value for skip_updates it is excluded from getting the min value.
|
||||
if (curr->skip_updates != 0) {
|
||||
|
||||
@@ -109,6 +109,7 @@ STATE_CLASSES = {
|
||||
"": StateClasses.STATE_CLASS_NONE,
|
||||
"measurement": StateClasses.STATE_CLASS_MEASUREMENT,
|
||||
"total_increasing": StateClasses.STATE_CLASS_TOTAL_INCREASING,
|
||||
"total": StateClasses.STATE_CLASS_TOTAL,
|
||||
}
|
||||
validate_state_class = cv.enum(STATE_CLASSES, lower=True, space="_")
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ std::string state_class_to_string(StateClass state_class) {
|
||||
return "measurement";
|
||||
case STATE_CLASS_TOTAL_INCREASING:
|
||||
return "total_increasing";
|
||||
case STATE_CLASS_TOTAL:
|
||||
return "total";
|
||||
case STATE_CLASS_NONE:
|
||||
default:
|
||||
return "";
|
||||
|
||||
@@ -36,6 +36,7 @@ enum StateClass : uint8_t {
|
||||
STATE_CLASS_NONE = 0,
|
||||
STATE_CLASS_MEASUREMENT = 1,
|
||||
STATE_CLASS_TOTAL_INCREASING = 2,
|
||||
STATE_CLASS_TOTAL = 3,
|
||||
};
|
||||
|
||||
std::string state_class_to_string(StateClass state_class);
|
||||
|
||||
@@ -76,7 +76,11 @@ void SPIComponent::setup() {
|
||||
if (spi_bus_num == 0) {
|
||||
this->hw_spi_ = &SPI;
|
||||
} else {
|
||||
#if defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32S2) || defined(USE_ESP32_VARIANT_ESP32S3)
|
||||
this->hw_spi_ = new SPIClass(FSPI); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
#else
|
||||
this->hw_spi_ = new SPIClass(VSPI); // NOLINT(cppcoreguidelines-owning-memory)
|
||||
#endif // USE_ESP32_VARIANT
|
||||
}
|
||||
spi_bus_num++;
|
||||
this->hw_spi_->begin(clk_pin, miso_pin, mosi_pin);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -360,9 +360,14 @@ void WebServer::handle_sensor_request(AsyncWebServerRequest *request, const UrlM
|
||||
}
|
||||
std::string WebServer::sensor_json(sensor::Sensor *obj, float value, JsonDetail start_config) {
|
||||
return json::build_json([obj, value, start_config](JsonObject root) {
|
||||
std::string state = value_accuracy_to_string(value, obj->get_accuracy_decimals());
|
||||
std::string state;
|
||||
if (isnan(value)) {
|
||||
state = "NA";
|
||||
} else {
|
||||
state = value_accuracy_to_string(value, obj->get_accuracy_decimals());
|
||||
if (!obj->get_unit_of_measurement().empty())
|
||||
state += " " + obj->get_unit_of_measurement();
|
||||
}
|
||||
set_json_icon_state_value(root, obj, "sensor-" + obj->get_object_id(), state, value, start_config);
|
||||
});
|
||||
}
|
||||
@@ -719,12 +724,15 @@ std::string WebServer::number_json(number::Number *obj, float value, JsonDetail
|
||||
root["step"] = obj->traits.get_step();
|
||||
root["mode"] = (int) obj->traits.get_mode();
|
||||
}
|
||||
std::string state = str_sprintf("%f", value);
|
||||
root["state"] = state;
|
||||
if (isnan(value)) {
|
||||
root["value"] = "\"NaN\"";
|
||||
root["state"] = "NA";
|
||||
} else {
|
||||
root["value"] = value;
|
||||
std::string state = value_accuracy_to_string(value, step_to_accuracy_decimals(obj->traits.get_step()));
|
||||
if (!obj->traits.get_unit_of_measurement().empty())
|
||||
state += " " + obj->traits.get_unit_of_measurement();
|
||||
root["state"] = state;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -839,6 +847,7 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf
|
||||
return json::build_json([obj, start_config](JsonObject root) {
|
||||
set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config);
|
||||
const auto traits = obj->get_traits();
|
||||
int8_t accuracy = traits.get_temperature_accuracy_decimals();
|
||||
char __buf[16];
|
||||
|
||||
if (start_config == DETAIL_ALL) {
|
||||
@@ -873,12 +882,15 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf
|
||||
}
|
||||
}
|
||||
|
||||
bool has_state = false;
|
||||
root["mode"] = PSTR_LOCAL(climate_mode_to_string(obj->mode));
|
||||
root["max_temp"] = traits.get_visual_max_temperature();
|
||||
root["min_temp"] = traits.get_visual_min_temperature();
|
||||
root["max_temp"] = value_accuracy_to_string(traits.get_visual_max_temperature(), accuracy);
|
||||
root["min_temp"] = value_accuracy_to_string(traits.get_visual_min_temperature(), accuracy);
|
||||
root["step"] = traits.get_visual_temperature_step();
|
||||
if (traits.get_supports_action()) {
|
||||
root["action"] = PSTR_LOCAL(climate_action_to_string(obj->action));
|
||||
root["state"] = root["action"];
|
||||
has_state = true;
|
||||
}
|
||||
if (traits.get_supports_fan_modes() && obj->fan_mode.has_value()) {
|
||||
root["fan_mode"] = PSTR_LOCAL(climate_fan_mode_to_string(obj->fan_mode.value()));
|
||||
@@ -896,14 +908,23 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf
|
||||
root["swing_mode"] = PSTR_LOCAL(climate_swing_mode_to_string(obj->swing_mode));
|
||||
}
|
||||
if (traits.get_supports_current_temperature()) {
|
||||
root["current_temperature"] = obj->current_temperature;
|
||||
if (!std::isnan(obj->current_temperature)) {
|
||||
root["current_temperature"] = value_accuracy_to_string(obj->current_temperature, accuracy);
|
||||
} else {
|
||||
root["current_temperature"] = "NA";
|
||||
}
|
||||
}
|
||||
if (traits.get_supports_two_point_target_temperature()) {
|
||||
root["current_temperature_low"] = obj->target_temperature_low;
|
||||
root["current_temperature_high"] = obj->target_temperature_low;
|
||||
root["target_temperature_low"] = value_accuracy_to_string(obj->target_temperature_low, accuracy);
|
||||
root["target_temperature_high"] = value_accuracy_to_string(obj->target_temperature_high, accuracy);
|
||||
if (!has_state) {
|
||||
root["state"] =
|
||||
value_accuracy_to_string((obj->target_temperature_high + obj->target_temperature_low) / 2.0f, accuracy);
|
||||
}
|
||||
} else {
|
||||
root["target_temperature"] = obj->target_temperature;
|
||||
root["state"] = obj->target_temperature;
|
||||
root["target_temperature"] = value_accuracy_to_string(obj->target_temperature, accuracy);
|
||||
if (!has_state)
|
||||
root["state"] = root["target_temperature"];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Constants used by esphome."""
|
||||
|
||||
__version__ = "2022.8.0b1"
|
||||
__version__ = "2022.8.3"
|
||||
|
||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||
|
||||
@@ -956,6 +956,9 @@ STATE_CLASS_MEASUREMENT = "measurement"
|
||||
# The state represents a total that only increases, a decrease is considered a reset.
|
||||
STATE_CLASS_TOTAL_INCREASING = "total_increasing"
|
||||
|
||||
# The state represents a total amount that can both increase and decrease, e.g. a net energy meter.
|
||||
STATE_CLASS_TOTAL = "total"
|
||||
|
||||
KEY_CORE = "core"
|
||||
KEY_TARGET_PLATFORM = "target_platform"
|
||||
KEY_TARGET_FRAMEWORK = "target_framework"
|
||||
|
||||
@@ -139,9 +139,19 @@ struct Color {
|
||||
return Color(uint8_t((uint16_t(r) * 255U / max_rgb)), uint8_t((uint16_t(g) * 255U / max_rgb)),
|
||||
uint8_t((uint16_t(b) * 255U / max_rgb)), w);
|
||||
}
|
||||
Color fade_to_white(uint8_t amnt) { return Color(255, 255, 255, 255) - (*this * amnt); }
|
||||
Color fade_to_black(uint8_t amnt) { return *this * amnt; }
|
||||
Color gradient(const Color &to_color, uint8_t amnt) { return (*this * amnt) + (to_color * (255 - amnt)); }
|
||||
|
||||
Color gradient(const Color &to_color, uint8_t amnt) {
|
||||
Color new_color;
|
||||
float amnt_f = float(amnt) / 255.0f;
|
||||
new_color.r = amnt_f * (to_color.r - (*this).r) + (*this).r;
|
||||
new_color.g = amnt_f * (to_color.g - (*this).g) + (*this).g;
|
||||
new_color.b = amnt_f * (to_color.b - (*this).b) + (*this).b;
|
||||
new_color.w = amnt_f * (to_color.w - (*this).w) + (*this).w;
|
||||
return new_color;
|
||||
}
|
||||
Color fade_to_white(uint8_t amnt) { return (*this).gradient(Color::WHITE, amnt); }
|
||||
Color fade_to_black(uint8_t amnt) { return (*this).gradient(Color::BLACK, amnt); }
|
||||
|
||||
Color lighten(uint8_t delta) { return *this + delta; }
|
||||
Color darken(uint8_t delta) { return *this - delta; }
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
#define USE_ESP32_IGNORE_EFUSE_MAC_CRC
|
||||
#define USE_IMPROV
|
||||
#define USE_SOCKET_IMPL_BSD_SOCKETS
|
||||
#define USE_BLUETOOTH_PROXY
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
#define USE_ARDUINO_VERSION_CODE VERSION_CODE(1, 0, 6)
|
||||
|
||||
@@ -258,6 +258,19 @@ std::string value_accuracy_to_string(float value, int8_t accuracy_decimals) {
|
||||
return std::string(tmp);
|
||||
}
|
||||
|
||||
int8_t step_to_accuracy_decimals(float step) {
|
||||
// use printf %g to find number of digits based on temperature step
|
||||
char buf[32];
|
||||
sprintf(buf, "%.5g", step);
|
||||
|
||||
std::string str{buf};
|
||||
size_t dot_pos = str.find('.');
|
||||
if (dot_pos == std::string::npos)
|
||||
return 0;
|
||||
|
||||
return str.length() - dot_pos - 1;
|
||||
}
|
||||
|
||||
// Colors
|
||||
|
||||
float gamma_correct(float value, float gamma) {
|
||||
|
||||
@@ -415,6 +415,9 @@ ParseOnOffState parse_on_off(const char *str, const char *on = nullptr, const ch
|
||||
/// Create a string from a value and an accuracy in decimals.
|
||||
std::string value_accuracy_to_string(float value, int8_t accuracy_decimals);
|
||||
|
||||
/// Derive accuracy in decimals from an increment step.
|
||||
int8_t step_to_accuracy_decimals(float step);
|
||||
|
||||
///@}
|
||||
|
||||
/// @name Colors
|
||||
|
||||
@@ -816,14 +816,17 @@ class LoginHandler(BaseHandler):
|
||||
import requests
|
||||
|
||||
headers = {
|
||||
"Authentication": f"Bearer {os.getenv('SUPERVISOR_TOKEN')}",
|
||||
"X-Supervisor-Token": os.getenv("SUPERVISOR_TOKEN"),
|
||||
}
|
||||
|
||||
data = {
|
||||
"username": self.get_argument("username", ""),
|
||||
"password": self.get_argument("password", ""),
|
||||
}
|
||||
try:
|
||||
req = requests.post("http://supervisor/auth", headers=headers, data=data)
|
||||
req = requests.post(
|
||||
"http://supervisor/auth", headers=headers, json=data, timeout=30
|
||||
)
|
||||
if req.status_code == 200:
|
||||
self.set_secure_cookie("authenticated", cookie_authenticated_yes)
|
||||
self.redirect("/")
|
||||
|
||||
@@ -288,6 +288,8 @@ adalight:
|
||||
|
||||
esp32_ble_tracker:
|
||||
|
||||
bluetooth_proxy:
|
||||
|
||||
ble_client:
|
||||
- mac_address: AA:BB:CC:DD:EE:FF
|
||||
id: ble_foo
|
||||
|
||||
@@ -615,3 +615,9 @@ switch:
|
||||
service_uuid: F61E3BE9-2826-A81B-970A-4D4DECFABBAE
|
||||
characteristic_uuid: 6490FAFE-0734-732C-8705-91B653A081FC
|
||||
value: [0x01, 0xab, 0xff]
|
||||
- ble_client.ble_write:
|
||||
id: airthings01
|
||||
service_uuid: F61E3BE9-2826-A81B-970A-4D4DECFABBAE
|
||||
characteristic_uuid: 6490FAFE-0734-732C-8705-91B653A081FC
|
||||
value: !lambda |-
|
||||
return {0x13, 0x37};
|
||||
|
||||
Reference in New Issue
Block a user