Skip to content
Martin Pihrt edited this page Oct 11, 2026 · 1 revision

OSPy Programs

Internally, all programs maintain a schedule that can be modified directly (just like the custom program). To allow for easier manipulation, the following types of programs have been created. Each program can be of one of these types. In the end, every program can also be written as a custom program.

Prog/type_data 0 1 2 3 4 5
DAYS_SIMPLE start_time duration repeat pause repeat times list days to run
REPEAT_SIMPLE start_time duration repeat pause repeat times repeat days start_date
DAYS_ADVANCED list of intervals [start, end] list days to run
REPEAT_ADVANCED list of intervals [start, end] repeat days
WEEKLY_ADVANCED list of intervals [start, end]
CUSTOM list of intervals [start, end]
WEEKLY_WEATHER irrigation min irrigation max run max pause ratio priority moments
SUNRISE duration repeat pause repeat times list days to run
SUNSET duration repeat pause repeat times list days to run
set_days_simple start_min, duration_min, pause_min, repeat_times, [days]
set_repeat_simple start_min, duration_min, pause_min, repeat_times, repeat_days, start_date
set_days_advanced [schedule], [days]
set_repeat_advanced [schedule], repeat_days, start_date
set_weekly_advanced [schedule]
set_solar program_type, duration_min, pause_min, repeat_times, [days]

Calendar rules

Every recurring program has additive calendar rules. Existing programs load with all months enabled, no parity or day-of-month restriction, no holiday exclusion and no excluded periods, so upgrading does not change their schedule.

allowed_months stores month numbers 1..12; the editor also provides spring, summer, autumn, winter and all-year shortcuts. day_parity is all, odd or even and refers to the calendar day number, not the weekday or the program occurrence number. month_days is an optional list from 1..31; a day that does not exist in a particular month is simply never generated. These selectors are combined with the program's normal weekday or repeat rules.

Program exclusions are persisted as exact ISO dates in excluded_dates and as inclusive ranges in excluded_ranges. Exact ranges use YYYY-MM-DD..YYYY-MM-DD. Annual ranges use MM-DD..MM-DD and may cross New Year, for example 12-20..01-10. An excluded occurrence remains visible in schedule prediction with the stable calendar_excluded block reason instead of silently disappearing.

When exclude_holidays is enabled, OSPy uses the Python holidays package and a two-letter country code. The country is detected from the configured weather coordinates through OpenStreetMap. An administrator may set Options → Weather → Holiday country override when a different calendar is required or automatic detection is unavailable. No weather API key is required for holiday calculation. The editor does not allow holiday exclusion when the library or country is unavailable. If an existing opted-in program is loaded after the package was removed or before an older installation ran the updated installer, its occurrences receive holiday_calendar_unavailable; the scheduler and unrelated programs continue normally.

Pause between programs

The Programs page stores a global pause in options.program_pause as a number of seconds. The editor accepts separate hours, minutes and seconds and persists the result across OSPy restarts. A value of zero disables the feature.

After normal station-duration, water-level and output-usage scheduling, OSPy compares complete occurrences of different automatic programs. When the next program would start before the preceding program's actual end plus the configured pause, every station interval belonging to that occurrence is shifted by the same amount. This preserves the program's internal station timing. An existing gap longer than the configured pause is left unchanged; blocked and manual runs neither move nor reserve the pause.

Mobile API clients read and configure the same value through GET/PUT /program-settings using pause_between_programs_seconds. The accepted range is 0 to 31536000 seconds.

Service outages

Administrators create and remove global service outages on the Programs page. The native mobile client can create, edit and remove the same records through Mobile API v1. Each outage contains a name and a local half-open time interval [start, end). An overlapping automatic occurrence remains in the predicted schedule with the service_outage block reason. Manual Run Now remains available, allowing an administrator to make a deliberate service test. Outages are stored in options.calendar_service_outages and survive a service restart.

Sunrise and sunset programs

SUNRISE and SUNSET are native program types, but astronomical calculation remains owned by the existing Sunrise and Sunset plug-in. OSPy offers these types only while the plug-in is running, Astral calculation is enabled and the plug-in has a valid location. The plug-in exposes program_sun_times_available() and program_sun_times(day); the core does not duplicate Astral or replace the plug-in.

The program stores a signed offset in minutes, a minimum and maximum allowed local start time and a window policy. clamp moves an out-of-window solar time to the nearest boundary; skip omits that occurrence. Duration, pause, repetitions and selected weekdays work like a simple selected-days program. Calendar filters and service outages are evaluated after the astronomical start is calculated. If the provider later becomes unavailable, OSPy does not guess a clock time and generates no automatic solar occurrence until the provider recovers; the saved definition is retained.

Mobile API v1 returns all common values in the additive calendar object and decodes solar type_data through editor.kind values sunrise and sunset. Clients that do not support these additions must preserve them unchanged.

Program group postponement

A program group postponement is a one-time scheduler overlay. It does not rewrite a program's start, schedule, type data, enabled days, or later recurring runs. The administrator selects a new date and start time for the group. OSPy obtains the nearest future eligible occurrence of every enabled program in that group from predicted_schedule(), uses the earliest resulting start as the group anchor, and shifts the selected occurrences by one common time delta. This preserves their scheduler-resolved order, durations, and relative gaps. An occurrence temporarily blocked by rain or by a disabled scheduler remains eligible for postponement, while an occurrence rejected by the program cut-off or a scheduler error is not copied.

The source occurrence UIDs and a minimal snapshot of the shifted station intervals are persisted in options.program_group_postponements. Each record contains a random postponement ID, group ID, creation time, original and target ranges, shift in seconds, and the selected station runs. Because the record is stored in options.db, it remains active after an OSPy service restart. Only one active postponement can exist for a group, the target must be later than the original anchor, and the target is limited to 30 days in the future.

During schedule prediction, programs.apply_group_postponements() removes only the recorded source UIDs and adds deterministic shifted replacement UIDs. The replacements are automatic runs (manual=False), so scheduler enablement, rain blocks, the rain sensor, output usage limits, master-station control, and station delays still apply. Their durations are fixed to the snapshot calculated when the postponement was created, preventing a later prediction from applying the water-level duration adjustment twice.

Before a snapshot is applied, OSPy verifies that the referenced program still exists, is enabled, remains in the same group, and still has the same stations and schedule. A stale snapshot is not allowed to run or suppress a different program after deletion, reindexing, moving, disabling, or material editing. A group with a pending postponement record cannot be deleted.

Cancelling before the original start removes the overlay and restores the normal source occurrence. Cancelling after the original start keeps a temporary cancellation tombstone until the original source range has passed; this prevents the missed source run from starting late, while the postponed replacement is no longer generated. Completed records are removed after their target range has passed, while normal run history remains in the log.

Creating and cancelling postponements is available only on the administrator Programs page. Both actions use POST, pass through the standard ProtectedPage CSRF verification, validate all group and date/time values again on the server, use a lock for concurrent updates, and write an event-log entry when event logging is enabled. The browser submits only the group ID and requested target time; the server always calculates the affected occurrences itself.

Clone this wiki locally