Skip to content

Commit

Permalink
Merge pull request #370 from fboundy/main
Browse files Browse the repository at this point in the history
Update patch-solarman from main
  • Loading branch information
stevebuk1 authored Jan 11, 2025
2 parents fce12a5 + edbf3d5 commit 821d8f1
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 218 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/auto_release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,15 @@ jobs:
RELEASE_NOTES+="No significant changes."
else
echo "Found commits:"
echo -e "${COMMITS}"
RELEASE_NOTES+=$(echo -e "${COMMITS}")
echo "${COMMITS}"
RELEASE_NOTES+="${COMMITS}"
fi
# Output the release notes
echo -e "Release notes generated:\n${RELEASE_NOTES}"
echo "${RELEASE_NOTES}" > release_notes.txt
echo "RELEASE_NOTES=$(<release_notes.txt)" >> $GITHUB_ENV
echo "Release notes generated:"\n"${RELEASE_NOTES}"
echo "release_notes<<EOF" >> $GITHUB_ENV
echo "${RELEASE_NOTES}" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
# Step 5: Create GitHub Release
- name: Create GitHub Release
Expand Down
38 changes: 15 additions & 23 deletions .github/workflows/black.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
- name: Format Code with Black and isort
run: |
black --line-length=119 .
isort .
isort --profile="black" .
# Step 5: Commit Formatting Changes
- name: Commit Formatting Changes
Expand Down Expand Up @@ -65,25 +65,16 @@ jobs:
exit 0
fi
- name: Fetch main branch into a temporary branch
- name: Get Latest Tag
id: get_latest_tag
run: |
git fetch origin main
git checkout -b temp-main origin/main
- name: Get VERSION from Main Branch
id: get_main_version
run: |
VERSION=$(grep -m 1 -oP '(?<=^VERSION = ")[^"]+' apps/pv_opt/pv_opt.py)
if [ -z "$VERSION" ]; then
echo "Error: VERSION not found in apps/pv_opt/pv_opt.py on main branch." >&2
LATEST_TAG=$(git describe --tags --abbrev=0)
if [ -z "$LATEST_TAG" ]; then
echo "Error: No tags found in the repository." >&2
exit 1
fi
echo "main_version=$VERSION" >> $GITHUB_ENV
- name: Switch Back to Source Branch
run: |
git fetch origin $GITHUB_HEAD_REF
git checkout $GITHUB_HEAD_REF
VERSION=$(echo "$LATEST_TAG" | sed -E 's/^v?([0-9]+\.[0-9]+\.[0-9]+)$/\1/')
echo "tag_version=$VERSION" >> $GITHUB_ENV
- name: Get VERSION from Current Branch
id: get_patch_version
Expand All @@ -99,16 +90,16 @@ jobs:
id: validate_or_fix_version
run: |
patch_version=$patch_version
main_version=$main_version
tag_version=$tag_version
main_major=$(echo "$main_version" | awk -F '.' '{print $1}')
main_minor=$(echo "$main_version" | awk -F '.' '{print $2}')
main_patch=$(echo "$main_version" | awk -F '.' '{print $3}')
tag_major=$(echo "$tag_version" | awk -F '.' '{print $1}')
tag_minor=$(echo "$tag_version" | awk -F '.' '{print $2}')
tag_patch=$(echo "$tag_version" | awk -F '.' '{print $3}')
if [[ "$GITHUB_HEAD_REF" == patch* ]]; then
new_patch_version="$main_major.$main_minor.$((main_patch + 1))"
new_patch_version="$tag_major.$tag_minor.$((tag_patch + 1))"
elif [[ "$GITHUB_HEAD_REF" == dev* ]]; then
new_patch_version="$main_major.$((main_minor + 1)).0"
new_patch_version="$tag_major.$((tag_minor + 1)).0"
else
echo "Error: Unsupported source branch type." >&2
exit 1
Expand All @@ -131,6 +122,7 @@ jobs:
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git checkout -B $GITHUB_HEAD_REF
git add apps/pv_opt/pv_opt.py README.md
if git diff --cached --quiet; then
echo "No version changes to commit."
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# PV Opt: Home Assistant Solar/Battery Optimiser v4.0.5
# PV Opt: Home Assistant Solar/Battery Optimiser v4.0.7


Solar / Battery Charging Optimisation for Home Assistant. This appDaemon application attempts to optimise charging and discharging of a home solar/battery system to minimise cost electricity cost on a daily basis using freely available solar forecast data from SolCast. This is particularly beneficial for Octopus Agile but is also benefeficial for other time-of-use tariffs such as Octopus Flux or simple Economy 7.
Expand Down
36 changes: 18 additions & 18 deletions apps/pv_opt/config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ pv_opt:
# ========================================
# Octopus account parameters
# ========================================
# octopus_auto: False # Read tariffs from the Octopus Energy integration. If successful this over-rides the following parameters
octopus_auto: False # Read tariffs from the Octopus Energy integration. If successful this over-rides the following parameters
# octopus_account: !secret octopus_account
# octopus_api_key: !secret octopus_api_key

Expand Down Expand Up @@ -159,12 +159,12 @@ pv_opt:

# octopus_import_tariff_code: E-1R-AGILE-23-12-06-G
# # octopus_export_tariff_code: E-1R-OUTGOING-LITE-FIX-12M-23-09-12-G
# octopus_export_tariff_code: E-1R-OUTGOING-FIX-12M-19-05-13-G
octopus_export_tariff_code: E-1R-OUTGOING-FIX-12M-19-05-13-G

# octopus_import_tariff_code: E-1R-FLUX-IMPORT-23-02-14-G
# octopus_export_tariff_code: E-1R-FLUX-EXPORT-23-02-14-G

# octopus_import_tariff_code: E-1R-GO-VAR-22-10-14-N
octopus_import_tariff_code: E-1R-GO-VAR-22-10-14-N
# octopus_export_tariff_code: E-1R-OUTGOING-LITE-FIX-12M-23-09-12-N

# ========================================
Expand Down Expand Up @@ -444,19 +444,19 @@ pv_opt:

# Tariff comparison
# id_daily_solar: sensor.{device_name}_power_generation_today
# id_solar_power:
# # - sensor.{device_name}_pv_power_1
# # - sensor.{device_name}_pv_power_2
# alt_tariffs:
# - name: Agile_Fix
# octopus_import_tariff_code: E-1R-AGILE-23-12-06-G
# octopus_export_tariff_code: E-1R-OUTGOING-FIX-12M-19-05-13-G

# # - name: Eco7_Fix
# # octopus_import_tariff_code: E-2R-VAR-22-11-01-G
# # octopus_export_tariff_code: E-1R-OUTGOING-FIX-12M-19-05-13-G

# - name: Flux
# octopus_import_tariff_code: E-1R-FLUX-IMPORT-23-02-14-G
# octopus_export_tariff_code: E-1R-FLUX-EXPORT-23-02-14-G
id_solar_power:
- sensor.{device_name}_pv_power_1
- sensor.{device_name}_pv_power_2
alt_tariffs:
- name: Agile_Fix
octopus_import_tariff_code: E-1R-AGILE-23-12-06-G
octopus_export_tariff_code: E-1R-OUTGOING-FIX-12M-19-05-13-G

# - name: Eco7_Fix
# octopus_import_tariff_code: E-2R-VAR-22-11-01-G
# octopus_export_tariff_code: E-1R-OUTGOING-FIX-12M-19-05-13-G

- name: Flux
octopus_import_tariff_code: E-1R-FLUX-IMPORT-23-02-14-G
octopus_export_tariff_code: E-1R-FLUX-EXPORT-23-02-14-G

148 changes: 84 additions & 64 deletions apps/pv_opt/pv_opt.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@
import pvpy as pv
from numpy import nan

VERSION = "4.0.5"
VERSION = "4.0.7"
UNITS = {
"current": "A",
"power": "W",
}


OCTOPUS_PRODUCT_URL = r"https://api.octopus.energy/v1/products/"

DEBUG = False
Expand Down Expand Up @@ -2354,66 +2355,69 @@ def optimise(self):
return

self.pv_system.static_flows = pd.concat([solcast, consumption], axis=1)
self.time_now = pd.Timestamp.utcnow()
self.time_now = pd.Timestamp.utcnow().floor("1min")

self.pv_system.static_flows = self.pv_system.static_flows[self.time_now.floor("30min") :].fillna(0)
self.pv_system.static_flows.index = [self.time_now] + list(self.pv_system.static_flows.index[1:])

soc_now = self.get_config("id_battery_soc")
soc_last_day = self.hass2df(self.config["id_battery_soc"], days=1, log=self.debug)
if self.debug and "S" in self.debug_cat:
self.log(f">>> soc_now: {soc_now}")
self.log(f">>> soc_last_day: {soc_last_day}")
self.log(
f">>> Original: {soc_last_day.loc[soc_last_day.loc[: self.pv_system.static_flows.index[0]].index[-1] :]}"
)

try:
soc_now = float(soc_now)

except:
self.log("")
self.log(
"Unable to get current SOC from HASS. Using last value from History.",
level="WARNING",
)
soc_now = soc_last_day.iloc[-1]

# x = x.astype(float)

try:
soc_last_day = pd.to_numeric(soc_last_day, errors="coerce").interpolate()
self.pv_system.initial_soc = soc_now
# soc_last_day = self.hass2df(self.config["id_battery_soc"], days=1, log=self.debug)
# if self.debug and "S" in self.debug_cat:
# self.log(f">>> soc_now: {soc_now}")
# self.log(f">>> soc_last_day: {soc_last_day}")
# self.log(
# f">>> Original: {soc_last_day.loc[soc_last_day.loc[: self.pv_system.static_flows.index[0]].index[-1] :]}"
# )

# try:
# soc_now = float(soc_now)

# except:
# self.log("")
# self.log(
# "Unable to get current SOC from HASS. Using last value from History.",
# level="WARNING",
# )
# soc_now = soc_last_day.iloc[-1]

# # x = x.astype(float)

# try:
# soc_last_day = pd.to_numeric(soc_last_day, errors="coerce").interpolate()

# soc_last_day = soc_last_day.loc[soc_last_day.loc[: self.pv_system.static_flows.index[0]].index[-1] :]
# if self.debug and "S" in self.debug_cat:
# self.log(
# f">>> Fixed : {soc_last_day.loc[soc_last_day.loc[: self.pv_system.static_flows.index[0]].index[-1] :]}"
# )

# soc_last_day = pd.concat(
# [
# soc_last_day,
# pd.Series(
# data=[soc_now, nan],
# index=[self.time_now, self.pv_system.static_flows.index[0]],
# ),
# ]
# ).sort_index()
# self.pv_system.initial_soc = soc_last_day.interpolate().loc[self.pv_system.static_flows.index[0]]
# except:
# self.pv_system.initial_soc = None

# if not isinstance(self.pv_system.initial_soc, float):
# self.log("")
# self.log(
# "Unable to retrieve initial SOC - assuming it is the same as current SOC",
# level="WARNING",
# )
# self.pv_system.initial_soc = soc_now

# self.pv_system.soc_now = (self.time_now, soc_now)

soc_last_day = soc_last_day.loc[soc_last_day.loc[: self.pv_system.static_flows.index[0]].index[-1] :]
if self.debug and "S" in self.debug_cat:
self.log(
f">>> Fixed : {soc_last_day.loc[soc_last_day.loc[: self.pv_system.static_flows.index[0]].index[-1] :]}"
)

soc_last_day = pd.concat(
[
soc_last_day,
pd.Series(
data=[soc_now, nan],
index=[self.time_now, self.pv_system.static_flows.index[0]],
),
]
).sort_index()
self.pv_system.initial_soc = soc_last_day.interpolate().loc[self.pv_system.static_flows.index[0]]
except:
self.pv_system.initial_soc = None

if not isinstance(self.pv_system.initial_soc, float):
self.log("")
self.log(
"Unable to retrieve initial SOC - assuming it is the same as current SOC",
level="WARNING",
)
self.pv_system.initial_soc = soc_now

self.pv_system.soc_now = (self.time_now, soc_now)

self.log("")
self.log(f"Initial SOC: {self.pv_system.initial_soc}")
# self.log("")
# self.log(f"Initial SOC: {self.pv_system.initial_soc}")
# self.log(f"Current SOC: {self.pv_system.soc_now}")

self.pv_system.calculate_flows()
self.flows = {"Base": self.pv_system.flows}
Expand Down Expand Up @@ -2506,6 +2510,8 @@ def optimise(self):

self.opt = self.flows[self.selected_case]

self.base = self.flows["Base"]

# SVB debug logging
# self.log("")
# self.log("Returned from .flows. self.opt is........")
Expand Down Expand Up @@ -3986,10 +3992,19 @@ def _compare_tariffs(self):
return

consumption = self.load_consumption(start, end)
static = pd.concat([solar, consumption], axis=1).set_axis(["solar", "consumption"], axis=1)
self.pv_system.static_flows = pd.concat([solar, consumption], axis=1).set_axis(
["solar", "consumption"], axis=1
)

initial_soc_df = self.hass2df(self.config["id_battery_soc"], days=2, freq="30min")
initial_soc = initial_soc_df.loc[start]
self.pv_system.initial_soc = initial_soc_df.loc[start]

# Not sure about the next lines, but calculate_flows requires soc_now
soc_now = self.get_config("id_battery_soc")
self.time_now = pd.Timestamp.utcnow()
self.pv_system.soc_now = (self.time_now, soc_now)

# self.pv_system.soc_now = initial_soc_df.loc[start]

self.pv_system.calculate_flows()
base = self.pv_system.flows
Expand All @@ -3999,12 +4014,12 @@ def _compare_tariffs(self):
self.log("")
self.log(f"Start: {start.strftime(DATE_TIME_FORMAT_SHORT):>15s}")
self.log(f"End: {end.strftime(DATE_TIME_FORMAT_SHORT):>15s}")
self.log(f"Initial SOC: {initial_soc:>15.1f}%")
self.log(f"Consumption: {static['consumption'].sum()/2000:15.1f} kWh")
self.log(f"Solar: {static['solar'].sum()/2000:15.1f} kWh")
self.log(f"Initial SOC: {self.pv_system.initial_soc:>15.1f}%")
self.log(f"Consumption: {self.pv_system.static_flows['consumption'].sum()/2000:15.1f} kWh")
self.log(f"Solar: {self.pv_system.static_flows['solar'].sum()/2000:15.1f} kWh")

if self.debug and "T" in self.debug_cat:
self.log(f">>> Yesterday's data:\n{static.to_string()}")
self.log(f">>> Yesterday's data:\n{self.pv_system.static_flows.to_string()}")

for tariff_set in self.config["alt_tariffs"]:
code = {}
Expand All @@ -4024,7 +4039,9 @@ def _compare_tariffs(self):
)

actual = self._cost_actual(start=start, end=end - pd.Timedelta(30, "minutes"))
static["period_start"] = static.index.tz_convert(self.tz).strftime("%Y-%m-%dT%H:%M:%S%z").str[:-2] + ":00"
self.pv_system.static_flows["period_start"] = (
self.pv_system.static_flows.index.tz_convert(self.tz).strftime("%Y-%m-%dT%H:%M:%S%z").str[:-2] + ":00"
)
entity_id = f"sensor.{self.prefix}_opt_cost_actual"
self.set_state(
state=round(actual.sum() / 100, 2),
Expand All @@ -4035,7 +4052,10 @@ def _compare_tariffs(self):
"unit_of_measurement": "GBP",
"friendly_name": f"PV Opt Comparison Actual",
}
| {col: static[["period_start", col]].to_dict("records") for col in ["solar", "consumption"]},
| {
col: self.pv_system.static_flows[["period_start", col]].to_dict("records")
for col in ["solar", "consumption"]
},
)

self.ulog("Net Cost comparison:", underline=None)
Expand Down
Loading

0 comments on commit 821d8f1

Please sign in to comment.