Skip to content

Commit

Permalink
v2024.7.0 fix #419, #401, progress for #363
Browse files Browse the repository at this point in the history
  • Loading branch information
jeroenterheerdt committed Jul 7, 2024
1 parent 583b448 commit b1f7284
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 22 deletions.
66 changes: 53 additions & 13 deletions custom_components/smart_irrigation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,19 +491,19 @@ async def async_sensor_state_changed(
)
else:
_LOGGER.debug(
f"async_sensor_state_changed: invalid value received, ignoring {val}"
f"async_sensor_state_changed: value received for entity {entity} that is not in use for mapping {mapping.get(const.MAPPING_ID)}, ignoring value {val} for key {key}"
)
await self.async_continuous_update_for_mapping(
mapping.get(const.MAPPING_ID)
)

async def async_continuous_update_for_mapping(self, mapping_id):
"""First, check is mapping doesn't use OWM. Then perform update for all automatic zones that use this mapping, assuming their modules do not use forecasting."""
"""First, check is mapping doesn't use a Weather Service (to avoid API overload). Then perform update for all automatic zones that use this mapping, assuming their modules do not use forecasting."""
if mapping_id is not None:
mapping = self.store.get_mapping(mapping_id)
if mapping is not None:
if not self.check_mapping_sources(mapping_id)[0]:
# mapping does not use OWM
# mapping does not use Weather Service
zones = self._get_zones_that_use_this_mapping(mapping_id)
for z in zones:
zone = self.store.get_zone(z)
Expand All @@ -515,23 +515,62 @@ async def async_continuous_update_for_mapping(self, mapping_id):
# check the module is not pyeto or if it is, that it does not use forecasting
mod = self.store.get_module(zone.get(const.ZONE_MODULE))
if mod is not None:
if mod.get(const.MODULE_NAME) != "PyETO" or (
mod.get(const.MODULE_NAME) == "PyETO"
and int(
mod.get(const.MODULE_CONFIG).get(
const.CONF_PYETO_FORECAST_DAYS
)
can_calculate = False
if mod.get(const.MODULE_NAME) != "PyETO":
can_calculate = True
_LOGGER.info(
f"[async_continuous_update_for_mapping]: module is not PyETO, so we can calculate for zone {zone.get(const.ZONE_ID)}."
)
== 0
):
else:
# module is PyETO. Check the config for forecast days == 0
if mod.get(const.MODULE_CONFIG):
# there is a config on the module, so let's check it
if (
mod.get(const.MODULE_CONFIG).get(
const.CONF_PYETO_FORECAST_DAYS
)
== 0
):
can_calculate = True
_LOGGER.info(
f"checked config for PyETO module on zone {zone.get(const.ZONE_ID)}, forecast_days==0, so we can calculate."
)
else:
_LOGGER.info(
f"checked config for PyETO module on zone {zone.get(const.ZONE_ID)}, forecast_days>0, skipping to avoid API calls that can incur costs."
)
else:
# default config for pyeto is forecast = 0, since there is no config we can calculate
can_calculate = True
_LOGGER.info(
f"no config on PyETO module, since default is forecast_days==0, we can calculate for zone {zone.get(const.ZONE_ID)}."
)

if can_calculate:
# get the zone and calculate
_LOGGER.debug(
f"continous_update_for_mapping: calculating zone {zone.get(const.ZONE_ID)}"
f"[async_continuous_update_for_mapping] for mapping {mapping_id}: calculating zone {zone.get(const.ZONE_ID)}"
)
await self.async_calculate_zone(
zone.get(const.ZONE_ID),
continuous_updates=True,
)
else:
_LOGGER.info(
f"[async_continuous_update_for_mapping] for mapping {mapping_id}: zone {z} has module {mod.get(const.MODULE_NAME)} that uses forecasting, skipping to avoid API calls that can incur costs."
)
else:
_LOGGER.info(
f"[async_continuous_update_for_mapping] for mapping {mapping_id}: zone {z} has no module, skipping."
)
else:
_LOGGER.info(
f"[async_continuous_update_for_mapping] for mapping {mapping_id}: zone {z} is not automatic, skipping."
)
else:
_LOGGER.info(
f"[async_continuous_update_for_mapping] for mapping {mapping_id}: mapping does use weather service, skipping automatic update to avoid API calls that can incur costs."
)

async def set_up_auto_calc_time(self, data):
# unsubscribe from any existing track_time_changes
Expand Down Expand Up @@ -957,6 +996,7 @@ async def async_calculate_zone(self, zone_id, continuous_updates=False):
const.DOMAIN + "_config_updated",
zone.get(const.ZONE_ID),
)
async_dispatcher_send(self.hass, const.DOMAIN + "_update_frontend")
else:
# no data to calculate with!
_LOGGER.warning(
Expand Down Expand Up @@ -1415,7 +1455,7 @@ async def async_update_zone_config(self, zone_id: int = None, data: dict = {}):
)
else:
_LOGGER.error(
"Error calculating zone {}. You have configured forecasting but but there is no OWM API configured. Either configure the OWM API or stop using forcasting on the PyETO module.".format(
"Error calculating zone {}. You have configured forecasting but but there is no Weather API configured. Either configure the OWM API or stop using forcasting on the PyETO module.".format(
res[const.ZONE_NAME]
)
)
Expand Down
3 changes: 2 additions & 1 deletion custom_components/smart_irrigation/const.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Store constants."""

VERSION = "v2024.6.13"
VERSION = "v2024.7.0"
NAME = "Smart Irrigation"
MANUFACTURER = "@jeroenterheerdt"

Expand Down Expand Up @@ -197,6 +197,7 @@
UNIT_INCH = "in"
UNIT_PERCENT = "%"
UNIT_MBAR = "mbar"
UNIT_MILLIBAR = "millibar"
UNIT_HPA = "hPa"
UNIT_PSI = "psi"
UNIT_INHG = "inch Hg"
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion custom_components/smart_irrigation/frontend/src/const.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const VERSION = "v2024.6.13";
export const VERSION = "v2024.7.0";
export const REPO = "https://github.com/jeroenterheerdt/HASmartIrrigation;";
export const ISSUES_URL = REPO + "/issues";

Expand Down
3 changes: 2 additions & 1 deletion custom_components/smart_irrigation/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
UNIT_M2,
UNIT_MBAR,
UNIT_MH,
UNIT_MILLIBAR,
UNIT_MJ_DAY_M2,
UNIT_MJ_DAY_SQFT,
UNIT_MM,
Expand Down Expand Up @@ -205,7 +206,7 @@ def convert_between(from_unit, to_unit, val):
elif from_unit in [UNIT_M2, UNIT_SQ_FT]:
return convert_area(from_unit, to_unit, val)
# convert pressures
elif from_unit in [UNIT_MBAR, UNIT_HPA, UNIT_PSI, UNIT_INHG]:
elif from_unit in [UNIT_MBAR, UNIT_MILLIBAR, UNIT_HPA, UNIT_PSI, UNIT_INHG]:
return convert_pressure(from_unit, to_unit, val)
# convert speeds
elif from_unit in [UNIT_KMH, UNIT_MS, UNIT_MH]:
Expand Down
2 changes: 1 addition & 1 deletion custom_components/smart_irrigation/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@
"iot_class": "local_push",
"issue_tracker": "https://github.com/jeroenterheerdt/HASmartIrrigation/issues",
"requirements": [],
"version": "v2024.6.13"
"version": "v2024.7.0"
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import requests

# DO NOT USE THESE FOR TESTING, INSTEAD DEFINE THE CONSTS IN THIS FILE
from ..const import (
"""from ..const import (
MAPPING_DEWPOINT,
MAPPING_HUMIDITY,
MAPPING_MAX_TEMP,
Expand All @@ -18,13 +18,30 @@
MAPPING_PRESSURE,
MAPPING_TEMPERATURE,
MAPPING_WINDSPEED,
)
)"""
MAPPING_ID = "id"
MAPPING_NAME = "name"
MAPPING_DATA = "data"
MAPPING_DATA_LAST_UPDATED = "data_last_updated"
MAPPING_DATA_MULTIPLIER = "data_multiplier"
MAPPING_MAPPINGS = "mappings"
MAPPING_DEWPOINT = "Dewpoint"
MAPPING_EVAPOTRANSPIRATION = "Evapotranspiration"
MAPPING_HUMIDITY = "Humidity"
MAPPING_MAX_TEMP = "Maximum Temperature"
MAPPING_MIN_TEMP = "Minimum Temperature"
MAPPING_PRECIPITATION = "Precipitation"
MAPPING_PRESSURE = "Pressure"
MAPPING_SOLRAD = "Solar Radiation"
MAPPING_TEMPERATURE = "Temperature"
MAPPING_WINDSPEED = "Windspeed"

_LOGGER = logging.getLogger(__name__)

# Open Weather Map URL
PirateWeather_URL = "https://api.pirateweather.net/forecast/{}/{},{}?units={}&version={}&exclude=minutely,hourly,alerts"

RETRY_TIMES = 3
# Required PirateWeather keys for validation
PirateWeather_wind_speed_key_name = "windSpeed"
PirateWeather_pressure_key_name = "pressure"
Expand Down Expand Up @@ -110,7 +127,18 @@ def get_forecast_data(self):
>= self._last_time_called + datetime.timedelta(seconds=self.cache_seconds)
):
try:
req = requests.get(self.url, timeout=60) # 60 seconds timeout
for i in range(RETRY_TIMES):
req = requests.get(self.url, timeout=60) # 60 seconds timeout
if req.status_code == 200:
break
i = i + 1
if req.status_code != 200:
_LOGGER.error(
"PirateWeather API returned error status code: {}".format(
req.status_code
)
)
return
doc = json.loads(req.text)
_LOGGER.debug(
"PirateWeatherClient get_forecast_data called API {} and received {}".format(
Expand Down Expand Up @@ -198,7 +226,11 @@ def get_data(self):
>= self._last_time_called + datetime.timedelta(seconds=self.cache_seconds)
):
try:
req = requests.get(self.url, timeout=60) # 60 seconds timeout
for i in range(RETRY_TIMES):
req = requests.get(self.url, timeout=60) # 60 seconds timeout
if req.status_code == 200:
break
i = i + 1
if req.status_code != 200:
_LOGGER.error(
"PirateWeather API returned error status code: {}".format(
Expand Down

0 comments on commit b1f7284

Please sign in to comment.