Created
January 10, 2025 18:06
-
-
Save tomquist/53bea769b8db13cf160ac84ac0122dbf to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
alias: "Zero Power Solar Control" | |
description: "Adjusts solar output to achieve zero grid consumption with safety features" | |
# Control parameters for easy tuning | |
variables: | |
# Entity Configuration | |
GRID_POWER_SENSOR: "sensor.lesekopf_sml_power_curr" # Grid power sensor | |
HELPER_LAST_COMMAND: "input_text.last_solar_command" # Helper storing last command | |
HELPER_FALLBACK_POWER: "input_number.solar_fallback_power" # Helper storing fallback power | |
HELPER_LAST_NOTIFICATION: "input_datetime.last_zero_power_notification" # Helper for notification timing | |
# MQTT Configuration | |
MQTT_TOPIC: "hame_energy/HMA-1/App/1234567/ctrl" # Your MQTT topic | |
# Control parameters | |
CONTROL_DEADBAND: 10 # ±W around zero where no adjustment is made | |
CONTROL_PROPORTION: 0.7 # Adjustment factor (0.7 = 70% of measured offset) | |
MIN_ADJUSTMENT: 20 # Minimum power adjustment threshold (W) | |
MAX_RATE_OF_CHANGE: 100 # Maximum change per adjustment (W) | |
SENSOR_TIMEOUT: 30 # Seconds before considering sensor failed | |
trigger: | |
- platform: state | |
entity_id: "{{ variables.GRID_POWER_SENSOR }}" | |
for: "0:00:10" | |
condition: | |
# Only run during daytime (optional, remove if 24/7 operation desired) | |
- condition: time | |
after: "07:00:00" | |
before: "21:00:00" | |
# Sensor value sanity check | |
- condition: template | |
value_template: > | |
{% set power = states(variables.GRID_POWER_SENSOR) | float(-9999) %} | |
{% set age = (now() - states[variables.GRID_POWER_SENSOR].last_updated).total_seconds() %} | |
{{ power > -5000 and power < 5000 and age < variables.SENSOR_TIMEOUT }} | |
variables: | |
# Get current grid power consumption | |
current_grid_power: > | |
{% set power = states(variables.GRID_POWER_SENSOR) | float(default=0) %} | |
{{ power }} | |
# Extract current solar output from stored command with enhanced validation | |
current_solar_output: > | |
{% set stored = states(variables.HELPER_LAST_COMMAND) | default('') %} | |
{% if stored and 'v1=' in stored %} | |
{% set matches = stored | regex_findall('v1=(\d+)') %} | |
{% if matches and matches[0] | int(-1) >= 0 %} | |
{{ matches[0] | int }} | |
{% else %} | |
{% set fallback = states(variables.HELPER_FALLBACK_POWER) | int(0) %} | |
{{ fallback }} | |
{% endif %} | |
{% else %} | |
{% set fallback = states(variables.HELPER_FALLBACK_POWER) | int(0) %} | |
{{ fallback }} | |
{% endif %} | |
# Check if adjustment is needed (implements deadband) | |
needs_adjustment: > | |
{% set grid = states(variables.GRID_POWER_SENSOR) | float(default=0) %} | |
{{ grid < -variables.CONTROL_DEADBAND or grid > variables.CONTROL_DEADBAND }} | |
# Calculate new solar output with rate limiting | |
new_solar_output: > | |
{% set grid = states(variables.GRID_POWER_SENSOR) | float(default=0) %} | |
{% set current_output = variables.current_solar_output | int %} | |
{% if not variables.needs_adjustment %} | |
{# Within deadband - maintain current output #} | |
{{ current_output }} | |
{% else %} | |
{# Calculate adjustment with proportion factor #} | |
{% set raw_adjustment = (grid * variables.CONTROL_PROPORTION) | round(0) %} | |
{# Limit rate of change #} | |
{% set max_change = variables.MAX_RATE_OF_CHANGE %} | |
{% set limited_adjustment = [raw_adjustment, max_change, -max_change] | |
| sort(reverse=false) [1] %} | |
{% set new_value = current_output + limited_adjustment %} | |
{# Clamp between 0 and 800 #} | |
{{ [new_value | round(0), 0, 800] | sort(reverse=false) [1] }} | |
{% endif %} | |
action: | |
# Only act if significant change needed | |
- condition: template | |
value_template: > | |
{% set current = variables.current_solar_output | int %} | |
{% set new = variables.new_solar_output | int %} | |
{% set significant_change = (current - new) | abs >= variables.MIN_ADJUSTMENT %} | |
{{ significant_change and variables.needs_adjustment }} | |
# Comprehensive logging | |
- service: system_log.write | |
data: | |
message: >- | |
Zero Power: Grid={{ variables.current_grid_power }}W, | |
Current Solar={{ variables.current_solar_output }}W, | |
New Solar={{ variables.new_solar_output }}W, | |
Needs Adjustment={{ variables.needs_adjustment }}, | |
Delta={{ (variables.new_solar_output - variables.current_solar_output) }}W | |
level: info | |
# Send MQTT command with error handling | |
- service: mqtt.publish | |
data: | |
topic: "{{ variables.MQTT_TOPIC }}" | |
payload: >- | |
cd=20,md=0,a1=1,b1=0:00,e1=23:59,v1={{ variables.new_solar_output }} | |
response_variable: mqtt_result | |
# Log MQTT failures | |
- if: | |
- condition: template | |
value_template: "{{ not mqtt_result }}" | |
then: | |
- service: system_log.write | |
data: | |
message: "Zero Power: Failed to publish MQTT message!" | |
level: warning | |
# Store last known good value as fallback | |
- service: input_number.set_value | |
target: | |
entity_id: "{{ variables.HELPER_FALLBACK_POWER }}" | |
data: | |
value: "{{ variables.current_solar_output }}" | |
# Store successful command | |
- service: input_text.set_value | |
target: | |
entity_id: "{{ variables.HELPER_LAST_COMMAND }}" | |
data: | |
value: "cd=20,md=0,a1=1,b1=0:00,e1=23:59,v1={{ variables.new_solar_output }}" | |
# Monitor for persistent issues (hourly check) | |
- condition: template | |
value_template: > | |
{% set avg_power = float(states(variables.GRID_POWER_SENSOR)) | float(0) %} | |
{% set last_notification = states(variables.HELPER_LAST_NOTIFICATION) | |
| default('2020-01-01') %} | |
{% set hours_since = (now() - strptime(last_notification, '%Y-%m-%d %H:%M:%S')) | |
.total_seconds() / 3600 %} | |
{{ (avg_power | abs > 50) and hours_since > 1 }} | |
- service: notify.notify | |
data: | |
message: > | |
Zero Power Warning: Grid power has been {{ states(variables.GRID_POWER_SENSOR) }}W | |
for over an hour. Check system status. | |
- service: input_datetime.set_datetime | |
target: | |
entity_id: "{{ variables.HELPER_LAST_NOTIFICATION }}" | |
data: | |
datetime: "{{ now() }}" | |
mode: queued | |
max: 5 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Helper entities for Zero Power Control | |
input_text: | |
last_solar_command: | |
name: Last Solar Command | |
initial: "" | |
max: 255 | |
input_number: | |
solar_fallback_power: | |
name: Solar Fallback Power | |
min: 0 | |
max: 800 | |
step: 1 | |
unit_of_measurement: "W" | |
icon: mdi:solar-power | |
input_datetime: | |
last_zero_power_notification: | |
name: Last Zero Power Notification | |
has_date: true | |
has_time: true |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment