My Solar Project

This year, I embarked on an exhilarating money-saving journey by equipping myself with six powerful solar panels and a couple of cutting-edge lithium iron batteries. Bursting with excitement, I couldn’t wait to share my groundbreaking setup with others.

To kick things off, the skillful installer fitted a state-of-the-art Growatt SPH3000 inverter. But that’s not all! I harnessed the true power of technology by employing a Raspberry Pi 4 and a remarkable custom software known as Solar Assistant. Curious? Check it out here: https://solar-assistant.io/. With the help of Home Assistant, I could now effortlessly monitor every aspect of my solar system.

Now, here’s where the real thrill begins. I joined forces with Octopus Energy and embraced their Flux tariff, a mesmerizing journey into the realm of affordable electricity. Picture this: not one, not two, but three distinct rates to optimize my savings—normal, peak, and off-peak.

As I delved deeper into my money-saving quest, I crafted ingenious automations that would revolutionize the way I consumed energy:

Automation 1: Power Harvesting during Off-Peak Times
At 5 pm sharp, Automation 1 seized the solar forecast for the following day from the impeccable Solcast service. I was always one step ahead, armed with the knowledge of what lay ahead.

Automation 2: Optimizing Battery Charging Based on Solar Forecast
Driven by the solar forecast, Automation 2 intelligently set a “max battery charge” level. If the forecast predicted ample sunlight, it set the level to a mere 10%. However, if clouds dominated the skies, the level skyrocketed to a full 100%—all dynamically adjusted on a sliding scale.

Automation 3: Seizing the Power of Night
At the stroke of 2 am, Automation 3 sprang into action. It toggled the battery charge mode on my remarkable inverter to “ON” and let it soak up the abundant electricity until 5 am. But that’s not all—it also kept a vigilant eye on the max battery charge level. Once that level was reached, the inverter promptly switched to “OFF,” preserving the precious energy for later use.

In essence, my batteries strategically sipped from the mains during the cheapest periods, while the radiant sun later in the day gracefully topped them up to a glorious 100%. The equation was simple: cheap electricity + solar power = the most economical choice.

But I didn’t stop there. I craved even more efficiency! Sensing the possibility of power depletion by 2 am, I ingeniously devised yet another automation. If the battery level dropped to a mere 10% (the dreaded empty state), this automation swiftly intervened, ensuring it was replenished to a predefined level (30%) based on my household’s nighttime power consumption. With this ingenious solution, I could sail through the night, still basking in the bliss of off-peak rates, until the morning sun would rise again.

Ever the vigilant cost-saver, I also considered the perils of reaching dangerously low battery levels just before the peak rate period. Who, in their right mind, would willingly surrender to exorbitant electricity charges? To combat this menace, I implemented a final option—a clever automation triggered by a simple toggle switch. As the late morning hours approached, and my batteries yearned for a boost, I activated the “Boost before Peak” period. This ingenious tactic allowed me to charge during the “normal rate” window, skillfully evading the dreaded peak rate trap.

But wait, there’s more! As an added measure of efficiency, I employed a final automation that extinguished unnecessary house lights when the sun’s generous rays flooded my abode. Who needs artificial illumination.

2 Likes

very nice interface!

I like your enthusiasm :grinning_face_with_smiling_eyes:
Unfortunately I only have 2 panels (600W) which is a little low to justify a battery.
So at peak solar time I alway hurry to plug in the notebook or something to use the excess energy. Should automate something at some point of time…

This is exactly what I’m planning on doing. I have the sph6000 and a few pi’s. I’m going to buy solar assistant and I was wondering are all these functions you’ve done already built in to it or do you have to create them and if so how do I start ?

Hi @Anniman so there are functions which you can control your growatt inverter with here is my list of what gets pulled through MQTT

I use the battery grid first charge and Output Source Priority (together) to set the batteries charging
image
image

To help here is my automation which just uses a helper as a trigger

alias: Charge Growatt Battery based on the helper times
description: ""
trigger:
  - platform: state
    entity_id:
      - binary_sensor.growatt_battery_charge_schedule
condition:
  - condition: numeric_state
    entity_id: input_number.battery_max_charge
    above: 0
action:
  - choose:
      - conditions:
          - condition: state
            entity_id: binary_sensor.growatt_battery_charge_schedule
            state: "on"
            for:
              hours: 0
              minutes: 0
              seconds: 0
        sequence:
          - service: select.select_option
            target:
              entity_id: null
            data:
              option: Enabled
          - service: select.select_option
            target:
              entity_id:
                - select.battery_first_grid_charge
            data:
              option: Enabled
          - service: select.select_option
            data:
              option: Grid first
            target:
              entity_id: select.output_source_priority
          - service: input_text.set_value
            data:
              value: Scheduled Charge
            target:
              entity_id:
                - input_text.battery_status
      - conditions:
          - condition: state
            entity_id: binary_sensor.growatt_battery_charge_schedule
            state: "off"
            for:
              hours: 0
              minutes: 0
              seconds: 0
        sequence:
          - service: select.select_option
            target:
              entity_id:
                - select.battery_first_grid_charge
            data:
              option: Disabled
          - service: select.select_option
            data:
              option: Load first
            target:
              entity_id: select.output_source_priority
          - service: input_text.set_value
            data:
              value: Scheduled Ended
            target:
              entity_id:
                - input_text.battery_status
mode: single

Please note on my inverter ```
select.battery_first_grid_charge
actually selects Battery First which I think is just a growatt inverter bug, (you may need to experiment with selecting the mode if you get a similar thing)

In any case I found solar assistant with the right lead better than the Shine app and I didn’t want to be reliant on growatts cloud as they may turn it off in 15 years!

It looks like Solar Assistant was updated recently as I had to ammend my automation to Select Battery First rather than Grid First so looks like that bug is now ironed out.

Here are my latest templates which I am now using for this automation

#Battery Remaining Time Template

      battery_time_left:
        friendly_name: "Battery Time Left"
        unit_of_measurement: "hours"
        value_template: >-
          {% set battery_capacity = 5.94 - 0.594 %} 
          {% set battery_percentage = states('sensor.battery_state_of_charge')|int / 100  %}
          {% set battery_load = states('sensor.battery_power')|int /-1000 %}
          {% set battery_power = battery_capacity * battery_percentage %}

          {% if battery_load < 0.01 %}
              {% set battery_time_left = 0 %}
            {% else %}
              {% set battery_time_left = battery_power / battery_load %}
          {% endif %}

          {% if battery_time_left < 0 %}
            0
          {% else %}
            {{ battery_time_left|round(2) }}
          {% endif %}

      battery_remains:
        friendly_name: Battery Remains
        unit_of_measurement: 'hours'
        value_template: >
          {% if states('sensor.battery_time_stats') in ['unknown', 'unavailable', 'None'] %}
            0
          {% else %}
            {{ states('sensor.battery_time_stats') }}
          {% endif %}
            
      hours_till_calculated_charge_start:
        friendly_name: "Hours Till Calculated Charge Start"
        unit_of_measurement: 'hours'
        value_template: >
          {% set calculated_charge_start_time = state_attr('sensor.calculated_charge_start_time', 'time') %}
          {% set t = today_at(calculated_charge_start_time) %}
          {% set s = t if now() < t else t + timedelta(hours=24) %}
          {{ ((s - now()).total_seconds() / 3600) | round(2) }}

#Sensors to manage the flexible charge starting time for the house battery charging

      actual_charge:
        friendly_name: "Actual Charge"
        unit_of_measurement: "%"
        value_template: "{{ max(0, (states('input_number.battery_max_charge') | float - states('sensor.battery_state_of_charge') | float)) | round(1) }}"

      actual_charge_time:
        friendly_name: "Actual Charge Time"
        unit_of_measurement: "hours"
        value_template: >
          {% set onepercent = states('input_number.time_to_last_charge_1_percent') | float %}
          {% set actual_charge = states('sensor.actual_charge') | float %}
          {{ ((onepercent * actual_charge) / 60) | round(1) }}

#dynamic charging getting the forecast and adjusting the max charge using full date and time stamps

      solar_forecast_scheduler:
        friendly_name: "Solar Forecast Schedule"
        value_template: >
          {% if state_attr('calendar.charge_schedule', 'message') == 'Charge Window' %}
            {{ (as_timestamp(state_attr('calendar.charge_schedule', 'start_time')) - 1800) | timestamp_custom('%Y-%m-%d %H:%M:%S') }}
          {% else %}
            N/A
          {% endif %}
        entity_id: schedule.charge_schedule

      time_to_adjust_max_charging:
        friendly_name: "Time to Adjust Max Charge"
        value_template: >
          {% if state_attr('calendar.charge_schedule', 'message') == 'Charge Window' %}
            {{ (as_timestamp(state_attr('calendar.charge_schedule', 'start_time')) - 900) | timestamp_custom('%Y-%m-%d %H:%M:%S') }}
          {% else %}
            N/A
          {% endif %}
        entity_id: schedule.charge_schedule

#dynamic charging working out what the time to start charging is using full date and time stamps

      time_to_start_charging:
        friendly_name: "Time to Start Charging"
        value_template: >
          {% if state_attr('calendar.charge_schedule', 'message') == 'Charge Window' %}
            {% set minutes_to_start = states('sensor.actual_charge_time') | float * 3600 %}
            {{ (as_timestamp(state_attr('calendar.charge_schedule', 'end_time')) - minutes_to_start) | timestamp_custom('%Y-%m-%d %H:%M:%S') }}
          {% else %}
            N/A
          {% endif %}
        entity_id: calendar.charge_schedule

#entity which changes state when the charging should be active

      activated_charge:
        friendly_name: "Activated Charge"
        value_template: >-
          {% set current_time = now().strftime('%Y-%m-%d %H:%M:%S') %}
          {% set start_time = states('sensor.time_to_start_charging') %}
          {% set end_time = state_attr('calendar.charge_schedule', 'end_time') %}
          {{ "On" if current_time >= start_time and current_time <= end_time else "Off" }}

#battery state template
      battery_state:
        friendly_name: "Battery State"
        value_template: >-
          {% if is_state('select.battery_first_grid_charge', 'Enabled') %}
            Charging from Grid
          {% elif float(states('sensor.load_power')) >= float(states('sensor.pv_power')) %}
            Discharging
          {% elif states('sensor.battery_state_of_charge') <= '11' %}
            Discharged
          {% else %}
            Charging from Solar
          {% endif %}

      battery_state_info:
        friendly_name: "Battery State Info"
        value_template: >-
          {% set battery_state = states('sensor.battery_state') %}
          {% if battery_state == 'Charging from Grid' %}
            Grid
          {% elif battery_state == 'Charging from Solar' %}
            Solar
          {% elif battery_state == 'Discharged' %}
            Discharged
          {% else %}
            Discharging
          {% endif %}
        icon_template: >-
          {% set battery_state = states('sensor.battery_state') %}
          {% if battery_state == 'Charging from Grid' %}
            mdi:transmission-tower
          {% elif battery_state == 'Charging from Solar' %}
            mdi:solar-power
          {% elif battery_state == 'Discharged' %}
            mdi:battery-alert
          {% else %}
            mdi:battery
          {% endif %}

      time:
        friendly_name: "Time"
        value_template: "{{ now().strftime('%H:%M:%S') }}"

  - platform: statistics
    name: "Battery Time Stats"
    entity_id: sensor.battery_time_left
    state_characteristic: mean
    max_age:
      minutes: 5
    sampling_size: 100

And my dashboard looks like this…isn’t winter bad for solar power!

@daknightuk this is pretty awesome! Thanks for putting this together.

I am still learning all of the ins and outs of Home Assistant however I have managed to onboard my Growatt inverter/battery with Solar Assistant… no need for crappy Chinese server/software anymore so I’m pretty happy about that.

Now on to automation… I tried adding your templates under my configuration.yaml and managed to get the formatting correct by moving platform: statistics to the top. However, I am getting errors in HA with it complaining that everything after the line below, is not allowed under the template configuration.

#dynamic charging working out what the time to start charging is using full date and time stamps

Can you explain a bit further about templates and how you have this set up within your environment, please? I guess the best way is to separate templates into files… (I’m not even sure how to use them at this point though… but I can figure that out).

You are basically at the end game right now. I have some very simple automation testing set up for this evening to see if my battery will charge, and then revert to load. I want to be able to base this off of my agile tariff… eventually, and this is a damn good start.

1 Like

Just to note, I’ve started to implement some of your automations using the code above with some tweaks as there is information missing that is critical to get this in place.

I will update with the code I’ve used once it’s all tested and in place but this has been a huuuuuuuge help so far. I’m using Chat-GPT to tweak a few things, for example, I could not get the battery remaining time to show a correct value using the formula above (probably due to the formatting/inverter), however this has now been fixed. I’m also getting a feel of where things go in the GUI etc.

1 Like