bhyve-home-assistant icon indicating copy to clipboard operation
bhyve-home-assistant copied to clipboard

Possible enhancement to bhyve_next_watering.py - Manual program intervals

Open ComputaBloke opened this issue 1 year ago • 1 comments

Is your feature request related to a problem? Please describe. I have a Orbit BHyve 6-channel, but found my watering program configuration (frequency, interval) wasn't supported by current script to calculate the a 'next watering' for the zone. So I wanted to share a rough solution which might work for others.

Describe the solution you'd like Want to calculate the zone 'next watering' for a manual interval-based watering schedule. Equivalent to what the mobile app shows. Since this is currently marked as *** TODO ***, here is my crack at resolving this which you can try by editing your copy of the bhyve_next_watering.py script accordingly.

Warning: This is just a start to help @sebr and the community, there are probably lots of edge cases to consider: if you have set a rain-delay, more than one program is active, more than one schedule time, or may be there are interval types which might be hours or weeks instead of days? I've also found that Orbit does not adjust for daylight savings so watering may run an hour before schedule.

Pre-requisites

  1. Standard install (per the README). HACS-installed the sebr/bhyve component, Python script integration is enabled in configuration.yaml, and bhyve_next_watering.py script downloaded and available in your <config>/python_scripts/ folder. An automation is configured to trigger the service python_script.bhyve_next_watering which you can run the script on demand for a zone's entity_id.
  2. Enable logging in configuration.yaml for python_script (set to info or debug)
# Logger config
# https://www.home-assistant.io/integrations/logger/
logger:
  default: warning
  logs:
    homeassistant.components.python_script: info # https://www.home-assistant.io/integrations/python_script [homeassistant.components.python_script.bhyve_next_watering.py]
  1. Watering program is configured in BHyve app, and can be output through the bhyve_next_watering.py (as logged on Line 89). When the watering program is run through a JSON formatter it should look something like this:
            logger.info("Checking manual program: %s", program)
            """
            Example output:
            {
                'enabled': True,
                'name': '3 Day Cycle',
                'is_smart_program': False,
                'start_times': ['07:10'],
                'frequency': {
                    'type': 'interval',
                    'interval': 3,
                    'interval_start_time': '2023-03-18T13:30:00.000Z'
                },
                'run_times': [{
                        'run_time': 6,
                        'station': 1
                    }
                ]
            }
            """

Calculation Logic Given program above. Pseudo code to calculate next watering is:

  1. intervals_td = (Now - Start Date) / Configured Interval - Returns a time-delta. The 'intervals_td.days' is not really a number of days, it should hold the rounded count of waterings which would occur for the given configured interval (e.g. 'interval': 3 = every 3 days), between when the program was started, up till the 'last' watering.
  2. next_watering_tsz = Start Date + ((intervals_td.days+1) * Configured Interval)+1) - Multiplies count of intervals (+1 for next interval) with the configured interval, into a total number of days, which can then be added to the program start date, to get next watering date... but also found I needed to plus one day in my case (since I'm on plus-side of UTC)
  3. next_watering = convert the next_watering_tsz (UTC) to local timezone, and replace hours & mins with configured start time.

Implementation (fragment only, suggested script amendment) Replace most of the existing placeholder code from Line 90 with this:

            configured_type = program.get("frequency", {}).get("type", "")
            if configured_type == "interval":
                configured_start = program.get("frequency", {}).get("interval_start_time", "")
                configured_start_tsz = dt_util.parse_datetime(str(configured_start))
                configured_intervals = program.get("frequency", {}).get("interval", 9999)
            
            configured_start_time = program.get("start_times", [])[0] # assuming only one..
            #start_time = dt_util.parse_time(configured_start_time) # Error executing script: Not allowed to access module.parse_time
            start_hours, start_mins = [int(i) for i in configured_start_time.split(":")]
            
            logger.info("intervals:%s", configured_intervals)
            
            intervals_td = (dt_util.now() - configured_start_tsz) / configured_intervals
            logger.info(f"intervals: {intervals_td.days}, ")
            
            next_watering_tsz = configured_start_tsz + datetime.timedelta(days=((intervals_td.days+1) * configured_intervals)+1)
            next_watering = dt_util.as_local(next_watering_tsz).replace(hour=start_hours, minute=start_mins)
            logger.info(f"next_watering: {configured_start_tsz} + ({(intervals_td.days+1)} * {configured_intervals}) = {next_watering_tsz} at start time {configured_start_time} = {next_watering}")

Resulting program and calculation will be logged when you run the Automation.

ComputaBloke avatar Apr 22 '23 04:04 ComputaBloke