|
# No Power Wasted 2500 V0.322 ONLY FOR TESTING !!!! |
|
# 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 |
|
on_boot: |
|
priority: 600 |
|
then: |
|
- lambda: |- |
|
id(npw2500_ble_disconnects).publish_state(0); |
|
id(npw2500_wifi_disconnects).publish_state(0); |
|
id(npw2500_api_disconnects).publish_state(0); |
|
id(npw2500_battery_wifi_disconnects).publish_state(0); |
|
id(npw2500_battery_control_status).publish_state(""); |
|
id(npw2500_command_sent_count).publish_state(0); |
|
|
|
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: |
|
# - <<: !include developer/globals.yaml |
|
# 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' |
|
# state for output ch1 |
|
- id: npw2500_set_output_state_ch1 |
|
type: bool |
|
restore_value: no |
|
initial_value: '0' |
|
# state for output ch2 |
|
- id: npw2500_set_output_state_ch2 |
|
type: bool |
|
restore_value: no |
|
initial_value: '0' |
|
# flag for pending output update |
|
- id: npw2500_update_output_state |
|
type: bool |
|
restore_value: no |
|
initial_value: 'false' |
|
# output update enabled |
|
- id: npw2500_enable_output_control |
|
type: bool |
|
restore_value: no |
|
initial_value: 'false' |
|
# use gridsensor for home consumption |
|
- id: npw2500_enable_gridsensor |
|
type: bool |
|
restore_value: no |
|
initial_value: 'true' #set to false, if you have no valid grid sensor in secrets.yaml |
|
|
|
########## text_sensors ########## |
|
text_sensor: |
|
# - <<: !include developer/text_sensor.yaml |
|
# 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 |
|
# reports the Battery Control Status |
|
- platform: template |
|
id: npw2500_battery_control_status |
|
name: PowerZero Battery Control Status |
|
|
|
########## binary_sensors ########## |
|
binary_sensor: |
|
# - <<: !include developer/binary_sensor.yaml |
|
# Bluetooth connected |
|
- platform: template |
|
name: ESP Ble connected |
|
id: npw2500_ble_connected |
|
on_state: |
|
- lambda: |- |
|
static bool ble_first_started = true; |
|
if(!x && !ble_first_started) id(npw2500_ble_disconnects).publish_state(id(npw2500_ble_disconnects).state + 1); |
|
else ble_first_started = false; |
|
# Wifi connected |
|
- platform: template |
|
name: ESP Wifi connected |
|
id: npw2500_wifi_connected |
|
on_state: |
|
- lambda: |- |
|
static bool wifi_first_started = true; |
|
if(!x && !wifi_first_started) id(npw2500_wifi_disconnects).publish_state(id(npw2500_wifi_disconnects).state + 1); |
|
else wifi_first_started = false; |
|
# HA Api connected |
|
- platform: template |
|
name: ESP HA connected |
|
id: npw2500_api_connected |
|
on_state: |
|
- lambda: |- |
|
static bool api_first_started = true; |
|
if(!x && !api_first_started) id(npw2500_api_disconnects).publish_state(id(npw2500_api_disconnects).state + 1); |
|
else api_first_started = false; |
|
# Battery connected to Wifi |
|
- platform: template |
|
name: Battery WiFi connected |
|
id: npw2500_battery_wifi |
|
on_state: |
|
- lambda: |- |
|
if(!x) id(npw2500_battery_wifi_disconnects).publish_state(id(npw2500_battery_wifi_disconnects).state + 1); |
|
# 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: Output Ch1 active |
|
id: npw2500_output_ch1_active |
|
# Output Ch1 power out flow |
|
- platform: template |
|
name: Output Ch1 Power Flow |
|
id: npw2500_output_ch1_power_flow |
|
# Output Ch2 power out flow |
|
- platform: template |
|
name: Output Ch2 Power Flow |
|
id: npw2500_output_ch2_power_flow |
|
# Output Ch2 active - does not show status in Passthrough Modus |
|
- platform: template |
|
name: Output Ch2 active |
|
id: npw2500_output_ch2_active |
|
# Battery connected to MQTT |
|
- platform: template |
|
name: Battery MQTT connected |
|
id: npw2500_battery_mqtt |
|
# 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: |
|
# - <<: !include developer/sensor.yaml |
|
- platform: template |
|
name: Command sent count |
|
id: npw2500_command_sent_count |
|
internal: true |
|
accuracy_decimals: 0 |
|
# Battery Input Power (Ch1 + Ch2) |
|
- platform: template |
|
name: Input Power |
|
id: npw2500_input_power |
|
unit_of_measurement: W |
|
device_class: power |
|
icon: mdi:solar-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: Wh |
|
device_class: energy |
|
accuracy_decimals: 0 |
|
# Grid Power Import |
|
- platform: template |
|
name: Grid Power Import |
|
id: npw2500_grid_power_import |
|
unit_of_measurement: W |
|
device_class: power |
|
accuracy_decimals: 0 |
|
internal: true |
|
# Grid Power Export |
|
- platform: template |
|
name: Grid Power Export |
|
id: npw2500_grid_power_export |
|
unit_of_measurement: 'W' |
|
device_class: power |
|
accuracy_decimals: 0 |
|
internal: true |
|
# Grid Power Export |
|
- platform: template |
|
name: Grid Power Average Export |
|
id: npw2500_grid_power_avg_export |
|
unit_of_measurement: 'W' |
|
device_class: power |
|
accuracy_decimals: 0 |
|
internal: false |
|
filters: |
|
- sliding_window_moving_average: |
|
window_size: 35 |
|
send_every: 1 |
|
# 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 |
|
icon: mdi:transmission-tower |
|
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_factor |
|
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_factor |
|
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 |
|
#Ble disconnects |
|
- platform: template |
|
name: ESP Ble disconnects |
|
id: npw2500_ble_disconnects |
|
accuracy_decimals: 0 |
|
# Wifi disconnects |
|
- platform: template |
|
name: ESP Wifi disconnects |
|
id: npw2500_wifi_disconnects |
|
accuracy_decimals: 0 |
|
# HA Api disconnects |
|
- platform: template |
|
name: ESP HA disconnects |
|
id: npw2500_api_disconnects |
|
accuracy_decimals: 0 |
|
# Battery disconnects to Wifi |
|
- platform: template |
|
name: Battery WiFi disconnects |
|
id: npw2500_battery_wifi_disconnects |
|
accuracy_decimals: 0 |
|
# Battery Ouput Energy Ch1 |
|
- platform: integration |
|
name: Output Ch1 Energy Daily |
|
id: npw2500_output_energy_ch1_daily |
|
sensor: npw2500_output_power_ch1 |
|
unit_of_measurement: 'kWh' |
|
state_class: total_increasing |
|
device_class: energy |
|
time_unit: h |
|
accuracy_decimals: 3 |
|
integration_method: left |
|
restore: false |
|
filters: |
|
- multiply: 0.001 |
|
- skip_initial: 1 |
|
- lambda: "return id(npw2500_communication_ready) ? x : 0;" |
|
# Battery Ouput Energy Ch2 |
|
- platform: integration |
|
name: Output Ch2 Energy Daily |
|
id: npw2500_output_energy_ch2_daily |
|
sensor: npw2500_output_power_ch2 |
|
unit_of_measurement: 'kWh' |
|
state_class: total_increasing |
|
device_class: energy |
|
time_unit: h |
|
accuracy_decimals: 3 |
|
integration_method: left |
|
restore: false |
|
filters: |
|
- multiply: 0.001 |
|
- skip_initial: 3 |
|
- lambda: "return id(npw2500_communication_ready) ? x : 0;" |
|
# Battery Output Energy (Ch1 + Ch2) |
|
- platform: integration |
|
name: Output Energy Daily |
|
id: npw2500_output_energy_daily |
|
sensor: npw2500_output_power |
|
unit_of_measurement: 'kWh' |
|
state_class: total_increasing |
|
device_class: energy |
|
time_unit: h |
|
accuracy_decimals: 3 |
|
integration_method: left |
|
restore: false |
|
filters: |
|
- multiply: 0.001 |
|
- skip_initial: 1 |
|
- lambda: "return id(npw2500_communication_ready) ? x : 0;" |
|
# Battery Input Energy Ch1 |
|
- platform: integration |
|
name: Input Ch2 Energy Daily |
|
id: npw2500_input_energy_ch2_daily |
|
sensor: npw2500_input_power_ch2 |
|
unit_of_measurement: 'kWh' |
|
state_class: total_increasing |
|
device_class: energy |
|
time_unit: h |
|
accuracy_decimals: 3 |
|
integration_method: left |
|
restore: false |
|
filters: |
|
- multiply: 0.001 |
|
- skip_initial: 1 |
|
- lambda: "return id(npw2500_communication_ready) ? x : 0;" |
|
# Battery Input Energy Ch1 |
|
- platform: integration |
|
name: Input Ch1 Energy Daily |
|
id: npw2500_input_energy_ch1_daily |
|
sensor: npw2500_input_power_ch1 |
|
unit_of_measurement: 'kWh' |
|
state_class: total_increasing |
|
device_class: energy |
|
time_unit: h |
|
accuracy_decimals: 3 |
|
integration_method: left |
|
restore: false |
|
filters: |
|
- multiply: 0.001 |
|
- skip_initial: 1 |
|
- lambda: "return id(npw2500_communication_ready) ? x : 0;" |
|
# Battery Input Energy (Ch1 + Ch2) |
|
- platform: integration |
|
name: Input Energy Daily |
|
id: npw2500_input_energy_daily |
|
sensor: npw2500_input_power |
|
unit_of_measurement: 'kWh' |
|
state_class: total_increasing |
|
device_class: energy |
|
time_unit: h |
|
accuracy_decimals: 3 |
|
integration_method: left |
|
restore: false |
|
filters: |
|
- multiply: 0.001 |
|
- skip_initial: 1 |
|
- lambda: "return id(npw2500_communication_ready) ? x : 0;" |
|
# Battery Energy |
|
- platform: integration |
|
name: Battery InOut Energy Daily |
|
id: npw2500_inout_energy_daily |
|
sensor: npw2500_inout_power |
|
unit_of_measurement: 'kWh' |
|
state_class: total |
|
device_class: energy |
|
time_unit: h |
|
accuracy_decimals: 3 |
|
integration_method: left |
|
restore: false |
|
filters: |
|
- multiply: 0.001 |
|
- skip_initial: 1 |
|
- lambda: "return id(npw2500_communication_ready) ? x : 0;" |
|
# Home Energy Consumption Daily |
|
- platform: integration |
|
name: Home Energy Consumption Daily |
|
id: npw2500_home_energy_daily |
|
sensor: npw2500_home_power_consuption |
|
unit_of_measurement: kWh |
|
state_class: total_increasing |
|
device_class: energy |
|
time_unit: h |
|
accuracy_decimals: 3 |
|
integration_method: left |
|
restore: false |
|
filters: |
|
- multiply: 0.001 |
|
- skip_initial: 1 |
|
- lambda: "return id(npw2500_communication_ready) ? x : 0;" |
|
# Grid Energy Daily |
|
- platform: integration |
|
name: Grid Energy Daily |
|
id: npw_grid_energy_daily |
|
sensor: npw2500_grid_power |
|
unit_of_measurement: kWh |
|
state_class: total |
|
device_class: energy |
|
time_unit: h |
|
accuracy_decimals: 3 |
|
icon: mdi:transmission-tower |
|
integration_method: left |
|
restore: false |
|
filters: |
|
- multiply: 0.001 |
|
- skip_initial: 1 |
|
- lambda: "return id(npw2500_communication_ready) ? x : 0;" |
|
# Grid Export Energy Daily |
|
- platform: integration |
|
name: Grid Export Energy Daily |
|
id: npw_grid_export_energy_daily |
|
sensor: npw2500_grid_power_export |
|
unit_of_measurement: 'kWh' |
|
state_class: total_increasing |
|
device_class: energy |
|
time_unit: h |
|
accuracy_decimals: 3 |
|
icon: mdi:transmission-tower-import |
|
integration_method: left |
|
restore: false |
|
filters: |
|
- multiply: 0.001 |
|
- skip_initial: 1 |
|
- lambda: "return id(npw2500_communication_ready) ? x : 0;" |
|
# Grid Import Energy Daily |
|
- platform: integration |
|
name: Grid Import Energy Daily |
|
id: npw_grid_import_energy_daily |
|
sensor: npw2500_grid_power_import |
|
unit_of_measurement: 'kWh' |
|
state_class: total_increasing |
|
device_class: energy |
|
time_unit: h |
|
icon: mdi:transmission-tower-export |
|
accuracy_decimals: 3 |
|
integration_method: left |
|
restore: false |
|
filters: |
|
- multiply: 0.001 |
|
- skip_initial: 1 |
|
- lambda: "return id(npw2500_communication_ready) ? x : 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]; |
|
|
|
########## button ########## |
|
button: |
|
# - <<: !include developer/button.yaml |
|
# Reset Daily Energy |
|
- platform: template |
|
name: Reset Daily Energy |
|
id: npw2500_reset_daily_energy |
|
on_press: |
|
- script.execute: |
|
id: reset_energy_sensors |
|
# Restart Inverter |
|
- platform: template |
|
name: Restart Inverter |
|
id: npw2500_restart_inverter |
|
icon: mdi:restart |
|
on_press: |
|
- logger.log: Restart Inverter Button Pressed |
|
- script.execute: |
|
id: restart_inverter |
|
# Restart ESP |
|
- platform: restart |
|
name: Restart ESP |
|
id: npw2500_restart_esp |
|
|
|
# Restart Batterie |
|
- platform: template |
|
name: Restart Battery |
|
id: npw2500_restart_battery |
|
icon: mdi:restart |
|
on_press: |
|
- logger.log: Restart Battery Button Pressed |
|
- script.execute: |
|
id: restart_battery |
|
|
|
########## switches ########## |
|
switch: |
|
# - <<: !include developer/switch1.yaml |
|
# - <<: !include developer/switch2.yaml |
|
# - <<: !include developer/switch3.yaml |
|
# - <<: !include developer/switch4.yaml |
|
# Battery Enable Battery Control |
|
- platform: template |
|
name: Enable Battery Control |
|
id: npw2500_enable_battery_control_switch |
|
icon: mdi:toggle-switch |
|
optimistic: True |
|
restore_mode: RESTORE_DEFAULT_OFF |
|
on_turn_on: |
|
then: |
|
lambda: 'id(npw2500_battery_control_status).publish_state("Battery Control enabled");' |
|
on_turn_off: |
|
then: |
|
lambda: 'id(npw2500_battery_control_status).publish_state("Battery Control disabled");' |
|
# 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: False |
|
|
|
########## numbers ########## |
|
number: |
|
# - <<: !include developer/number.yaml |
|
# 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: |
|
- 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: 990 |
|
step: 10 |
|
initial_value : 990 |
|
restore_value : true |
|
optimistic: true |
|
unit_of_measurement: 'W' |
|
device_class: 'energy' |
|
icon: 'mdi:speedometer' |
|
on_value: |
|
- 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.4 |
|
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: -800 |
|
max_value: 200 |
|
step: 10 |
|
initial_value: 0 |
|
restore_value: true |
|
optimistic: true |
|
unit_of_measurement: 'W' |
|
device_class: 'power' |
|
icon: 'mdi:speedometer' |
|
|
|
# - platform: homeassistant |
|
# id: homeassistant_time |
|
### synchronize ESP time with SNTP time |
|
time: |
|
- platform: sntp |
|
id: sntp_time |
|
timezone: Europe/Berlin |
|
on_time_sync: |
|
then: |
|
- logger.log: "Synchronized system clock" |
|
on_time: |
|
- hours: 23 |
|
minutes: 59 |
|
seconds: 0 |
|
then: |
|
- script.execute: |
|
id: reset_energy_sensors |
|
- logger.log: "Energy Sensors Reset" |
|
|
|
########## intervals ########## |
|
interval: |
|
# - <<: !include developer/interval.yaml |
|
- 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: 1 sec |
|
- script.execute: |
|
id: ble_command |
|
ble_cmd: 0x0f # Request Command 0x0f |
|
ble_cmd_parm: 0x01 |
|
- logger.log: "Request command 0x0f sent" |
|
- delay: 1 sec |
|
- script.execute: |
|
id: ble_set_outputs # Set Outputs |
|
|
|
|
|
- interval: 10 sec # do not change time !!! |
|
then: |
|
- if: |
|
condition: |
|
lambda: 'return id(npw2500_communication_ready);' |
|
then: |
|
- script.execute: |
|
id: power_zero # Call script powerzero |
|
|
|
- interval: 10 sec # do not change time !!! |
|
then: |
|
- script.execute: |
|
id: watch_task # Call script watch_task |
|
|
|
- interval: 600 sec |
|
then: |
|
- script.execute: |
|
id: check_dod # # Call script check_dod |
|
|
|
|
|
script: |
|
#- <<: !include developer/script1.yaml |
|
#- <<: !include developer/script2.yaml |
|
- 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 |
|
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 stateS |
|
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[15] == 0x03 ) { id(npw2500_battery_wifi).publish_state(true); id(npw2500_battery_mqtt).publish_state(false); } |
|
|
|
ESP_LOGD("npw2500","wifi state = %d ", x[15]); |
|
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); |
|
} |
|
|
|
# set ouputs synchronized with other ble commands |
|
- id: ble_set_outputs |
|
then: |
|
lambda: |- |
|
int act_ble_cmd =0; |
|
if( id(npw2500_enable_output_control) && id(npw2500_enable_battery_control_switch).state && |
|
( (id(npw2500_set_output_state_ch1) != id(npw2500_output_ch1_active).state ) || |
|
(id(npw2500_set_output_state_ch2) != id(npw2500_output_ch2_active).state)) && id(npw2500_update_output_state)) |
|
{ |
|
if ( ! id(npw2500_set_output_state_ch1) && ! id(npw2500_set_output_state_ch2)) { act_ble_cmd = 0x00; } |
|
if ( id(npw2500_set_output_state_ch1) && ! id(npw2500_set_output_state_ch2)) { act_ble_cmd = 0x01; } |
|
if ( ! id(npw2500_set_output_state_ch1) && id(npw2500_set_output_state_ch2)) { act_ble_cmd = 0x02; } |
|
if ( id(npw2500_set_output_state_ch1) && id(npw2500_set_output_state_ch2)) { act_ble_cmd = 0x03; } |
|
if(act_ble_cmd !=0) |
|
{ // do not allow setting both outputs to zero |
|
id(ble_command).execute(0x0E, act_ble_cmd); // synchronized with other automatic sent ble commands |
|
ESP_LOGD("npw2500","ble_switch_out_set_state output changed"); |
|
} |
|
id(npw2500_update_output_state) = false; // reset global update flag |
|
ESP_LOGD("npw2500","npw2500_powerout_switch cmd = %d ", act_ble_cmd); |
|
} |
|
|
|
# get the position of the output switches and calculate the ble_cmd to be sent |
|
# for updating the battery switch states. |
|
- id: ble_switch_powerout |
|
then: |
|
- lambda: |- |
|
if(id(npw2500_communication_ready)) |
|
{ |
|
int ble_cmd = 0x00; |
|
if ( ! id(npw2500_powerout_switch_ch1).state && ! id(npw2500_powerout_switch_ch2).state ) { ble_cmd = 0x00; } |
|
if ( id(npw2500_powerout_switch_ch1).state && ! id(npw2500_powerout_switch_ch2).state ) { ble_cmd = 0x01; } |
|
if ( ! id(npw2500_powerout_switch_ch1).state && id(npw2500_powerout_switch_ch2).state ) { ble_cmd = 0x02; } |
|
if ( id(npw2500_powerout_switch_ch1).state && id(npw2500_powerout_switch_ch2).state ) { ble_cmd = 0x03; } |
|
id(ble_command).execute(0x0E, ble_cmd); |
|
ESP_LOGD("npw2500","npw2500_powerout_switch cmd = %d ", ble_cmd); |
|
} |
|
else |
|
ESP_LOGD("npw2500","ble_switch_powerout Communication not ready"); |
|
|
|
# Script for sending ble commands to battery |
|
- id: ble_command |
|
#mode: queued |
|
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.3 ### |
|
## Now includes ChargeControl for ### |
|
### loadvoltage preregulation ### |
|
##################################################################### |
|
- id: power_zero |
|
then: |
|
- lambda: |- |
|
if(id(npw2500_zeropower_enabled).state && id(npw2500_communication_ready) && |
|
id(npw2500_response_0x03_data_ready) && id(npw2500_response_0x0f_data_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 / 50; |
|
int grid_power_offset_target_value = id(npw2500_power_offset_slider).state; |
|
int dtu_max_slew_rate = 40; |
|
int send_every_number_of_cycles = 6; |
|
bool use_slewrate_limiter = 0; |
|
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; |
|
static int cycle_counter = 0; |
|
// ************************* NPW ChargeControl *********************************** |
|
// Inputs |
|
float cell_voltage_target_value = id(npw2500_max_cell_voltage_slider).state; |
|
float cell_vmax_act_value = id(npw2500_cell_vmax).state; |
|
float cell_voltage_diff_to_power_ratio = 8000; // cell voltage difference to grid power ratio factor |
|
// Outputs // factor 8000 enables soft regulation |
|
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); |
|
} |
|
if(id(npw2500_inout_power).state > 0) |
|
powerzero_status = "Charge"; |
|
else |
|
powerzero_status = "Discharge"; |
|
} |
|
} |
|
// 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); |
|
//limit max grid power |
|
int power_limit = 50000; // enable max +/-50KW grid power |
|
if(abs(grid_actual_power_value) > power_limit) // also filters out HA sensor status message with value of INT_MAX on start |
|
{ |
|
if(grid_actual_power_value != INT_MAX) |
|
ESP_LOGE("npw2500", "Grid Power out of limit %d > %d" ,int(grid_actual_power_value),power_limit); |
|
grid_actual_power_value = 0; |
|
} |
|
|
|
// 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 ; |
|
|
|
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 |
|
|
|
grid_old_power_value = grid_actual_power_value; //save dtu limit |
|
|
|
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 PT"; // 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 PT"; // 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 |
|
id(npw2500_powerzero_status).publish_state(powerzero_status); |
|
|
|
if(grid_actual_power_value >= 0) |
|
{ |
|
id(npw2500_grid_power_import).publish_state(grid_actual_power_value); |
|
id(npw2500_grid_power_export).publish_state(0); |
|
} |
|
if(grid_actual_power_value <= 0) |
|
{ |
|
id(npw2500_grid_power_export).publish_state(-grid_actual_power_value); |
|
id(npw2500_grid_power_import).publish_state(0); |
|
} |
|
|
|
id(npw2500_home_power_consuption).publish_state(id(npw2500_output_power).state + grid_actual_power_value ); |
|
|
|
// ************************* Slew Rate Limiter ************************* |
|
// Limits ptu limit power changes to dtu_max_slew_rate / cycle |
|
// default limits battery output power changes to 320W /cycle |
|
// ********************************************************************* |
|
if(use_slewrate_limiter) |
|
{ |
|
int dtu_limited_value = dtu_limit_calculated_value - dtu_old_limit_value; |
|
|
|
if (dtu_limited_value > dtu_max_slew_rate) |
|
dtu_limited_value = dtu_max_slew_rate; |
|
if (dtu_limited_value < -dtu_max_slew_rate) |
|
dtu_limited_value = -dtu_max_slew_rate; |
|
|
|
dtu_limit_calculated_value = dtu_old_limit_value + dtu_limited_value; |
|
} |
|
|
|
if( id(npw2500_powerzero_inverter_producing).state ) |
|
{ |
|
if( dtu_old_limit_value != dtu_limit_calculated_value || |
|
id(npw2500_limit_nonpersistent_relative).state != id(opendtu_limit_nonpersistent_relative_target_value).state || cycle_counter == 0) |
|
{ |
|
// 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); |
|
if(cycle_counter == 0) ESP_LOGD("npw2500","Cycle sent"); |
|
id(api_server).send_homeassistant_service_call(resp); |
|
//************************************* |
|
|
|
dtu_old_limit_value = dtu_limit_calculated_value; // save calculated dtu limit |
|
} |
|
} |
|
|
|
if(cycle_counter >= send_every_number_of_cycles) |
|
{ |
|
cycle_counter = 0; |
|
} |
|
else |
|
cycle_counter++; |
|
|
|
// 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"; |
|
} |
|
else |
|
{ |
|
ESP_LOGD("npw2500", "PowerZero - Communication not ready"); |
|
id(npw2500_response_0x03_data_ready) = 0; |
|
id(npw2500_response_0x0f_data_ready) = 0; |
|
} |
|
|
|
// npw switch disabled, but communication ready |
|
if(!id(npw2500_zeropower_enabled).state && id(npw2500_enable_gridsensor) && id(npw2500_communication_ready)) |
|
{ |
|
id(npw2500_home_power_consuption).publish_state(id(npw2500_output_power).state + int(id(npw2500_grid_power).state)); |
|
ESP_LOGD("npw2500", "Home Power Consuption updated"); |
|
} |
|
|
|
- id: reset_energy_sensors |
|
then: |
|
- sensor.integration.reset: npw2500_output_energy_ch1_daily |
|
- sensor.integration.reset: npw2500_output_energy_ch2_daily |
|
- sensor.integration.reset: npw2500_output_energy_daily |
|
- sensor.integration.reset: npw2500_inout_energy_daily |
|
- sensor.integration.reset: npw2500_input_energy_ch1_daily |
|
- sensor.integration.reset: npw2500_input_energy_ch2_daily |
|
- sensor.integration.reset: npw2500_input_energy_daily |
|
- sensor.integration.reset: npw2500_inout_energy_daily |
|
- sensor.integration.reset: npw2500_home_energy_daily |
|
- sensor.integration.reset: npw_grid_energy_daily |
|
- sensor.integration.reset: npw_grid_export_energy_daily |
|
- sensor.integration.reset: npw_grid_import_energy_daily |
|
|
|
- id: restart_inverter |
|
then: |
|
- logger.log: Restart Inverter |
|
- homeassistant.service: |
|
service: button.press |
|
data_template: |
|
entity_id: !secret inverter_restart |
|
|
|
- id: restart_battery |
|
then: |
|
lambda: |- |
|
id(ble_command).execute(0x25, 0x01); |
|
ESP_LOGD("npw2500", "Restart Battery Command sent"); |
|
|
|
- id: check_dod |
|
then: |
|
lambda: |- |
|
// BugFix - Battery Discharge randomly 100% |
|
bool enable_dodfix = true; // set enable_dodfix to false if you dont need bugfix |
|
if(id(npw2500_response_0x03_data_ready) && enable_dodfix) |
|
{ |
|
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"); |
|
} |
|
} |
|
|
|
- id: watch_task |
|
then: |
|
lambda: |- |
|
int batt_out_power_switch_power = 400; // set avarage power breakpoint for switching channels |
|
int min_balancing_value = 50; // if one channel has less power start rebalancing |
|
int max_timecount = 6; // defines the cycle time. One timecount is 10 seconds, 6*10 is one minute, 18*10 is three minutes |
|
|
|
static int timecount = 0; |
|
static int chn = 1; |
|
static int batt_out_power = 0; |
|
static bool rebalancing_flag = false; |
|
static int last_channel_state = 0; |
|
|
|
int batt_out_power_avg = 0; |
|
int batt_out_power_act = 0; |
|
|
|
ESP_LOGD("npw2500", "Watch Task started"); |
|
if(id(npw2500_enable_battery_control_switch).state && id(npw2500_communication_ready)) |
|
{ |
|
//ESP_LOGD("npw2500", "Watch Task - npw2500_enable_battery_control_switch"); |
|
|
|
batt_out_power_act = id(npw2500_output_power).state; // get actual battery output power |
|
if(batt_out_power_act > 1000) batt_out_power_act = 0; // limit check in case of start up |
|
|
|
//id(npw2500_battery_control_status).publish_state("Battery Control enabled"); |
|
ESP_LOGD("npw2500", "Watch Task - timecount = %d",timecount); |
|
if(timecount == 0) |
|
{ |
|
batt_out_power = 0; // reset values when starting new cycle |
|
batt_out_power_avg = 0; |
|
timecount ++; |
|
} |
|
else if (timecount < max_timecount) |
|
{ |
|
batt_out_power += batt_out_power_act; // sum batt_out_power |
|
batt_out_power_avg = batt_out_power / timecount; |
|
ESP_LOGD("npw2500", "Watch Task - timecount avg. power = %d",timecount); |
|
timecount ++; |
|
} |
|
else // we reached max_timecount |
|
{ |
|
batt_out_power += batt_out_power_act; // calculate actual values |
|
batt_out_power_avg = batt_out_power / timecount; |
|
bool switch_ch1_on = id(npw2500_powerout_switch_ch1).state; // get state of channel1 switch |
|
bool switch_ch2_on = id(npw2500_powerout_switch_ch2).state; // get state of channel2 switch |
|
timecount = 0; // reset timecount |
|
|
|
// Enable if only one output switch is set ON and the other one is set OFF and PT switch is OFF |
|
if(((!switch_ch1_on && switch_ch2_on) || (switch_ch1_on && !switch_ch2_on)) && !id(npw2500_powerout_pv2_switch).state) |
|
{ |
|
// check if we are not in pt transparent mode |
|
if(!(id(npw2500_input_ch1_transparent).state || id(npw2500_input_ch2_transparent).state)) |
|
{ |
|
ESP_LOGD("npw2500", "Watch Task - check output power"); |
|
|
|
std::string power_str = ""; |
|
id(npw2500_command_sent_count).publish_state(0); |
|
id(npw2500_enable_output_control) = true; |
|
|
|
// check if the battery output power > output_higher_limit |
|
if(batt_out_power_avg > batt_out_power_switch_power) |
|
{ |
|
// check if rebalancing flag is set and rebalancing conditions are met |
|
if( rebalancing_flag && id(npw2500_output_ch1_active).state && id(npw2500_output_ch2_active).state && |
|
(id(npw2500_output_power_ch1).state <= min_balancing_value || id(npw2500_output_power_ch2).state <= min_balancing_value)) |
|
{ // send restart inverter command |
|
id(restart_inverter)->execute(); |
|
ESP_LOGD("npw2500", "Inverter restarted for rebalancing"); |
|
id(npw2500_command_sent_count).publish_state(4); |
|
rebalancing_flag = false; // reset rebalancing flag |
|
} |
|
else |
|
{ |
|
rebalancing_flag = false; // reset rebalancing flag |
|
} |
|
|
|
// check for channel switch 1->2 |
|
if((last_channel_state == 1) && id(npw2500_output_ch1_active).state && id(npw2500_output_ch2_active).state ) |
|
{ |
|
rebalancing_flag = true; // set rebalancing flag |
|
last_channel_state = 2; // 2 channels active |
|
id(npw2500_battery_control_status).publish_state("Checking for Rebalancing"); |
|
id(npw2500_command_sent_count).publish_state(3); |
|
} |
|
|
|
if(!rebalancing_flag) // if rebalancing flag is set, we are already in 2 channel mode |
|
{ |
|
id(npw2500_set_output_state_ch1) = true; // turn on both outputs |
|
id(npw2500_set_output_state_ch2) = true; |
|
id(npw2500_update_output_state) = true; |
|
id(npw2500_command_sent_count).publish_state(2); |
|
|
|
if(id(npw2500_output_ch1_active).state && id(npw2500_output_ch2_active).state) // if both outputs are on, report 2 Channel mode |
|
{ |
|
power_str = "2 Channel avg. power " + std::to_string(batt_out_power_avg) +" W"; |
|
id(npw2500_battery_control_status).publish_state(power_str); |
|
ESP_LOGD("npw2500",power_str.c_str()); |
|
} |
|
else // we are in switching mode |
|
{ |
|
power_str = "Switching Channels avg. power " + std::to_string(batt_out_power_avg) +" W"; |
|
id(npw2500_battery_control_status).publish_state(power_str); |
|
ESP_LOGD("npw2500",power_str.c_str()); |
|
} |
|
} |
|
} |
|
else // batt_out_power_avg < batt_out_power_switch_power |
|
{ |
|
if(!switch_ch1_on && switch_ch2_on) // switch channel 1 off |
|
{ |
|
id(npw2500_set_output_state_ch1) = false; |
|
id(npw2500_set_output_state_ch2) = true; |
|
id(npw2500_update_output_state) = true; |
|
} |
|
else // switch channel 2 off |
|
{ |
|
id(npw2500_set_output_state_ch1) = true; |
|
id(npw2500_set_output_state_ch2) = false; |
|
id(npw2500_update_output_state) = true; |
|
} |
|
id(npw2500_command_sent_count).publish_state(1); |
|
|
|
if(id(npw2500_output_ch1_active).state && id(npw2500_output_ch2_active).state) // if both outputs are on, report switching mode |
|
{ |
|
power_str = "Switching Channels avg. power " + std::to_string(batt_out_power_avg) +" W"; |
|
id(npw2500_battery_control_status).publish_state(power_str); |
|
ESP_LOGD("npw2500",power_str.c_str()); |
|
} |
|
else |
|
{ |
|
power_str = "1 Channel avg. power " + std::to_string(batt_out_power_avg) +" W"; // report 1 Channel mode |
|
id(npw2500_battery_control_status).publish_state(power_str); |
|
ESP_LOGD("npw2500",power_str.c_str()); |
|
} |
|
|
|
rebalancing_flag = false; |
|
last_channel_state = 1; // set 1 channel state |
|
} |
|
} |
|
else |
|
{ |
|
id(npw2500_battery_control_status).publish_state("PTMode active"); |
|
id(npw2500_enable_output_control) = false; |
|
} |
|
} |
|
else |
|
{ |
|
id(npw2500_battery_control_status).publish_state("Both CH1/Ch2 are ON or OFF or PT ON"); |
|
id(npw2500_enable_output_control) = false; |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
if(!id(npw2500_enable_battery_control_switch).state) |
|
{ |
|
id(npw2500_battery_control_status).publish_state("Battery Control disabled"); |
|
timecount = 0; |
|
rebalancing_flag = false; |
|
last_channel_state = 0; |
|
id(npw2500_enable_output_control) = false; |
|
} |
|
} |
|
|
|
### End of NPW2500.yaml ### |
Hallo @neromatrix, ich würde mich erst "am Anfang" der Reise bezeichnen, aber immerhin habe ich dein Yaml in HA geschrieben / integriert,,, und plötzlich ging es im Dashboard. WAHNSINN!!! Falls es helfen kann: wenn man ein ESP32-C3 verwendet, muss man dementsprechend folgende schreiben (zumindest aber das "board Zeichen"):
esp32:
board: "esp32-c3-devkitm-1"
flash_size: 4MB
framework:
type: esp-idf
sdkconfig_options:
CONFIG_FREERTOS_UNICORE: y
COMPILER_OPTIMIZATION_SIZE: y
den Teil habe ich von Tomquist-Yaml, was leider irgendwie bei mir (mit dem board) noch nicht so geklappt hat
Dazu habe ich einen API Schlüssel in secret.yaml hinzugenommen für das "api_server".
und ganz wichtig aus meiner Erfahrung: wenn man in Raspi und ESPhome arbeitet, soll das blöde ESP32 natürlich im USB des Raspi stecken und nicht im USB des Laptops :-)
Jetzt ist (so spät) mein Akku leer, so dass ich nicht damit spielen kann, aber hoffentlich morgen schon :-)
Viele Grüße,
Gregory