Skip to content

Commit

Permalink
Merge pull request #45 from isorin/perf_fix_1
Browse files Browse the repository at this point in the history
Performance and functional improvements
  • Loading branch information
autoSteve authored Jun 25, 2024
2 parents a0f6d87 + 66e88a9 commit fc8897c
Show file tree
Hide file tree
Showing 3 changed files with 330 additions and 261 deletions.
162 changes: 81 additions & 81 deletions custom_components/solcast_solar/coordinator.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""The Solcast Solar integration."""
from __future__ import annotations
from datetime import datetime as dt

import logging
import traceback
Expand All @@ -23,6 +24,9 @@ def __init__(self, hass: HomeAssistant, solcast: SolcastApi, version: str) -> No
self._hass = hass
self._previousenergy = None
self._version = version
self._lastDay = None
self._dayChanged = False
self._dataUpdated = False

super().__init__(
hass,
Expand All @@ -38,35 +42,40 @@ async def _async_update_data(self):
async def setup(self):
d={}
self._previousenergy = d

self._lastDay = dt.now(self.solcast._tz).day
try:
#4.0.18 - added reset usage call to reset usage sensors at UTC midnight
async_track_utc_time_change(self._hass, self.update_utcmidnight_usage_sensor_data, hour=0,minute=0,second=0)
async_track_utc_time_change(self._hass, self.update_integration_listeners, second=0)
async_track_utc_time_change(self._hass, self.update_integration_listeners, minute=range(0, 60, 5), second=0)
except Exception as error:
_LOGGER.error("SOLCAST - Error coordinator setup: %s", traceback.format_exc())


async def update_integration_listeners(self, *args):
try:
crtDay = dt.now(self.solcast._tz).day
self._dateChanged = (crtDay != self._lastDay)
if self._dateChanged:
self._lastDay = crtDay

self.async_update_listeners()
except Exception:
#_LOGGER.error("SOLCAST - update_integration_listeners: %s", traceback.format_exc())
pass

async def update_utcmidnight_usage_sensor_data(self, *args):
try:
for k in self.solcast._api_used.keys():
self.solcast._api_used[k] = 0
self.async_update_listeners()
await self.solcast.reset_api_usage()
except Exception:
#_LOGGER.error("SOLCAST - update_utcmidnight_usage_sensor_data: %s", traceback.format_exc())
pass

async def service_event_update(self, *args):
#await self.solcast.sites_weather()
await self.solcast.http_data(dopast=False)
self._dataUpdated = True
await self.update_integration_listeners()
self._dataUpdated = False

async def service_event_delete_old_solcast_json_file(self, *args):
await self.solcast.delete_solcast_file()
Expand All @@ -78,84 +87,75 @@ def get_energy_tab_data(self):
return self.solcast.get_energy_data()

def get_sensor_value(self, key=""):
if key == "total_kwh_forecast_today":
return self.solcast.get_total_kwh_forecast_day(0)
elif key == "peak_w_today":
return self.solcast.get_peak_w_day(0)
elif key == "peak_w_time_today":
return self.solcast.get_peak_w_time_day(0)
elif key == "forecast_this_hour":
return self.solcast.get_forecast_n_hour(0)
elif key == "forecast_next_hour":
return self.solcast.get_forecast_n_hour(1)
elif key == "forecast_custom_hour":
return self.solcast.get_forecast_custom_hour(self.solcast._customhoursensor)
elif key == "forecast_next_12hour":
return self.solcast.get_forecast_n_hour(12)
elif key == "forecast_next_24hour":
return self.solcast.get_forecast_n_hour(24)
elif key == "total_kwh_forecast_tomorrow":
return self.solcast.get_total_kwh_forecast_day(1)
elif key == "total_kwh_forecast_d3":
return self.solcast.get_total_kwh_forecast_day(2)
elif key == "total_kwh_forecast_d4":
return self.solcast.get_total_kwh_forecast_day(3)
elif key == "total_kwh_forecast_d5":
return self.solcast.get_total_kwh_forecast_day(4)
elif key == "total_kwh_forecast_d6":
return self.solcast.get_total_kwh_forecast_day(5)
elif key == "total_kwh_forecast_d7":
return self.solcast.get_total_kwh_forecast_day(6)
elif key == "power_now":
return self.solcast.get_power_production_n_mins(0)
elif key == "power_now_30m":
return self.solcast.get_power_production_n_mins(30)
elif key == "power_now_1hr":
return self.solcast.get_power_production_n_mins(60)
elif key == "power_now_12hr":
return self.solcast.get_power_production_n_mins(60*12)
elif key == "power_now_24hr":
return self.solcast.get_power_production_n_mins(60*24)
elif key == "peak_w_tomorrow":
return self.solcast.get_peak_w_day(1)
elif key == "peak_w_time_tomorrow":
return self.solcast.get_peak_w_time_day(1)
elif key == "get_remaining_today":
return self.solcast.get_remaining_today()
elif key == "api_counter":
return self.solcast.get_api_used_count()
elif key == "api_limit":
return self.solcast.get_api_limit()
elif key == "lastupdated":
return self.solcast.get_last_updated_datetime()
elif key == "hard_limit":
#return self.solcast._hardlimit < 100
return False if self.solcast._hardlimit == 100 else f"{round(self.solcast._hardlimit * 1000)}w"
# elif key == "weather_description":
# return self.solcast.get_weather()


#just in case
return None
match key:
case "total_kwh_forecast_today":
return self.solcast.get_total_kwh_forecast_day(0)
case "peak_w_today":
return self.solcast.get_peak_w_day(0)
case "peak_w_time_today":
return self.solcast.get_peak_w_time_day(0)
case "forecast_this_hour":
return self.solcast.get_forecast_n_hour(0)
case "forecast_next_hour":
return self.solcast.get_forecast_n_hour(1)
case "forecast_custom_hour":
return self.solcast.get_forecast_custom_hours(self.solcast._customhoursensor)
case "total_kwh_forecast_tomorrow":
return self.solcast.get_total_kwh_forecast_day(1)
case "total_kwh_forecast_d3":
return self.solcast.get_total_kwh_forecast_day(2)
case "total_kwh_forecast_d4":
return self.solcast.get_total_kwh_forecast_day(3)
case "total_kwh_forecast_d5":
return self.solcast.get_total_kwh_forecast_day(4)
case "total_kwh_forecast_d6":
return self.solcast.get_total_kwh_forecast_day(5)
case "total_kwh_forecast_d7":
return self.solcast.get_total_kwh_forecast_day(6)
case "power_now":
return self.solcast.get_power_n_mins(0)
case "power_now_30m":
return self.solcast.get_power_n_mins(30)
case "power_now_1hr":
return self.solcast.get_power_n_mins(60)
case "peak_w_tomorrow":
return self.solcast.get_peak_w_day(1)
case "peak_w_time_tomorrow":
return self.solcast.get_peak_w_time_day(1)
case "get_remaining_today":
return self.solcast.get_forecast_remaining_today()
case "api_counter":
return self.solcast.get_api_used_count()
case "api_limit":
return self.solcast.get_api_limit()
case "lastupdated":
return self.solcast.get_last_updated_datetime()
case "hard_limit":
#return self.solcast._hardlimit < 100
return False if self.solcast._hardlimit == 100 else f"{round(self.solcast._hardlimit * 1000)}w"
# case "weather_description":
# return self.solcast.get_weather()
case _:
return None

def get_sensor_extra_attributes(self, key=""):
if key == "total_kwh_forecast_today":
return self.solcast.get_forecast_day(0)
elif key == "total_kwh_forecast_tomorrow":
return self.solcast.get_forecast_day(1)
elif key == "total_kwh_forecast_d3":
return self.solcast.get_forecast_day(2)
elif key == "total_kwh_forecast_d4":
return self.solcast.get_forecast_day(3)
elif key == "total_kwh_forecast_d5":
return self.solcast.get_forecast_day(4)
elif key == "total_kwh_forecast_d6":
return self.solcast.get_forecast_day(5)
elif key == "total_kwh_forecast_d7":
return self.solcast.get_forecast_day(6)

#just in case
return None
match key:
case "total_kwh_forecast_today":
return self.solcast.get_forecast_day(0)
case "total_kwh_forecast_tomorrow":
return self.solcast.get_forecast_day(1)
case "total_kwh_forecast_d3":
return self.solcast.get_forecast_day(2)
case "total_kwh_forecast_d4":
return self.solcast.get_forecast_day(3)
case "total_kwh_forecast_d5":
return self.solcast.get_forecast_day(4)
case "total_kwh_forecast_d6":
return self.solcast.get_forecast_day(5)
case "total_kwh_forecast_d7":
return self.solcast.get_forecast_day(6)
case _:
return None

def get_site_sensor_value(self, roof_id, key):
match key:
Expand Down
31 changes: 30 additions & 1 deletion custom_components/solcast_solar/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import logging
import traceback
from dataclasses import dataclass
from enum import Enum

from homeassistant.components.sensor import (
SensorDeviceClass,
Expand Down Expand Up @@ -34,7 +35,6 @@

_LOGGER = logging.getLogger(__name__)


SENSORS: dict[str, SensorEntityDescription] = {
"total_kwh_forecast_today": SensorEntityDescription(
key="total_kwh_forecast_today",
Expand Down Expand Up @@ -232,6 +232,25 @@
#),
}

class SensorUpdatePolicy(Enum):
DEFAULT = 0
EVERY_TIME_INTERVAL = 1

def getSensorUpdatePolicy(key) -> SensorUpdatePolicy:
match key:
case (
"forecast_this_hour" |
"forecast_next_hour" |
"forecast_custom_hour" |
"forecast_remaining_today" |
"get_remaining_today" |
"power_now" |
"power_now_30m" |
"power_now_1hr"
):
return SensorUpdatePolicy.EVERY_TIME_INTERVAL
case _:
return SensorUpdatePolicy.DEFAULT

async def async_setup_entry(
hass: HomeAssistant,
Expand Down Expand Up @@ -289,6 +308,7 @@ def __init__(

self.entity_description = entity_description
self.coordinator = coordinator
self.update_policy = getSensorUpdatePolicy(entity_description.key)
self._attr_unique_id = f"{entity_description.key}"

self._attributes = {}
Expand Down Expand Up @@ -344,6 +364,15 @@ def should_poll(self) -> bool:
@callback
def _handle_coordinator_update(self) -> None:
"""Handle updated data from the coordinator."""

# these sensors will pick-up the change on the next interval update (5mins)
if self.update_policy == SensorUpdatePolicy.EVERY_TIME_INTERVAL and self.coordinator._dataUpdated:
return

# these sensors update when the date changed or when there is new data
if self.update_policy == SensorUpdatePolicy.DEFAULT and not (self.coordinator._dateChanged or self.coordinator._dataUpdated) :
return

try:
self._sensor_data = self.coordinator.get_sensor_value(
self.entity_description.key
Expand Down
Loading

0 comments on commit fc8897c

Please sign in to comment.