Skip to content

Instantly share code, notes, and snippets.

@mikkel22010
Forked from neromatrix/NPW2500.yaml
Created March 12, 2024 13:27
Show Gist options
  • Save mikkel22010/d18513034cfb5ccff8e81c8a8e599d04 to your computer and use it in GitHub Desktop.
Save mikkel22010/d18513034cfb5ccff8e81c8a8e599d04 to your computer and use it in GitHub Desktop.
# No Power Wasted 2500 V0.21
# ESPHome Software für alle gängigen Versionen des Balkonspeichers xy2500.
# Diese Version ist speziell für Verwendung mit dem Home Assistant ausgelegt und
# inkludiert die Kommunikation mit diesem, Regelung für Nulleinspeisung
# und in Zukunft einiges mehr.
# Die Kommunikation mit dem Balkonspeichers xy2500 basiert auf der Arbeit von noone2K.
# Die Hauptseite für neue Entwicklungen, Integration MQTT, openhab usw. findet ihr unter:
# https://gist.github.com/noone2k/2ddea4c9bf116aaaefb8626b064d9a41
#
# Mögen euch die Bits gnädig sein,
# neromatrix
esphome:
name: esphome-npw2500
friendly_name: No Power Wasted 2500
esp32:
board: esp32dev
framework:
type: esp-idf
#ota: #remove '#' to use ota
# Enable wifi
wifi:
id: npw2500_wifi
ssid: !secret wifi_ssid
password: !secret wifi_password
reboot_timeout: 0s
fast_connect: True
# Enable Home Assistant API
api:
id: api_server
# Enable logging
logger:
esp32_ble_tracker:
#web_server: #remove '#' to use web_server
# port: 80
# local: true
# js_include: "./v2/www.js"
# js_url: "/0.js"
# version: 2
ble_client:
# mac address of the batterie from secrets
mac_address: !secret battery_ble_mac
id: npw2500_ble
########## globals ##########
globals:
# helper for npw2500_config_limit_nonpersistent_relative name
- id: npw2500_config_limit_nonpersistent_relative
type: std::string
restore_value: yes
initial_value: !secret inverter_rel_power_limit
# communication with all devices ready
- id: npw2500_communication_ready
type: bool
restore_value: no
initial_value: '0'
# communication with all devices just started
- id: npw2500_communication_started
type: bool
restore_value: no
initial_value: '0'
# response 0x03 received
- id: npw2500_response_0x03_data_ready
type: bool
restore_value: no
initial_value: '0'
# response 0x0f received
- id: npw2500_response_0x0f_data_ready
type: bool
restore_value: no
initial_value: '0'
# switches updated after communication ready
- id: npw2500_switches_updated
type: bool
restore_value: no
initial_value: '0'
########## text_sensors ##########
text_sensor:
# should report NPW Version, does not work
- platform: version
id: npw2500_version
name: "No Power Wasted 2500"
hide_timestamp: true
# reports the internal PowerZero Status
- platform: template
id: npw2500_powerzero_status
name: PowerZero Status
########## binary_sensors ##########
binary_sensor:
# Bluetooth connected
- platform: template
name: ESP Ble connected
id: npw2500_ble_connected
# Wifi connected
- platform: template
name: ESP Wifi connected
id: npw2500_wifi_connected
# HA Api connected
- platform: template
name: ESP HA connected
id: npw2500_api_connected
# Input Ch1 active
- platform: template
name: Input Ch1 active
id: npw2500_input_ch1_active
# Input Ch2 active
- platform: template
name: Input Ch2 active
id: npw2500_input_ch2_active
# Input Ch1 transparent - Passthrough active/inactive
- platform: template
name: Input Ch1 transparent
id: npw2500_input_ch1_transparent
# Input Ch2 transparent - Passthrough active/inactive
- platform: template
name: Input Ch2 transparent
id: npw2500_input_ch2_transparent
# Output Ch1 active - does not show status in Passthrough Modus
- platform: template
name: Ouput Ch1 active
id: npw2500_output_ch1_active
# Output Ch2 active - does not show status in Passthrough Modus
- platform: template
name: Ouput Ch2 active
id: npw2500_output_ch2_active
# Battery connected to Wifi
- platform: template
name: Battery WiFi connected
id: npw2500_battery_wifi
# Battery connected to MQTT
- platform: template
name: Battery MQTT connected
id: npw2500_battery_mqtt
# reflects status of Passthrough switch
- platform: template
name: PassThrough active
id: npw2500_passthrough_active
# PowerZero enabled/disabled
- platform: template
name: PowerZero active
id: npw2500_zeropower_active
# OpenDTU status of inverter producing
- platform: homeassistant
name: PowerZero Inverter producing
id: npw2500_powerzero_inverter_producing
entity_id: !secret inverter_producing
internal: false
########## sensors ##########
sensor:
# Battery Input Power (Ch1 + Ch2)
- platform: template
name: Input Power
id: npw2500_input_power
unit_of_measurement: 'W'
device_class: 'power'
accuracy_decimals: 0
# Battery Input Power Ch1
- platform: template
name: Input Ch1 Power
id: npw2500_input_power_ch1
unit_of_measurement: 'W'
device_class: 'power'
accuracy_decimals: 0
# Battery Input Power Ch2
- platform: template
name: Input Ch2 Power
id: npw2500_input_power_ch2
unit_of_measurement: 'W'
device_class: 'power'
accuracy_decimals: 0
# Battery Ouput Power (Ch1 + Ch2)
- platform: template
name: Output Power
id: npw2500_output_power
unit_of_measurement: 'W'
device_class: 'power'
accuracy_decimals: 0
# Battery Ouput Power Ch1
- platform: template
name: Output Power Ch1
id: npw2500_output_power_ch1
unit_of_measurement: 'W'
device_class: 'power'
accuracy_decimals: 0
# Battery Ouput Power Ch2
- platform: template
name: Output Power Ch2
id: npw2500_output_power_ch2
unit_of_measurement: 'W'
device_class: 'power'
accuracy_decimals: 0
# Battery InOutput Power (Input power - Output power)
- platform: template
name: Power InOut
id: npw2500_inout_power
unit_of_measurement: 'W'
device_class: 'power'
accuracy_decimals: 0
# Battery SOC
- platform: template
name: Battery SOC
id: npw2500_battery_soc
unit_of_measurement: '%'
device_class: 'battery'
accuracy_decimals: 0
# Battery SOC dynamic
- platform: template
name: Battery SOC dynamic.
id: npw2500_battery_soc_dynamic
unit_of_measurement: '%'
device_class: 'battery'
accuracy_decimals: 1
# Battery remaining capacity
- platform: template
name: Battery remaining capacity
id: npw2500_battery_remaining
unit_of_measurement: 'W'
device_class: 'battery'
accuracy_decimals: 0
# Ha Integration import grid power
- platform: homeassistant
name: Grid Power
id: npw2500_grid_power
entity_id: !secret npw2500_grid_power
unit_of_measurement: 'W'
device_class: 'power'
accuracy_decimals: 0
internal: false
# Grid Power offset for PowerZero
- platform: template
name: Grid Power Offset
id: npw2500_grid_power_offset
unit_of_measurement: 'W'
device_class: 'power'
accuracy_decimals: 0
# Ha Integration openDTU Power limit rel.
- platform: homeassistant
name: openDTU Power limit rel. actual value
id: npw2500_limit_nonpersistent_relative
entity_id: !secret inverter_rel_power_limit_2
unit_of_measurement: '%'
device_class: 'power_factor'
accuracy_decimals: 0
internal: false
# Internal sensor openDTU Power limit rel. Target value
- platform: template
name: openDTU Power limit rel. target value
id: opendtu_limit_nonpersistent_relative_target_value
unit_of_measurement: '%'
device_class: 'power'
accuracy_decimals: 0
internal: false
# Battery Temperature 1
- platform: template
name: Temperature Sensor 1
id: npw2500_temperature_1
unit_of_measurement: '°C'
device_class: 'temperature'
accuracy_decimals: 0
# Battery Temperature 2
- platform: template
name: Temperature Sensor 2
id: npw2500_temperature_2
unit_of_measurement: '°C'
device_class: 'temperature'
accuracy_decimals: 0
# Battery summation of Cell Voltages
- platform: template
name: "Cell Voltage sum"
id: npw2500_cell_vsum
unit_of_measurement: 'V'
device_class: 'voltage'
accuracy_decimals: 3
# Battery maximum of all Cell Voltages
- platform: template
name: "Cell Voltage max"
id: npw2500_cell_vmax
unit_of_measurement: 'V'
device_class: 'voltage'
accuracy_decimals: 3
# Battery maximumminimum of all Cell Voltages
- platform: template
name: "Cell Voltage min"
id: npw2500_cell_vmin
unit_of_measurement: 'V'
device_class: 'voltage'
accuracy_decimals: 3
# Battery average value of all Cell Voltages
- platform: template
name: "Cell Voltage avg"
id: npw2500_cell_vavg
unit_of_measurement: 'V'
device_class: 'voltage'
accuracy_decimals: 3
# Battery max difference between all Cell Voltages
- platform: template
name: "Cell Voltage diff"
id: npw2500_cell_vdiff
unit_of_measurement: 'V'
device_class: 'voltage'
accuracy_decimals: 3
# Battery Discharge threshold
- platform: template
name: Discharge threshold
id: npw2500_discharge_treshold
unit_of_measurement: '%'
device_class: 'power'
accuracy_decimals: 0
# Battery Solar Charge threshold
- platform: template
name: Solar Charge threshold
id: npw2500_solar_charge_treshold
unit_of_measurement: 'W'
device_class: 'power'
accuracy_decimals: 0
# Battery Software Version
- platform: template
name: "Battery Version"
id: npw2500_device_version
accuracy_decimals: 3
# Home Power usage
- platform: template
name: "Home Power Consumption"
id: npw2500_home_power_consuption
unit_of_measurement: 'W'
device_class: 'power'
accuracy_decimals: 0
# ESP Bluetooth client
- platform: ble_client
ble_client_id: npw2500_ble
type: characteristic
name: "npw2500Info"
id: npw2500_Info
service_uuid: 'ff00'
characteristic_uuid: 'ff02'
update_interval: never
internal: True
notify: True
lambda: |-
std::vector<char> xdata;
for (auto b : x) { xdata.push_back(b); }
id(ble_parse_response).execute(xdata);
return (float)x[0];
########## switches ##########
switch:
# Battery Output switch Ch1
- platform: template
name: Set Power Out Switch Ch1
id: npw2500_powerout_switch_ch1
icon: mdi:toggle-switch
optimistic: True
restore_mode: RESTORE_DEFAULT_OFF
on_turn_on:
then:
- script.execute:
id: ble_switch_powerout
on_turn_off:
then:
- script.execute:
id: ble_switch_powerout
# Battery Output switch Ch2
- platform: template
name: Set Power Out Switch Ch2
id: npw2500_powerout_switch_ch2
icon: mdi:toggle-switch
optimistic: True
restore_mode: RESTORE_DEFAULT_ON
on_turn_on:
then:
- script.execute:
id: ble_switch_powerout
on_turn_off:
then:
- script.execute:
id: ble_switch_powerout
# Battery Output switch Passtrough
- platform: template
name: Set PV2 Passtrough Switch
id: npw2500_powerout_pv2_switch
icon: mdi:toggle-switch
optimistic: True
restore_mode: RESTORE_DEFAULT_ON
on_turn_on:
then:
- script.execute:
id: ble_command
ble_cmd: 0x0D
ble_cmd_parm: 0x00
- script.wait: ble_command
on_turn_off:
then:
- script.execute:
id: ble_command
ble_cmd: 0x0D
ble_cmd_parm: 0x01
- script.wait: ble_command
# PowerZero enable
- platform: template
name: PowerZero enabled
id: npw2500_zeropower_enabled
optimistic: True
restore_mode: RESTORE_DEFAULT_OFF
# ble client enable
- platform: ble_client
ble_client_id: npw2500_ble
name: "Enable Bluetooth"
internal: True
########## numbers ##########
number:
# Slider Discharge threshold
- platform: template
name: Set Discharge threshold
id: npw2500_discharge_slider
min_value: 10
max_value: 90
step: 10
optimistic: true
initial_value : 90
restore_value : true
unit_of_measurement: '%'
device_class: 'battery'
icon: 'mdi:speedometer'
on_value:
then:
- script.execute:
id: ble_command
ble_cmd: 0x0B
ble_cmd_parm: !lambda 'return x;'
- script.wait: ble_command
# Slider Solar Charge threshold
- platform: template
name: Set Solar Charge threshold
id: npw2500_solar_charge_slider
min_value: 0
max_value: 500
step: 10
initial_value : 500
restore_value : true
optimistic: true
unit_of_measurement: 'W'
device_class: 'energy'
icon: 'mdi:speedometer'
on_value:
then:
- script.execute:
id: ble_command
ble_cmd: 0x0C
ble_cmd_parm: !lambda 'return int(x);'
- script.wait: ble_command
# Slider Power Limit rel.
- platform: template
name: Set Power Limit rel.
id: npw2500_powerlimit_slider
min_value: 2
max_value: 100
step: 1
initial_value: 10
restore_value: true
optimistic: true
unit_of_measurement: '%'
device_class: 'power_factor'
icon: 'mdi:speedometer'
# Slider Max Cell Voltage
- platform: template
name: Set max Cell Voltage
id: npw2500_max_cell_voltage_slider
min_value: 3.2
max_value: 3.6
step: 0.01
initial_value: 3.3
restore_value: true
optimistic: true
unit_of_measurement: 'V'
device_class: 'voltage'
icon: 'mdi:speedometer'
# Slider Set Grid Power Offset
- platform: template
name: Set Grid Power Offset
id: npw2500_power_offset_slider
min_value: -400
max_value: 400
step: 10
initial_value: 0
restore_value: true
optimistic: true
unit_of_measurement: 'W'
device_class: 'power'
icon: 'mdi:speedometer'
### homeassistant synchronize ESP time
### needed for scheduler implementations
time:
- platform: homeassistant
id: homeassistant_time
on_time_sync:
then:
- logger.log: "Synchronized system clock"
########## intervals ##########
interval:
- interval: 5 sec
then:
- logger.log: "Auf gehts..."
- lambda: |-
id(npw2500_wifi_connected).publish_state(id(npw2500_wifi).is_connected()); // publish state of wifi
id(npw2500_ble_connected).publish_state(id(npw2500_ble).connected()); // publish state of ble
id(npw2500_api_connected).publish_state(global_api_server->is_connected()); // publish state of HA Api
id(npw2500_communication_ready) = id(npw2500_wifi_connected).state && // set global npw2500_communication_ready
id(npw2500_ble_connected).state && id(npw2500_api_connected).state;
ESP_LOGD("npw2500","service HA api: %d wifi: %d ble: %d", // log values
id(npw2500_api_connected).state,id(npw2500_wifi_connected).state,id(npw2500_ble_connected).state);
- if:
condition:
lambda: 'return id(npw2500_communication_ready);'
then:
- script.execute:
id: ble_command
ble_cmd: 0x03 # Request Command 0x03
ble_cmd_parm: 0x01
- logger.log: "Request command 0x03 sent"
- delay: 2 sec
- script.execute:
id: ble_command
ble_cmd: 0x0f # Request Command 0x0f
ble_cmd_parm: 0x01
- logger.log: "Request command 0x0f sent"
- delay: 2 sec
- interval: 5 sec
then:
- if:
condition:
lambda: 'return id(npw2500_communication_ready);'
then:
- script.execute:
id: power_zero # Call script powerzero
- interval: 30 sec
then:
- if:
condition:
lambda: 'return id(npw2500_communication_ready);'
then:
- script.execute:
id: watch_task # Call script watch_task
script:
- id: ble_parse_response
parameters:
x: char[]
then:
lambda: |-
//ESP_LOG_BUFFER_HEXDUMP("npw2500", &x[0], x.size(), ESP_LOG_ERROR);
//### Cell parser by neromatrix ###
//### Ver. 0.4 ###
//### highlimit now from slider id(npw2500_max_cell_voltage_slider).state ###
if ((std::count (x.begin(), x.end(), '_') == 16) || (std::count (x.begin(), x.begin() + 10, '_') == 3))
{
int pos = 0;
int soc = 0;
int t1 = 0;
int t2 = 0;
float cv = 0.0;
float cmin = std::numeric_limits<float>::max();
float cmax = std::numeric_limits<float>::min();
float ct = 0.0;
int found = -1;
char delimiter = '_';
std::string xstr;
ESP_LOGD("npw2500","Parsing cell information, response of request command 0xf");
id(npw2500_response_0x0f_data_ready) = true;
xstr.assign(x.begin(), x.end()); // copy values from vector into string xstr, deep copy
xstr = xstr + delimiter; // append delimiter to xstr
found = xstr.find(delimiter); // search for position of the first delimiter
while (found != -1) // loop until no more delimiter found
{
if(pos == 0) soc = atoi( xstr.substr(0, found).c_str()); // pos 0 get int value of device SOC // pos 0 don't care
if(pos == 1) t1 = atoi( xstr.substr(0, found).c_str()); // pos 1 get int value of temperature sensor 1
if(pos == 2) t2 = atoi( xstr.substr(0, found).c_str()); // pos 2 get int value of temperature sensor 2
if((pos >= 3) && (pos <= 16)) // pos 3-16 parse pos for the 14 cell voltages
{
ct = atof( xstr.substr(0, found).c_str()); // get float value of pos x
cv += ct; // add actual value to var cv
if(ct > cmax) cmax = ct; // check for higher value as stored in cmax
if(ct < cmin) cmin = ct; // check for lower value as stored in cmin
}
xstr.erase(xstr.begin(), xstr.begin() + found + 1); // remove parsed string part
found = xstr.find(delimiter); // find next delimiter
pos++; // increment pos
}
/* calculate SoC from cell voltages
cell empty = lowlimit = 0% SoC
cell full = highlimit = 100% SoC = id(npw2500_max_cell_voltage_slider).state
*/
float lowlimit = 3.0; // low voltage limit
float highlimit = id(npw2500_max_cell_voltage_slider).state ; // changed V0.4 - high voltage limit from slider
float soccalc = 100*((cv/14000)
- highlimit)/(highlimit - lowlimit) + 100; // equation of line with two points (0,lowlimit) (100,highlimit)
id(npw2500_battery_soc_dynamic).publish_state(soccalc); // id changed V0.4 - dynamic SOC calculated from cell voltages (%)
id(npw2500_temperature_1).publish_state(t1); // Temperature 1 (°C)
id(npw2500_temperature_2).publish_state(t2); // Temperature 2 (°C)
id(npw2500_cell_vsum).publish_state(cv/1000); // sum of cellvoltages = battery Voltage(V)
id(npw2500_cell_vmin).publish_state(cmin/1000); // lowest cellvoltage (V)
id(npw2500_cell_vmax).publish_state(cmax/1000); // highest cellvoltage (V)
id(npw2500_cell_vdiff).publish_state((cmax-cmin)/1000); // difference high-low (V)
id(npw2500_cell_vavg).publish_state(cv/14000); // avarage cellvoltage (V)
}
else if((x[3] == 3))
{
ESP_LOGD("npw2500","Parsing response of request command 0x3");
id(npw2500_response_0x03_data_ready) = true;
// Input power ch1
int inputpower1 = x[6] | x[7] << 8;
id(npw2500_input_power_ch1).publish_state(inputpower1);
// Input power ch2
int inputpower2 = x[8] | x[9] << 8;
id(npw2500_input_power_ch2).publish_state(inputpower2);
// Input power ch1 + ch2
id(npw2500_input_power).publish_state(inputpower1 + inputpower2);
// Output power Ch1
int outputpower1 = x[24] | x[25] << 8;
id(npw2500_output_power_ch1).publish_state(outputpower1);
// Output power Ch2
int outputpower2 = x[26] | x[27] << 8;
id(npw2500_output_power_ch2).publish_state(outputpower2);
// Output power Ch1 + Ch2
id(npw2500_output_power).publish_state(outputpower1 + outputpower2);
// Input-Output power
id(npw2500_inout_power).publish_state(inputpower1 + inputpower2 - outputpower1 - outputpower2);
//int dod_level = x[18];
id(npw2500_discharge_treshold).publish_state(x[18]);
//Solar Treshold
id(npw2500_solar_charge_treshold).publish_state(x[19] | x[20] << 8);
// Battery state of charge %
id(npw2500_battery_soc).publish_state((x[10] | x[11] << 8 ) / 10);
// Battery remaining capacity
id(npw2500_battery_remaining).publish_state(x[22] | x[23] << 8);
// update active and transparent state of input channels
if( x[4] == 0x00 ) { id(npw2500_input_ch1_active).publish_state(false); id(npw2500_input_ch1_transparent).publish_state(false); }
if( x[4] == 0x01 ) { id(npw2500_input_ch1_active).publish_state(true); id(npw2500_input_ch1_transparent).publish_state(false); }
if( x[4] == 0x02 ) { id(npw2500_input_ch1_active).publish_state(true); id(npw2500_input_ch1_transparent).publish_state(true); }
if( x[5] == 0x00 ) { id(npw2500_input_ch2_active).publish_state(false); id(npw2500_input_ch2_transparent).publish_state(false); }
if( x[5] == 0x01 ) { id(npw2500_input_ch2_active).publish_state(true); id(npw2500_input_ch2_transparent).publish_state(false); }
if( x[5] == 0x02 ) { id(npw2500_input_ch2_active).publish_state(true); id(npw2500_input_ch2_transparent).publish_state(true); }
//device software version
float dev_version = x[12];
id(npw2500_device_version).publish_state(dev_version / 100);
// Passthrough state
id(npw2500_passthrough_active).publish_state(x[13] == 0);
// update output channels state
if( x[15] == 0x00 ) { id(npw2500_battery_wifi).publish_state(false); id(npw2500_battery_mqtt).publish_state(false); }
if( x[15] == 0x01 ) { id(npw2500_battery_wifi).publish_state(true); id(npw2500_battery_mqtt).publish_state(false); }
if( x[15] == 0x02 ) { id(npw2500_battery_wifi).publish_state(true); id(npw2500_battery_mqtt).publish_state(true); }
if( x[16] == 0x00 ) { id(npw2500_output_ch1_active).publish_state(false);}
if( x[16] == 0x01 ) { id(npw2500_output_ch1_active).publish_state(true); }
if( x[17] == 0x00 ) { id(npw2500_output_ch2_active).publish_state(false);}
if( x[17] == 0x01 ) { id(npw2500_output_ch2_active).publish_state(true); }
}
else
{
ESP_LOG_BUFFER_HEXDUMP("npw2500", &x[0], x.size(), ESP_LOG_ERROR);
}
- id: ble_switch_powerout
then:
- lambda: |-
if(id(npw2500_communication_ready))
{
int ble_cmd_t = 0x00;
// Update switches when communication is ready
if ( ! id(npw2500_powerout_switch_ch1).state && ! id(npw2500_powerout_switch_ch2).state ) { ble_cmd_t = 0x00; }
if ( id(npw2500_powerout_switch_ch1).state && ! id(npw2500_powerout_switch_ch2).state ) { ble_cmd_t = 0x01; }
if ( ! id(npw2500_powerout_switch_ch1).state && id(npw2500_powerout_switch_ch2).state ) { ble_cmd_t = 0x02; }
if ( id(npw2500_powerout_switch_ch1).state && id(npw2500_powerout_switch_ch2).state ) { ble_cmd_t = 0x03; }
id(ble_command).execute(0x0E, ble_cmd_t);
ESP_LOGD("npw2500","npw2500_powerout_switch cmd = %d ", ble_cmd_t);
}
else
ESP_LOGD("npw2500","ble_switch_powerout Communication not ready");
# Script for sending ble commands to battery
- id: ble_command
parameters:
ble_cmd: int
ble_cmd_parm: int
then:
- if:
condition:
lambda: 'return id(npw2500_communication_ready);'
then:
- lambda: 'ESP_LOGD("NPW2500","ble_command cmd = %d parm = %d" ,ble_cmd, ble_cmd_parm); '
- ble_client.ble_write:
id: npw2500_ble
service_uuid: 'ff00'
characteristic_uuid: 'ff01'
value: !lambda |-
int rlen = 0;
int rxor = 0;
std::vector<unsigned char> rdat1{ 0x73,0x06,0x23,(unsigned char)ble_cmd};
if (ble_cmd == 0x0C) {
rdat1.push_back((uint8_t)((ble_cmd_parm >> 0) & 0xFF));
rdat1.push_back((uint8_t)((ble_cmd_parm >> 8) & 0xFF));
} else {
rdat1.push_back((unsigned char)ble_cmd_parm);
}
rlen = rdat1.size();
rdat1.at(1) = rlen+1;
for (int i=0;i<rlen;i++) {
rxor = rxor ^ rdat1[i];
}
rdat1.push_back(rxor);
return rdat1;
else:
- lambda: 'ESP_LOGE("NPW2500","ble_command Communication not ready"); '
#####################################################################
### PowerZero Nulleinspeisung - by neromatrix ###
### Ver. 0.2 ###
## Now includes ChargeControl for ###
### loadvoltage preregulation ###
#####################################################################
- id: power_zero
then:
- lambda: |-
if(id(npw2500_zeropower_enabled).state && id(npw2500_communication_ready))
{
int dtu_limit_min_value = 2;
int dtu_limit_max_value = id(npw2500_powerlimit_slider).state;
int dtu_limit_calculated_value = dtu_limit_min_value;
int dtu_max_power = 800;
int grid_to_dtu_limit_npr_ratio = dtu_max_power / 100;
int grid_power_offset_target_value = id(npw2500_power_offset_slider).state;
std::string powerzero_status = "";
static int grid_power_offset_old_value = id(npw2500_power_offset_slider).state;
static int dtu_old_limit_value = dtu_limit_min_value;
static int grid_old_power_value = 100;
// ************************* NPW ChargeControl ***********************************
// Inputs
float cell_voltage_target_value = id(npw2500_max_cell_voltage_slider).state;
float cell_vmax_act_value = id(npw2500_cell_vavg).state;
float cell_voltage_diff_to_power_ratio = 8000; // cell voltage difference to grid power ratio factor
// factor 8000 enables soft regulation
// Outputs
int grid_power_offset_act_value = id(npw2500_power_offset_slider).state;
// Local
int grid_power_offset_new_value = id(npw2500_power_offset_slider).state;
float cell_vmax_diff = cell_vmax_act_value - cell_voltage_target_value; // calculate difference
if(id(npw2500_response_0x0f_data_ready)) // sanity check of response 0x0f
{
if((cell_vmax_act_value > cell_voltage_target_value)) // actual cell voltage higher then traget value
{ // calculate grid power offset for PowerZero
grid_power_offset_new_value = grid_power_offset_old_value - cell_vmax_diff * cell_voltage_diff_to_power_ratio;
grid_power_offset_act_value = round(( id(npw2500_power_offset_slider).state + grid_power_offset_new_value)/2);
powerzero_status = "Loadregulation";
}
else
{
if(cell_vmax_act_value < cell_voltage_target_value) // actual cell voltage lower then traget value
{
if(grid_power_offset_act_value != grid_power_offset_target_value)
{ // calculate grid power offset for PowerZero
grid_power_offset_act_value = round(( id(npw2500_power_offset_slider).state - grid_power_offset_old_value)/2);
}
powerzero_status = "Charging";
}
}
// Limit grid_power_offset_act_value to +- dtu_max_power
if(grid_power_offset_act_value > dtu_max_power) grid_power_offset_act_value = dtu_max_power;
if(grid_power_offset_act_value < - dtu_max_power) grid_power_offset_act_value = - dtu_max_power;
grid_power_offset_old_value = grid_power_offset_act_value;
}
else
ESP_LOGD("npw2500", "NPW Charge control - Cell data not available");
// ************************* End NPW ChargeControl *************************
// ************************* NPW PowerZero ***********************************
int grid_actual_power_value = int(id(npw2500_grid_power).state);
// id(npw2500_grid_power_offset).publish_state(grid_power_offset_act_value); // publich value of actual grid power offset
// calculate new dtu limit
dtu_limit_calculated_value = ((grid_actual_power_value - grid_power_offset_act_value ) / grid_to_dtu_limit_npr_ratio ) + dtu_old_limit_value ;
grid_old_power_value = grid_actual_power_value; //save dtu limit
if(dtu_limit_calculated_value > dtu_limit_max_value) dtu_limit_calculated_value = dtu_limit_max_value; // limit dtu_limit upper limit to dtu_max_value
if(dtu_limit_calculated_value < dtu_limit_min_value) dtu_limit_calculated_value = dtu_limit_min_value; // limit dtu_limit lower limit to dtu_min_value
ESP_LOGD("npw2500","PowerZero dtu old limit %d, dtu new limit %d, Grid value %d ",
dtu_old_limit_value, dtu_limit_calculated_value, grid_actual_power_value);
if(id(npw2500_input_ch1_transparent).state && id(npw2500_input_ch2_transparent).state) // check for 2 channel passthrough
{
dtu_limit_calculated_value = 100; // set dtu relativ power limit 100%
powerzero_status = "2 Channel Passthrough"; // report status
grid_power_offset_act_value = 0; // no function in passthrough
}
else
{
if(id(npw2500_input_ch1_transparent).state || id(npw2500_input_ch2_transparent).state) // check for 1 channel passthrough
{
dtu_limit_calculated_value = 100; // set dtu relativ power limit 100%
powerzero_status = "1 Channel Passthrough"; // report status
grid_power_offset_act_value = 0; // set grid_power_offset_act_value,
} // no function in passthrough
}
id(npw2500_grid_power_offset).publish_state(grid_power_offset_act_value); // publish value of actual grid power offset
if(id(npw2500_powerzero_inverter_producing).state)
{
// cannot use id(npw2500_hm_800_limit_nonpersistent_relative).publish_state(dtu_limit)
// any longer because entity id is a string now from !secret inverter_rel_power_limit
//**** update HA dtu limit value *****/
HomeassistantServiceResponse resp;
HomeassistantServiceMap entity_id_kv;
resp.service = "number.set_value";
entity_id_kv.key = "entity_id";
entity_id_kv.value = id(npw2500_config_limit_nonpersistent_relative);
resp.data.push_back(entity_id_kv);
entity_id_kv.key = "value";
entity_id_kv.value = to_string(dtu_limit_calculated_value);
resp.data.push_back(entity_id_kv);
id(opendtu_limit_nonpersistent_relative_target_value).publish_state(dtu_limit_calculated_value);
ESP_LOGD("npw2500", "Power Zero dtu limit calculated %d ",dtu_limit_calculated_value);
id(api_server).send_homeassistant_service_call(resp);
//*************************************
}
// Report internal PowerZero status
if( !id(npw2500_powerzero_inverter_producing).state)
{
if(id(npw2500_input_ch1_transparent).state && id(npw2500_input_ch2_transparent).state)
powerzero_status = "PT active, waitung for OpenDTU";
else
powerzero_status = "Inverter not producing";
}
else
if(!(id(npw2500_input_ch1_active).state || id(npw2500_input_ch2_active).state))
powerzero_status = "Inputs not active";
id(npw2500_powerzero_status).publish_state(powerzero_status);
id(npw2500_home_power_consuption).publish_state(id(npw2500_output_power).state + grid_actual_power_value );
dtu_old_limit_value = dtu_limit_calculated_value; // save calculated dtu limit
}
else
{
ESP_LOGD("npw2500", "PowerZero - Communication not ready");
id(npw2500_response_0x03_data_ready) = 0;
id(npw2500_response_0x0f_data_ready) = 0;
}
- id: watch_task
then:
lambda: |-
if(id(npw2500_communication_ready))
{
ESP_LOGD("npw2500", "Watch Task active");
// Bug - Battery Discharge randomly 100%
if(id(npw2500_response_0x03_data_ready))
{
int discharge_state = int(id(npw2500_discharge_slider).state);
if(id(npw2500_discharge_treshold).state != discharge_state) // check for difference between slider value and battery value
{
id(ble_command)->execute(0x0B,discharge_state); // send slidervalue to battery
ESP_LOGE("npw2500", "Watch Task - DOD corrected");
}
}
// Bug - Outputs not always turned on correct after 2 channel Passthrough
// Work in progress.....
// if( !id(npw2500_input_ch1_active).state &&
// !id(npw2500_input_ch1_active).state &&
// !id(npw2500_input_ch1_transparent).state &&
// !id(npw2500_input_ch2_transparent).state &&
// !id(npw2500_powerzero_inverter_producing).state &&
// (id(npw2500_output_power_ch1).state == 0) &&
// (id(npw2500_output_power_ch2).state == 0) )
//
// if(id(npw2500_input_ch1_transparent).state || id(npw2500_input_ch2_transparent).state)
// {
// ESP_LOGE("npw2500", "Watch Task - Outputs corrected after 2 channel passthrough");
// id(ble_switch_powerout)->execute();
// }
//
}
### End of NPW2500.yaml ###
NPWDashboard Setup
Nachdem ihr den ESPNode NPW2500 in den HA integriert habt und unter
Settings->Device&Services->Integrations>ESPhome
das device "No Power Wasted 2500" vorhanden ist,
könnt ihr mit der Vorlage NPWDashboard V0.2 das entsprechende Dashboard erzeugen.
Ihr geht folgend vor:
HA-> Settings -> Dasboards
rechts unten auf das blaue Feld + ADD DASHBOARD drücken und
"New Dashboard" from Scratch" auswählen.
Als Name NPW2500 eingeben und "CREATE" drücken.
In der linken HA Dashboard Auswahlliste sollte dieses nun angezeigt werden.
In der Liste auswählen und öffnen.
Es sollte sich nun eine leeres Dashboard mit dem Namen NPW2500 öffnen.
Rechts oben in der blauen Leiste die 3 senkrechten Punkte clicken und "Edit Dashboard" auswählen .
Nun in der grauen Leiste oben wieder die 3 Punkte anklicken und "RAW CONFIGURATION EDITOR" auswählen.
Es öffnet sich nun ein Editierfenster in das ihr den Inhalt von NPWDashboard V0.2 kopiert. (Die ersten 2 vorgegebenen Zeilen überschreiben)
Wenn ihr fertig seid rechts oben "Save drücken" und den Editor mit dem x links oben verlassen.
Nun sollten die Panels im Dashboard angezeigt werden und oben rechts auf "Done" drücken.
views:
- title: Home
cards:
- type: entities
entities:
- entity: number.esphome_npw2500_set_discharge_threshold
name: Set Discharge threshold
- entity: sensor.esphome_npw2500_discharge_threshold
name: Discharge threshold
- entity: switch.esphome_npw2500_set_power_out_switch_ch1
name: Set Power Out Switch Ch1
- entity: binary_sensor.esphome_npw2500_ouput_ch1_active
name: Ouput Ch1 active
- entity: switch.esphome_npw2500_set_power_out_switch_ch2
name: Set Power Out Switch Ch2
- entity: binary_sensor.esphome_npw2500_ouput_ch2_active
name: Ouput Ch2 active
- entity: switch.esphome_npw2500_set_pv2_passtrough_switch
name: Set PV2 Passtrough Switch
- entity: binary_sensor.esphome_npw2500_passthrough_active
name: PassThrough active
- entity: number.esphome_npw2500_set_solar_charge_threshold
name: Set Solar Charge threshold
- entity: sensor.esphome_npw2500_solar_charge_threshold
name: Solar Charge threshold
title: No Power Wasted 2500 V0.2
show_header_toggle: false
- type: entities
entities:
- entity: switch.esphome_npw2500_powerzero_enabled
name: PowerZero enabled
- entity: sensor.esphome_npw2500_powerzero_status
name: PowerZero Status
- entity: number.esphome_npw2500_set_power_limit_rel
name: Set Power Limit maximum
- entity: sensor.esphome_npw2500_opendtu_power_limit_rel_target_value
name: openDTU Power Limit target value
- entity: sensor.esphome_npw2500_opendtu_power_limit_rel_actual_value
name: openDTU Power Limit actual value
- entity: number.esphome_npw2500_set_max_cell_voltage
name: Set max Cell Voltage
- entity: sensor.esphome_npw2500_cell_voltage_max
name: Cell Voltage actual
- entity: number.esphome_npw2500_set_grid_power_offset
name: Set Grid Power Offset
- entity: sensor.esphome_npw2500_grid_power_offset
name: Grid Power Offset
- entity: sensor.esphome_npw2500_grid_power
name: Grid Power
- entity: binary_sensor.esphome_npw2500_powerzero_inverter_producing
name: Inverter producing
title: NPW PowerZero - Nulleinspeisung
show_header_toggle: false
- type: entities
entities:
- entity: sensor.esphome_npw2500_input_power
name: Battery Solar Input Power
- entity: sensor.esphome_npw2500_output_power
name: Battery DTU Output Power
- entity: sensor.esphome_npw2500_power_inout
name: Battery Power
- entity: sensor.esphome_npw2500_grid_power
name: Grid Power
- entity: sensor.esphome_npw2500_home_power_consumption
name: Home Power Consumption
title: NPW Solar
- type: entities
entities:
- entity: sensor.esphome_npw2500_battery_remaining_capacity
name: Battery remaining capacity
- entity: sensor.esphome_npw2500_battery_soc
name: Battery SOC
- entity: sensor.esphome_npw2500_battery_soc_dynamic
name: Battery SOC dynamic
- entity: binary_sensor.esphome_npw2500_battery_wifi_connected
name: Battery WiFi connected
- entity: binary_sensor.esphome_npw2500_esp_ble_connected
name: ESP Ble connected
- entity: binary_sensor.esphome_npw2500_esp_ha_connected
name: ESP HA connected
- entity: binary_sensor.esphome_npw2500_esp_wifi_connected
name: ESP Wifi connected
- entity: sensor.esphome_npw2500_temperature_sensor_1
name: Temperature Sensor 1
- entity: sensor.esphome_npw2500_temperature_sensor_2
name: Temperature Sensor 2
- entity: sensor.esphome_npw2500_battery_version
name: Battery Version
title: NPW Battery
show_header_toggle: false
- type: entities
entities:
- entity: sensor.esphome_npw2500_input_power
name: Input Power
- entity: sensor.esphome_npw2500_input_ch1_power
name: Input Ch1 Power
- entity: binary_sensor.esphome_npw2500_input_ch1_active
name: Input Ch1 active
- entity: binary_sensor.esphome_npw2500_input_ch1_transparent
name: Input Ch1 transparent
- entity: sensor.esphome_npw2500_input_ch2_power
name: Input Ch2 Power
- entity: binary_sensor.esphome_npw2500_input_ch2_active
name: Input Ch2 active
- entity: binary_sensor.esphome_npw2500_input_ch2_transparent
name: Input Ch2 transparent
title: NPW Inputs
show_header_toggle: false
- type: entities
entities:
- entity: sensor.esphome_npw2500_output_power
name: Output Power
- entity: sensor.esphome_npw2500_output_power_ch1
name: Output Power Ch1
- entity: binary_sensor.esphome_npw2500_ouput_ch1_active
name: Ouput Ch1 active
- entity: sensor.esphome_npw2500_output_power_ch2
name: Output Power Ch2
- entity: binary_sensor.esphome_npw2500_ouput_ch2_active
name: Ouput Ch2 active
title: NPW Outputs
show_header_toggle: false
- type: entities
entities:
- entity: sensor.esphome_npw2500_cell_voltage_avg
name: Cell Voltage avg
- entity: sensor.esphome_npw2500_cell_voltage_diff
name: Cell Voltage diff
- entity: sensor.esphome_npw2500_cell_voltage_max
name: Cell Voltage max
- entity: sensor.esphome_npw2500_cell_voltage_min
name: Cell Voltage min
- entity: sensor.esphome_npw2500_cell_voltage_sum
name: Cell Voltage sum
title: NPW Battery Cells
show_header_toggle: false
# sectrets.yaml
# Wi-Fi SSID and password
# use your ssid and password
wifi_ssid: "ssid"
wifi_password: "password"
# mac address of the batterie, you find it in in the App
battery_ble_mac: "e8:8d:a6:56:xx:xx"
# HA Sensor of your actual grid power
# sensor.grid_power is the id of my entity, enter your id
# "sensor.xyz..."
npw2500_grid_power: "sensor.grid_power"
# HA Sensor of your OpenDTU relativ power limit
# number.hm_800_limit_nonpersistent_relative is the id of my entity, enter your id
# '"number.xyz..."'
inverter_rel_power_limit: '"number.hm_800_limit_nonpersistent_relative"'
# "number.xyz..."
inverter_rel_power_limit_2: "number.hm_800_limit_nonpersistent_relative"
# HA Sensor of your OpenDTU inverter producing
# binary_sensor.hm_800_producing is the id of my entity, use your id
# "binary_sensor.xyz..."
inverter_producing: "binary_sensor.hm_800_producing"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment