Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add reset energy #22

Merged
merged 4 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ jobs:
- name: Build ESPHome firmware to verify configuration
uses: esphome/build-action@v4.0.1
with:
yaml_file: ${{ matrix.file }}
yaml-file: ${{ matrix.file }}
29 changes: 23 additions & 6 deletions athom-energy-monitor-x2.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
substitutions:
name: "athom-energy-monitor-x2"
friendly_name: "Athom Energy Monitor"
name: "athom-em-2"
friendly_name: "Athom Energy Meter"
# Allows ESP device to be automatically lined to an 'Area' in Home Assistant. Typically used for areas such as 'Lounge Room', 'Kitchen' etc
room: ""
device_description: "athom bl0906 energy monitor (2 channels)"
project_name: "Athom Technology.Athom Energy Monitor(2 Channels)"
project_version: "2.0.2"
device_description: "athom bl0906 energy meter (2 channels)"
project_name: "Athom Technology.Athom Energy Meter(2 Channels)"
project_version: "2.0.3"
update_interval: 5s
# Define a domain for this device to use. i.e. iot.home.lan (so device will appear as athom-smart-plug-v2.iot.home.lan in DNS/DHCP logs)
dns_domain: ""
Expand Down Expand Up @@ -42,7 +42,7 @@ esphome:
friendly_name: "${friendly_name}"
area: "${room}"
name_add_mac_suffix: true
min_version: 2024.6.0
min_version: 2024.7.0
project:
name: "${project_name}"
version: "${project_version}"
Expand Down Expand Up @@ -180,6 +180,7 @@ sensor:
device_class: ""

- platform: bl0906
id: id_bl0906
update_interval: "${update_interval}"
frequency:
name: 'Frequency'
Expand Down Expand Up @@ -301,6 +302,22 @@ button:
internal: false
entity_category: config

- platform: template
name: Reset Energy
entity_category: config
on_press:
then:
- globals.set:
id: id_Energy_1_persist
value: '0.0'
- globals.set:
id: id_Energy_2_persist
value: '0.0'
- globals.set:
id: id_Energy_sum_persist
value: '0.0'
- bl0906.reset_energy: id_bl0906

text_sensor:
- platform: wifi_info
ip_address:
Expand Down
41 changes: 35 additions & 6 deletions athom-energy-monitor-x6.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
substitutions:
name: "athom-energy-monitor-x6"
friendly_name: "Athom Energy Monitor"
name: "athom-em-6"
friendly_name: "Athom Energy Meter"
# Allows ESP device to be automatically lined to an 'Area' in Home Assistant. Typically used for areas such as 'Lounge Room', 'Kitchen' etc
room: ""
device_description: "athom bl0906 energy monitor (6 channels)"
project_name: "Athom Technology.Athom Energy Monitor(6 Channels)"
project_version: "2.0.2"
device_description: "athom bl0906 energy meter (6 channels)"
project_name: "Athom Technology.Athom Energy Meter(6 Channels)"
project_version: "2.0.3"
update_interval: 5s
# Define a domain for this device to use. i.e. iot.home.lan (so device will appear as athom-smart-plug-v2.iot.home.lan in DNS/DHCP logs)
dns_domain: ""
Expand Down Expand Up @@ -54,7 +54,7 @@ esphome:
friendly_name: "${friendly_name}"
area: "${room}"
name_add_mac_suffix: true
min_version: 2024.6.0
min_version: 2024.7.0
project:
name: "${project_name}"
version: "${project_version}"
Expand Down Expand Up @@ -236,6 +236,7 @@ sensor:
device_class: ""

- platform: bl0906
id: id_bl0906
update_interval: "${update_interval}"
frequency:
name: 'Frequency'
Expand Down Expand Up @@ -489,6 +490,34 @@ button:
internal: false
entity_category: config

- platform: template
name: Reset Energy
entity_category: config
on_press:
then:
- globals.set:
id: id_Energy_1_persist
value: '0.0'
- globals.set:
id: id_Energy_2_persist
value: '0.0'
- globals.set:
id: id_Energy_3_persist
value: '0.0'
- globals.set:
id: id_Energy_4_persist
value: '0.0'
- globals.set:
id: id_Energy_5_persist
value: '0.0'
- globals.set:
id: id_Energy_6_persist
value: '0.0'
- globals.set:
id: id_Energy_sum_persist
value: '0.0'
- bl0906.reset_energy: id_bl0906

text_sensor:
- platform: wifi_info
ip_address:
Expand Down
52 changes: 52 additions & 0 deletions components/bl0906/bl0906.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,19 @@ static const uint8_t BL0906_WATTGN_4 = 0xBA;
static const uint8_t BL0906_WATTGN_5 = 0xBD;
static const uint8_t BL0906_WATTGN_6 = 0xBE; // Channel_6

// User write protection setting register,
// You must first write 0x5555 to the write protection setting register before writing to other registers.
static const uint8_t BL0906_USR_WRPROT = 0x9E;

// Reset Register
static const uint8_t BL0906_SOFT_RESET = 0x9F;

const uint8_t BL0906_INIT[2][6] = {
// Reset to default
{BL0906_WRITE_COMMAND, BL0906_SOFT_RESET, 0x5A, 0x5A, 0x5A, 0x52},
// Enable User Operation Write
{BL0906_WRITE_COMMAND, BL0906_USR_WRPROT, 0x55, 0x55, 0x00, 0xB7}};

void BL0906::loop() {
if (this->current_channel_ == UINT8_MAX) {
return;
Expand Down Expand Up @@ -126,6 +139,7 @@ void BL0906::loop() {
return;
}
this->current_channel_++;
handleActionCallback();
}

void BL0906::setup() {
Expand Down Expand Up @@ -163,6 +177,44 @@ uint8_t bl0906_checksum(const uint8_t address, const DataPacket *data) {
return (address + data->l + data->m + data->h) ^ 0xFF;
}

int BL0906::addActionCallBack(ActionCallbackFuncPtr ptrFunc) {
m_vecActionCallback.push_back(ptrFunc);
return m_vecActionCallback.size();
}

void BL0906::handleActionCallback() {
if (m_vecActionCallback.size() == 0) {
return;
}
ActionCallbackFuncPtr ptrFunc = nullptr;
for (int i = 0; i < m_vecActionCallback.size(); i++) {
ptrFunc = m_vecActionCallback[i];
if (ptrFunc) {
ESP_LOGI(TAG, "HandleActionCallback[%d]...", i);
(this->*ptrFunc)();
}
}

while (this->available()) {
this->read();
}

m_vecActionCallback.clear();
if (m_process_state != PROCESS_DONE) {
m_process_state = PROCESS_DONE;
}
}

// Reset energy
void BL0906::reset_energy() {
this->write_array(BL0906_INIT[0], 6);
delay(1);
this->flush();

ESP_LOGW(TAG, "RMSOS:%02X%02X%02X%02X%02X%02X", BL0906_INIT[0][0], BL0906_INIT[0][1], BL0906_INIT[0][2],
BL0906_INIT[0][3], BL0906_INIT[0][4], BL0906_INIT[0][5]);
}

// Read data
void BL0906::read_data(const uint8_t address, const float reference, sensor::Sensor *sensor) {
if (sensor == nullptr) {
Expand Down
27 changes: 27 additions & 0 deletions components/bl0906/bl0906.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/uart/uart.h"
#include "esphome/core/component.h"
#include "esphome/core/datatypes.h"
#include "esphome/core/automation.h"

// https://www.belling.com.cn/media/file_object/bel_product/BL0906/datasheet/BL0906_V1.02_cn.pdf
// https://www.belling.com.cn/media/file_object/bel_product/BL0906/guide/BL0906%20APP%20Note_V1.02.pdf
Expand Down Expand Up @@ -60,6 +62,11 @@ struct sbe24_t { // NOLINT(readability-identifier-naming,altera-struct-pack-ali
int8_t h;
} __attribute__((packed));

typedef enum process_state_ { PROCESS_DONE = 0 } process_state;
template<typename... Ts> class ResetEnergyAction;
class BL0906;
typedef void (BL0906::*ActionCallbackFuncPtr)(void);

class BL0906 : public PollingComponent, public uart::UARTDevice {
public:
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
Expand Down Expand Up @@ -92,6 +99,8 @@ class BL0906 : public PollingComponent, public uart::UARTDevice {
void dump_config() override;

protected:
process_state m_process_state{PROCESS_DONE};
template<typename... Ts> friend class ResetEnergyAction;
sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *current_sensor_1_{nullptr};
sensor::Sensor *current_sensor_2_{nullptr};
Expand Down Expand Up @@ -136,6 +145,8 @@ class BL0906 : public PollingComponent, public uart::UARTDevice {

static int32_t to_int32_t(sbe24_t input);

void reset_energy();

void read_data(const uint8_t address, const float reference, sensor::Sensor *sensor_);

void Bias_correction(const uint8_t address, const float measurements, const float Correction);
Expand All @@ -144,6 +155,22 @@ class BL0906 : public PollingComponent, public uart::UARTDevice {
const float coefficient);

uint8_t current_channel_ = 0;
int addActionCallBack(ActionCallbackFuncPtr ptrFunc);
void handleActionCallback();
bool isNeedHandleActionCallback() { return (m_vecActionCallback.size() > 0); }

private:
std::vector<ActionCallbackFuncPtr> m_vecActionCallback{};
};

template<typename... Ts> class ResetEnergyAction : public Action<Ts...> {
public:
ResetEnergyAction(BL0906 *bl0906) : bl0906_(bl0906) {}

void play(Ts... x) override { this->bl0906_->addActionCallBack(&BL0906::reset_energy);}

protected:
BL0906 *bl0906_;
};
} // namespace bl0906
} // namespace esphome
17 changes: 16 additions & 1 deletion components/bl0906/sensor.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.automation import maybe_simple_id
from esphome.components import sensor, uart
from esphome.const import (
CONF_CHANNEL,
Expand Down Expand Up @@ -28,11 +30,12 @@
)

DEPENDENCIES = ["uart"]

AUTO_LOAD = ["bl0906"]
CONF_TOTAL_ENERGY = "total_energy"

bl0906_ns = cg.esphome_ns.namespace("bl0906")
BL0906 = bl0906_ns.class_("BL0906", cg.PollingComponent, uart.UARTDevice)
ResetEnergyAction = bl0906_ns.class_("ResetEnergyAction", automation.Action)

CONFIG_SCHEMA = (
cv.Schema(
Expand Down Expand Up @@ -105,6 +108,18 @@
.extend(cv.polling_component_schema("60s"))
)

@automation.register_action(
"bl0906.reset_energy",
ResetEnergyAction,
maybe_simple_id(
{
cv.Required(CONF_ID): cv.use_id(BL0906),
}
),
)
async def reset_energy_to_code(config, action_id, template_arg, args):
paren = await cg.get_variable(config[CONF_ID])
return cg.new_Pvariable(action_id, template_arg, paren)

async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
Expand Down