diff --git a/esphome/components/ble_client/ble_client.cpp b/esphome/components/ble_client/ble_client.cpp index 06d06a59c695..db82db074c85 100644 --- a/esphome/components/ble_client/ble_client.cpp +++ b/esphome/components/ble_client/ble_client.cpp @@ -92,12 +92,12 @@ void BLEClient::connect() { } } -void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t esp_gattc_if, +bool BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t esp_gattc_if, esp_ble_gattc_cb_param_t *param) { if (event == ESP_GATTC_REG_EVT && this->app_id != param->reg.app_id) - return; + return false; if (event != ESP_GATTC_REG_EVT && esp_gattc_if != ESP_GATT_IF_NONE && esp_gattc_if != this->gattc_if) - return; + return false; bool all_established = this->all_nodes_established_(); @@ -142,7 +142,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es } case ESP_GATTC_DISCONNECT_EVT: { if (memcmp(param->disconnect.remote_bda, this->remote_bda, 6) != 0) { - return; + return false; } ESP_LOGV(TAG, "[%s] ESP_GATTC_DISCONNECT_EVT, reason %d", this->address_str().c_str(), param->disconnect.reason); for (auto &svc : this->services_) @@ -204,6 +204,7 @@ void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t es delete svc; // NOLINT(cppcoreguidelines-owning-memory) this->services_.clear(); } + return true; } void BLEClient::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { diff --git a/esphome/components/ble_client/ble_client.h b/esphome/components/ble_client/ble_client.h index 4f6f87538448..3314187e6f54 100644 --- a/esphome/components/ble_client/ble_client.h +++ b/esphome/components/ble_client/ble_client.h @@ -86,7 +86,7 @@ class BLEClient : public espbt::ESPBTClient, public Component { void loop() override; float get_setup_priority() const override; - void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, + bool gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) override; void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) override; bool parse_device(const espbt::ESPBTDevice &device) override; diff --git a/esphome/components/powerpal_ble/powerpal_ble.cpp b/esphome/components/powerpal_ble/powerpal_ble.cpp index 7be45d16baf0..ff9ffe962e5e 100644 --- a/esphome/components/powerpal_ble/powerpal_ble.cpp +++ b/esphome/components/powerpal_ble/powerpal_ble.cpp @@ -1,9 +1,10 @@ #include "powerpal_ble.h" #include "esphome/core/log.h" #include "esphome/core/hal.h" +#include "esphome/core/time.h" +#include "WiFi.h" #ifdef USE_ESP32 - namespace esphome { namespace powerpal_ble { @@ -15,19 +16,18 @@ void Powerpal::dump_config() { LOG_SENSOR(" ", "Power", this->power_sensor_); LOG_SENSOR(" ", "Daily Energy", this->daily_energy_sensor_); LOG_SENSOR(" ", "Total Energy", this->energy_sensor_); -} + } void Powerpal::setup() { this->authenticated_ = false; this->pulse_multiplier_ = ((seconds_in_minute * this->reading_batch_size_[0]) / (this->pulses_per_kwh_ / kw_to_w_conversion)); - ESP_LOGD(TAG, "pulse_multiplier_: %f", this->pulse_multiplier_ ); + ESP_LOGI(TAG, "pulse_multiplier_: %f", this->pulse_multiplier_ ); #ifdef USE_HTTP_REQUEST - this->stored_measurements_.resize(15); //TODO dynamic - this->cloud_uploader_->set_method("POST"); + this->stored_measurements_.resize(1); //TODO dynamic + //this->cloud_uploader_->set_method("POST"); #endif } - // void Powerpal::loop() { // // for (uint16_t i = 0; i < 15; i++) { // // uint32_t timestamp = 1632487923494; @@ -76,20 +76,40 @@ void Powerpal::parse_measurement_(const uint8_t *data, uint16_t length) { unix_time += (data[1] << 8); unix_time += (data[2] << 16); unix_time += (data[3] << 24); - + long int new_time = unix_time; + // + ESP_LOGI(TAG, "Timestamp_test: %ld", new_time); uint16_t pulses_within_interval = data[4]; pulses_within_interval += data[5] << 8; // float total_kwh_within_interval = pulses_within_interval / this->pulses_per_kwh_; float avg_watts_within_interval = pulses_within_interval * this->pulse_multiplier_; - ESP_LOGI(TAG, "Timestamp: %ld, Pulses: %d, Average Watts within interval: %f W", unix_time, pulses_within_interval, - avg_watts_within_interval); + ESP_LOGI(TAG, "Timestamp: %ld, Pulses: %d, Average Watts within interval: %f W, Daily Pulses: %d", unix_time, pulses_within_interval, + avg_watts_within_interval, daily_pulses_); if (this->power_sensor_ != nullptr) { this->power_sensor_->publish_state(avg_watts_within_interval); + // + } + + if (this->cost_sensor_ != nullptr) { + double mycost = (pulses_within_interval / this->pulses_per_kwh_) * this->energy_cost_; + this->cost_sensor_->publish_state(mycost); + } + + if (this->pulses_sensor_ != nullptr) { + this->pulses_sensor_->publish_state(pulses_within_interval); } + if (this->watt_hours_sensor_ != nullptr) { + int mywatt_hrs = (uint32_t)roundf(pulses_within_interval * (this->pulses_per_kwh_ / kw_to_w_conversion)); + this->watt_hours_sensor_->publish_state(mywatt_hrs); + } + if (this->timestamp_sensor_ != nullptr) { + //int mywatt_hrs = (uint32_t)roundf(pulses_within_interval * (this->pulses_per_kwh_ / kw_to_w_conversion)); + this->timestamp_sensor_->publish_state(new_time); + } if (this->energy_sensor_ != nullptr) { this->total_pulses_ += pulses_within_interval; float energy = this->total_pulses_ / this->pulses_per_kwh_; @@ -102,11 +122,14 @@ void Powerpal::parse_measurement_(const uint8_t *data, uint16_t length) { float energy = this->daily_pulses_ / this->pulses_per_kwh_; this->daily_energy_sensor_->publish_state(energy); + if (this->daily_pulses_sensor_ != nullptr) { + this->daily_pulses_sensor_->publish_state(daily_pulses_); + } // if esphome device has a valid time component set up, use that (preferred) // else, use the powerpal measurement timestamps #ifdef USE_TIME auto *time_ = *this->time_; - time::ESPTime date_of_measurement = time_->now(); + ESPTime date_of_measurement = time_->now(); if (date_of_measurement.is_valid()) { if (this->day_of_last_measurement_ == 0) { this->day_of_last_measurement_ = date_of_measurement.day_of_year;} else if (this->day_of_last_measurement_ != date_of_measurement.day_of_year) { @@ -130,19 +153,19 @@ void Powerpal::parse_measurement_(const uint8_t *data, uint16_t length) { #endif } -#ifdef USE_HTTP_REQUEST - if(this->cloud_uploader_ != nullptr) { - this->store_measurement_( - pulses_within_interval, - unix_time, - (uint32_t)roundf(pulses_within_interval * (this->pulses_per_kwh_ / kw_to_w_conversion)), - (pulses_within_interval / this->pulses_per_kwh_) * this->energy_cost_ - ); - if (this->stored_measurements_count_ == 14) { - this->upload_data_to_cloud_(); - } - } -#endif +// #ifdef USE_HTTP_REQUEST +// if(this->cloud_uploader_ != nullptr) { +// this->store_measurement_( +// pulses_within_interval, +// unix_time, +// (uint32_t)roundf(pulses_within_interval * (this->pulses_per_kwh_ / kw_to_w_conversion)), +// (pulses_within_interval / this->pulses_per_kwh_) * this->energy_cost_ +// ); +// if (this->stored_measurements_count_ == 1) { +// this->upload_data_to_cloud_(); +// } +// } +// #endif } } @@ -169,39 +192,54 @@ std::string Powerpal::serial_to_apikey_(const uint8_t *data, uint16_t length) { return api_key; } -#ifdef USE_HTTP_REQUEST -void Powerpal::store_measurement_(uint16_t pulses, time_t timestamp, uint32_t watt_hours, float cost) { - this->stored_measurements_count_++; - this->stored_measurements_[this->stored_measurements_count_].pulses = pulses; - this->stored_measurements_[this->stored_measurements_count_].timestamp = timestamp; - this->stored_measurements_[this->stored_measurements_count_].watt_hours = watt_hours; - this->stored_measurements_[this->stored_measurements_count_].cost = cost; -} +// #ifdef USE_HTTP_REQUEST +// void Powerpal::store_measurement_(uint16_t pulses, time_t timestamp, uint32_t watt_hours, float cost) { +// this->stored_measurements_count_++; +// this->stored_measurements_[this->stored_measurements_count_].pulses = pulses; +// this->stored_measurements_[this->stored_measurements_count_].timestamp = timestamp; +// this->stored_measurements_[this->stored_measurements_count_].watt_hours = watt_hours; +// this->stored_measurements_[this->stored_measurements_count_].cost = cost; +// } -void Powerpal::upload_data_to_cloud_() { - this->stored_measurements_count_ = 0; - if (this->powerpal_device_id_.length() && this->powerpal_apikey_.length()) { - StaticJsonDocument<2048> doc; // 768 bytes, each entry may take up 15 bytes (uint16_t + uint32_t + uint32_t + float + bool) - JsonArray array = doc.to(); - for (int i = 0; i < 15; i++) { - JsonObject nested = array.createNestedObject(); - nested["timestamp"] = this->stored_measurements_[i].timestamp; - nested["pulses"] = this->stored_measurements_[i].pulses; - nested["watt_hours"] = this->stored_measurements_[i].watt_hours; - nested["cost"] = this->stored_measurements_[i].cost; - nested["is_peak"] = false; - } - std::string body; - serializeJson(doc, body); - this->cloud_uploader_->set_body(body); - // empty triggers, but requirement of using the send function - std::vector response_triggers_; - this->cloud_uploader_->send(response_triggers_); - } else { - // apikey or device missing - } -} -#endif +// void Powerpal::upload_data_to_cloud_() { +// //WiFi.begin(ssid, password); +// //WiFiClientSecure wifiClient; +// //wifiClient.setInsecure(); +// //HTTPClient http; +// //HttpClient(wifiClient, serverAddress, port); +// //http.setTimeout(5000); +// //http.useHTTP10(true); +// //http.begin(wifiClient, serverAddress); +// //http.addHeader("Authorization", this->powerpal_apikey_.c_str()); +// //http.addHeader("Content-Type", "application/json"); +// this->stored_measurements_count_ = 0; +// if (this->powerpal_device_id_.length() && this->powerpal_apikey_.length()) { +// StaticJsonDocument<2048> doc; // 768 bytes, each entry may take up 15 bytes (uint16_t + uint32_t + uint32_t + float + bool) +// JsonArray array = doc.to(); +// for (int i = 0; i < 1; i++) { +// JsonObject nested = array.createNestedObject(); +// nested["cost"] = this->stored_measurements_[i].cost; +// nested["is_peak"] = false; +// nested["pulses"] = this->stored_measurements_[i].pulses; +// nested["timestamp"] = this->stored_measurements_[i].timestamp; +// nested["watt_hours"] = this->stored_measurements_[i].watt_hours; +// } +// std::string body; +// serializeJson(doc, body); +// //int httpResponseCode = http.POST(body.c_str()); +// //http.end(); +// std::string my_json = body.c_str(); +// ESP_LOGW(TAG, "http_code= %s", body.c_str()); +// //this->powerpal_json_->publish_state(my_json); +// //this->cloud_uploader_->set_body(body); +// // empty triggers, but requirement of using the send function +// //std::vector response_triggers_; +// //this->cloud_uploader_->send(response_triggers_); +// } else { +// // apikey or device missing +// } +// } +// #endif void Powerpal::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { @@ -275,7 +313,7 @@ void Powerpal::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gat if (param->read.value[0] != this->reading_batch_size_[0]) { // reading batch size needs changing, so write auto status = - esp_ble_gattc_write_char(this->parent()->gattc_if, this->parent()->conn_id, + esp_ble_gattc_write_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->reading_batch_size_char_handle_, sizeof(this->reading_batch_size_), this->reading_batch_size_, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE); if (status) { @@ -283,7 +321,7 @@ void Powerpal::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gat } } else { // reading batch size is set correctly so subscribe to measurement notifications - auto status = esp_ble_gattc_register_for_notify(this->parent_->gattc_if, this->parent_->remote_bda, + auto status = esp_ble_gattc_register_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(), this->measurement_char_handle_); if (status) { ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", @@ -322,10 +360,11 @@ void Powerpal::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gat ESP_LOGI(TAG, "Recieved uuid read event"); this->powerpal_device_id_ = this->uuid_to_device_id_(param->read.value, param->read.value_len); ESP_LOGI(TAG, "Powerpal device id: %s", this->powerpal_device_id_.c_str()); -#ifdef USE_HTTP_REQUEST - this->powerpal_api_root_.append(this->powerpal_device_id_); - this->cloud_uploader_->set_url(this->powerpal_api_root_); -#endif +// #ifdef USE_HTTP_REQUEST +// this->powerpal_api_root_.append(this->powerpal_device_id_); +// ESP_LOGI(TAG, "URL request id: %s", this->powerpal_api_root_.c_str()); +// this->cloud_uploader_->set_url(this->powerpal_api_root_); +// #endif break; } @@ -334,22 +373,24 @@ void Powerpal::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gat ESP_LOGI(TAG, "Recieved serial_number read event"); this->powerpal_apikey_ = this->serial_to_apikey_(param->read.value, param->read.value_len); ESP_LOGI(TAG, "Powerpal apikey: %s", this->powerpal_apikey_.c_str()); -#ifdef USE_HTTP_REQUEST - http_request::Header acceptheader; - acceptheader.name = "Accept"; - acceptheader.value = "application/json"; - http_request::Header contentheader; - contentheader.name = "Content-Type"; - contentheader.value = "application/json"; - http_request::Header authheader; - authheader.name = "Authorization"; - authheader.value = this->powerpal_apikey_.c_str(); - std::list headers; - headers.push_back(acceptheader); - headers.push_back(contentheader); - headers.push_back(authheader); - this->cloud_uploader_->set_headers(headers); -#endif +// #ifdef USE_HTTP_REQUEST +// http_request::Header acceptheader; +// acceptheader.name = "Accept"; +// acceptheader.value = "application/json"; +// http_request::Header contentheader; +// contentheader.name = "Content-Type"; +// contentheader.value = "application/json"; +// http_request::Header authheader; +// authheader.name = "Authorization:"; +// authheader.value = this->powerpal_apikey_.c_str(); +// ESP_LOGI(TAG, "auth_header: %s", authheader.value); +// //ESP_LOGI(TAG, "auth_header_name: %s", authheader); +// std::list headers; +// headers.push_back(acceptheader); +// headers.push_back(contentheader); +// headers.push_back(authheader); +// this->cloud_uploader_->set_headers(headers); +// #endif break; } @@ -367,7 +408,7 @@ void Powerpal::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gat this->authenticated_ = true; auto read_reading_batch_size_status = - esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, + esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->reading_batch_size_char_handle_, ESP_GATT_AUTH_REQ_NONE); if (read_reading_batch_size_status) { ESP_LOGW(TAG, "Error sending read request for reading batch size, status=%d", read_reading_batch_size_status); @@ -375,7 +416,7 @@ void Powerpal::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gat if (!this->powerpal_apikey_.length()) { // read uuid (apikey) - auto read_uuid_status = esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, + auto read_uuid_status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->uuid_char_handle_, ESP_GATT_AUTH_REQ_NONE); if (read_uuid_status) { ESP_LOGW(TAG, "Error sending read request for powerpal uuid, status=%d", read_uuid_status); @@ -383,7 +424,7 @@ void Powerpal::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gat } if (!this->powerpal_device_id_.length()) { // read serial number (device id) - auto read_serial_number_status = esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, + auto read_serial_number_status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->serial_number_char_handle_, ESP_GATT_AUTH_REQ_NONE); if (read_serial_number_status) { ESP_LOGW(TAG, "Error sending read request for powerpal serial number, status=%d", read_serial_number_status); @@ -392,14 +433,14 @@ void Powerpal::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gat if (this->battery_ != nullptr) { // read battery - auto read_battery_status = esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, + auto read_battery_status = esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->battery_char_handle_, ESP_GATT_AUTH_REQ_NONE); if (read_battery_status) { ESP_LOGW(TAG, "Error sending read request for battery, status=%d", read_battery_status); } // Enable notifications for battery auto notify_battery_status = esp_ble_gattc_register_for_notify( - this->parent_->gattc_if, this->parent_->remote_bda, this->battery_char_handle_); + this->parent_->get_gattc_if(), this->parent_->get_remote_bda(), this->battery_char_handle_); if (notify_battery_status) { ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->parent_->address_str().c_str(), notify_battery_status); @@ -408,7 +449,7 @@ void Powerpal::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gat // read firmware version auto read_firmware_status = - esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, + esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->firmware_char_handle_, ESP_GATT_AUTH_REQ_NONE); if (read_firmware_status) { ESP_LOGW(TAG, "Error sending read request for led sensitivity, status=%d", read_firmware_status); @@ -416,7 +457,7 @@ void Powerpal::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gat // read led sensitivity auto read_led_sensitivity_status = - esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, + esp_ble_gattc_read_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->led_sensitivity_char_handle_, ESP_GATT_AUTH_REQ_NONE); if (read_led_sensitivity_status) { ESP_LOGW(TAG, "Error sending read request for led sensitivity, status=%d", read_led_sensitivity_status); @@ -426,7 +467,7 @@ void Powerpal::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gat } if (param->write.handle == this->reading_batch_size_char_handle_) { // reading batch size is now set correctly so subscribe to measurement notifications - auto status = esp_ble_gattc_register_for_notify(this->parent_->gattc_if, this->parent_->remote_bda, + auto status = esp_ble_gattc_register_for_notify(this->parent_->get_gattc_if(), this->parent_->get_remote_bda(), this->measurement_char_handle_); if (status) { ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", @@ -469,7 +510,7 @@ void Powerpal::gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_pa case ESP_GAP_BLE_AUTH_CMPL_EVT: { if (param->ble_security.auth_cmpl.success) { ESP_LOGI(TAG, "[%s] Writing pairing code to Powerpal", this->parent_->address_str().c_str()); - auto status = esp_ble_gattc_write_char(this->parent()->gattc_if, this->parent()->conn_id, + auto status = esp_ble_gattc_write_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->pairing_code_char_handle_, sizeof(this->pairing_code_), this->pairing_code_, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE); if (status) { diff --git a/esphome/components/powerpal_ble/powerpal_ble.h b/esphome/components/powerpal_ble/powerpal_ble.h index 71029edc7610..5e681110bafc 100644 --- a/esphome/components/powerpal_ble/powerpal_ble.h +++ b/esphome/components/powerpal_ble/powerpal_ble.h @@ -5,10 +5,7 @@ #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" #include "esphome/components/sensor/sensor.h" #include "esphome/core/defines.h" -#ifdef USE_HTTP_REQUEST -#include "esphome/components/http_request/http_request.h" -#include -#endif +#include "esphome/core/helpers.h" #ifdef USE_TIME #include "esphome/components/time/real_time_clock.h" @@ -64,7 +61,7 @@ static const espbt::ESPBTUUID POWERPAL_BATTERY_CHARACTERISTIC_UUID = espbt::ESPB static const uint8_t seconds_in_minute = 60; // seconds static const float kw_to_w_conversion = 1000.0; // conversion ratio -// static const std::string POWERPAL_API_URL = "https://readings.powerpal.net/api/v1/meter_reading/"; + class Powerpal : public esphome::ble_client::BLEClientNode, public Component { // class Powerpal : public esphome::ble_client::BLEClientNode, public PollingComponent { @@ -81,9 +78,11 @@ class Powerpal : public esphome::ble_client::BLEClientNode, public Component { void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; } void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; } void set_daily_energy_sensor(sensor::Sensor *daily_energy_sensor) { daily_energy_sensor_ = daily_energy_sensor; } -#ifdef USE_HTTP_REQUEST - void set_http_request(http_request::HttpRequestComponent *cloud_uploader) { cloud_uploader_ = cloud_uploader; } -#endif + void set_cost_sensor(sensor::Sensor *cost_sensor) { cost_sensor_ = cost_sensor;} + void set_pulses_sensor(sensor::Sensor *pulses_sensor) { pulses_sensor_ = pulses_sensor;} + void set_watt_hours(sensor::Sensor *watt_hours_sensor) {watt_hours_sensor_ = watt_hours_sensor;} + void set_timestamp(sensor::Sensor *timestamp_sensor) { timestamp_sensor_ = timestamp_sensor;} + void set_daily_pulses_sensor(sensor::Sensor *daily_pulses_sensor) { daily_pulses_sensor_ = daily_pulses_sensor;} #ifdef USE_TIME void set_time(time::RealTimeClock *time) { time_ = time; } #endif @@ -97,19 +96,18 @@ class Powerpal : public esphome::ble_client::BLEClientNode, public Component { void set_notification_interval(uint8_t reading_batch_size) { reading_batch_size_[0] = reading_batch_size; } void set_device_id(std::string powerpal_device_id) { powerpal_device_id_ = powerpal_device_id; } void set_apikey(std::string powerpal_apikey) { powerpal_apikey_ = powerpal_apikey; } - void set_energy_cost(float energy_cost) { energy_cost_ = energy_cost; } + void set_energy_cost(double energy_cost) { energy_cost_ = energy_cost; } + uint64_t daily_pulses_{0}; protected: std::string pkt_to_hex_(const uint8_t *data, uint16_t len); void decode_(const uint8_t *data, uint16_t length); void parse_battery_(const uint8_t *data, uint16_t length); void parse_measurement_(const uint8_t *data, uint16_t length); + std::string uuid_to_device_id_(const uint8_t *data, uint16_t length); std::string serial_to_apikey_(const uint8_t *data, uint16_t length); -#ifdef USE_HTTP_REQUEST - void store_measurement_(uint16_t measurement, time_t timestamp, uint32_t watt_hours, float cost); - void upload_data_to_cloud_(); -#endif + bool authenticated_; @@ -117,9 +115,13 @@ class Powerpal : public esphome::ble_client::BLEClientNode, public Component { sensor::Sensor *power_sensor_{nullptr}; sensor::Sensor *energy_sensor_{nullptr}; sensor::Sensor *daily_energy_sensor_{nullptr}; -#ifdef USE_HTTP_REQUEST - http_request::HttpRequestComponent *cloud_uploader_{nullptr}; -#endif + sensor::Sensor *cost_sensor_{nullptr}; + sensor::Sensor *pulses_sensor_{nullptr}; + sensor::Sensor *daily_pulses_sensor_{nullptr}; + sensor::Sensor *watt_hours_sensor_{nullptr}; + sensor::Sensor *timestamp_sensor_{nullptr}; + + #ifdef USE_TIME optional time_{}; #endif @@ -129,16 +131,13 @@ class Powerpal : public esphome::ble_client::BLEClientNode, public Component { uint8_t reading_batch_size_[4] = {0x01, 0x00, 0x00, 0x00}; float pulses_per_kwh_; float pulse_multiplier_; - uint64_t daily_pulses_{0}; uint64_t total_pulses_{0}; uint8_t stored_measurements_count_{0}; std::vector stored_measurements_; - // std::string powerpal_api_root_ = "http://192.168.1.167:1880/powerpal/"; - std::string powerpal_api_root_ = "https://readings.powerpal.net/api/v1/meter_reading/"; - std::string powerpal_device_id_; // = "00002bc3"; - std::string powerpal_apikey_; // = "4a89e298-b17b-43e7-a0c1-fcd1412e98ef"; - float energy_cost_; + std::string powerpal_device_id_; + std::string powerpal_apikey_; + double energy_cost_; uint16_t pairing_code_char_handle_ = 0x2e; uint16_t reading_batch_size_char_handle_ = 0x33; @@ -151,7 +150,7 @@ class Powerpal : public esphome::ble_client::BLEClientNode, public Component { uint16_t serial_number_char_handle_ = 0x2b; }; -} // namespace powerpal_ble -} // namespace esphome +} +} #endif diff --git a/esphome/components/powerpal_ble/sensor.py b/esphome/components/powerpal_ble/sensor.py index 08075b1b5c12..fd6d42357e05 100644 --- a/esphome/components/powerpal_ble/sensor.py +++ b/esphome/components/powerpal_ble/sensor.py @@ -1,7 +1,7 @@ import logging import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import sensor, ble_client, time, http_request +from esphome.components import sensor, ble_client, time from esphome.const import ( CONF_ID, CONF_BATTERY_LEVEL, @@ -17,6 +17,7 @@ UNIT_WATT, UNIT_PERCENT, CONF_TIME_ID, + CONF_TEXT_SENSORS, ) _LOGGER = logging.getLogger(__name__) @@ -30,12 +31,15 @@ CONF_PAIRING_CODE = "pairing_code" CONF_NOTIFICATION_INTERVAL = "notification_interval" CONF_PULSES_PER_KWH = "pulses_per_kwh" -CONF_HTTP_REQUEST_ID = "http_request_id" CONF_COST_PER_KWH = "cost_per_kwh" CONF_POWERPAL_DEVICE_ID = "powerpal_device_id" CONF_POWERPAL_APIKEY = "powerpal_apikey" CONF_DAILY_ENERGY = "daily_energy" - +CONF_WATT_HOURS = "watt_hours" +CONF_TIME_STAMP = "timestamp" +CONF_PULSES = "pulses" +CONF_COST = "cost" +CONF_DAILY_PULSES = "daily_pulses" def _validate(config): if CONF_DAILY_ENERGY in config and CONF_TIME_ID not in config: @@ -43,10 +47,6 @@ def _validate(config): "Using daily_energy without a time_id means relying on your Powerpal's RTC for packet times, which is not recommended. " "Please consider adding a time component to your ESPHome yaml, and it's time_id to your powerpal_ble component." ) - if CONF_HTTP_REQUEST_ID in config and CONF_COST_PER_KWH not in config: - raise cv.Invalid( - f"If using the Powerpal cloud uploader, you must also set '{CONF_COST_PER_KWH}'" - ) return config @@ -102,6 +102,7 @@ def powerpal_apikey(value): accuracy_decimals=3, device_class=DEVICE_CLASS_ENERGY, state_class=STATE_CLASS_TOTAL_INCREASING, + ), cv.Optional(CONF_ENERGY): sensor.sensor_schema( unit_of_measurement=UNIT_KILOWATT_HOURS, @@ -109,6 +110,13 @@ def powerpal_apikey(value): device_class=DEVICE_CLASS_ENERGY, state_class=STATE_CLASS_TOTAL_INCREASING, ), + cv.Optional(CONF_WATT_HOURS): sensor.sensor_schema(), + cv.Optional(CONF_PULSES): sensor.sensor_schema(), + cv.Optional(CONF_DAILY_PULSES): sensor.sensor_schema(), + cv.Optional(CONF_TIME_STAMP): sensor.sensor_schema(), + cv.Optional(CONF_COST): sensor.sensor_schema( + accuracy_decimals=11 + ), cv.Required(CONF_PAIRING_CODE): cv.int_range(min=1, max=999999), cv.Required(CONF_NOTIFICATION_INTERVAL): cv.int_range(min=1, max=60), cv.Required(CONF_PULSES_PER_KWH): cv.float_range(min=1), @@ -118,9 +126,6 @@ def powerpal_apikey(value): accuracy_decimals=0, entity_category=ENTITY_CATEGORY_DIAGNOSTIC, ), - cv.Optional(CONF_HTTP_REQUEST_ID): cv.use_id( - http_request.HttpRequestComponent - ), cv.Optional(CONF_COST_PER_KWH): cv.float_range(min=0), cv.Optional( CONF_POWERPAL_DEVICE_ID @@ -155,6 +160,25 @@ async def to_code(config): sens = await sensor.new_sensor(config[CONF_DAILY_ENERGY]) cg.add(var.set_daily_energy_sensor(sens)) + if CONF_PULSES in config: + sens = await sensor.new_sensor(config[CONF_PULSES]) + cg.add(var.set_pulses_sensor(sens)) + + if CONF_DAILY_PULSES in config: + sens = await sensor.new_sensor(config[CONF_DAILY_PULSES]) + cg.add(var.set_daily_pulses_sensor(sens)) + if CONF_WATT_HOURS in config: + sens = await sensor.new_sensor(config[CONF_WATT_HOURS]) + cg.add(var.set_watt_hours(sens)) + + if CONF_TIME_STAMP in config: + sens = await sensor.new_sensor(config[CONF_TIME_STAMP]) + cg.add(var.set_timestamp(sens)) + + if CONF_COST in config: + sens = await sensor.new_sensor(config[CONF_COST]) + cg.add(var.set_cost_sensor(sens)) + if CONF_PAIRING_CODE in config: cg.add(var.set_pairing_code(config[CONF_PAIRING_CODE])) @@ -168,10 +192,6 @@ async def to_code(config): sens = await sensor.new_sensor(config[CONF_BATTERY_LEVEL]) cg.add(var.set_battery(sens)) - if CONF_HTTP_REQUEST_ID in config: - cg.add_define("USE_HTTP_REQUEST") - http_request_component = await cg.get_variable(config[CONF_HTTP_REQUEST_ID]) - cg.add(var.set_http_request(http_request_component)) if CONF_COST_PER_KWH in config: cg.add(var.set_energy_cost(config[CONF_COST_PER_KWH]))