Exemple #1
0
    def POST(self):
        qdict = web.input()
        station_seconds = {}
        for station in stations.enabled_stations():
            mm_str = "mm" + str(station.index)
            ss_str = "ss" + str(station.index)
            if mm_str in qdict and ss_str in qdict:
                seconds = int(qdict[mm_str] or 0) * 60 + int(qdict[ss_str] or 0)
                station_seconds[station.index] = seconds

        run_once.set(station_seconds)
        raise web.seeother('/')
Exemple #2
0
    def POST(self):
        qdict = web.input()
        station_seconds = {}
        for station in stations.enabled_stations():
            mm_str = "mm" + str(station.index)
            ss_str = "ss" + str(station.index)
            if mm_str in qdict and ss_str in qdict:
                seconds = int(qdict[mm_str] or 0) * 60 + int(qdict[ss_str] or 0)
                station_seconds[station.index] = seconds

        run_once.set(station_seconds)
        raise web.seeother('/')
Exemple #3
0
def predicted_schedule(start_time, end_time):
    """Determines all schedules for the given time range.
    To calculate what should currently be active, a start time of some time (a day) ago should be used."""

    adjustment = level_adjustments.total_adjustment()
    max_usage = options.max_usage
    delay_delta = datetime.timedelta(seconds=options.station_delay)

    rain_block_start = datetime.datetime.now()
    rain_block_end = rain_blocks.block_end()

    skip_intervals = log.finished_runs() + log.active_runs()
    current_active = [
        interval for interval in skip_intervals if not interval['blocked']
    ]

    usage_changes = {}
    for active in current_active:
        start = active['start']
        end = active['end']
        if start not in usage_changes:
            usage_changes[start] = 0
        if end not in usage_changes:
            usage_changes[end] = 0

        usage_changes[start] += active['usage']
        usage_changes[end] -= active['usage']

    station_schedules = {}

    # Get run-once information:
    for station in stations.enabled_stations():
        run_once_intervals = run_once.active_intervals(start_time, end_time,
                                                       station.index)
        for interval in run_once_intervals:
            if station.index not in station_schedules:
                station_schedules[station.index] = []

            program_name = _('Run-Once')

            new_schedule = {
                'active':
                None,
                'program':
                -1,
                'program_name':
                program_name,
                'fixed':
                True,
                'cut_off':
                0,
                'manual':
                True,
                'blocked':
                False,
                'start':
                interval['start'],
                'original_start':
                interval['start'],
                'end':
                interval['end'],
                'uid':
                '%s-%s-%d' %
                (str(interval['start']), str(program_name), station.index),
                'usage':
                station.usage
            }
            station_schedules[station.index].append(new_schedule)

    # Get run-now information:
    if programs.run_now_program is not None:
        program = programs.run_now_program
        for station in sorted(program.stations):
            run_now_intervals = program.active_intervals(
                start_time, end_time, station)
            for interval in run_now_intervals:
                if station >= stations.count(
                ) or stations.master == station or stations.master_two == station or not stations[
                        station].enabled:
                    continue

                if station not in station_schedules:
                    station_schedules[station] = []

                program_name = "%s " % program.name + _('Run-Now')

                new_schedule = {
                    'active':
                    None,
                    'program':
                    -1,
                    'program_name':
                    program_name,
                    'fixed':
                    program.
                    fixed,  # True for ignore water level else program.fixed for use water level in run now-program xx
                    'cut_off':
                    0,
                    'manual':
                    True,
                    'blocked':
                    False,
                    'start':
                    interval['start'],
                    'original_start':
                    interval['start'],
                    'end':
                    interval['end'],
                    'uid':
                    '%s-%s-%d' %
                    (str(interval['start']), str(program_name), station),
                    'usage':
                    stations.get(station).usage
                }
                station_schedules[station].append(new_schedule)

    # Aggregate per station:
    for program in programs.get():
        if not program.enabled:
            continue

        for station in sorted(program.stations):
            program_intervals = program.active_intervals(
                start_time, end_time, station)
            if station >= stations.count(
            ) or stations.master == station or stations.master_two == station or not stations[
                    station].enabled:
                continue

            if station not in station_schedules:
                station_schedules[station] = []

            for interval in program_intervals:
                if current_active and current_active[-1][
                        'original_start'] > interval['start']:
                    continue

                new_schedule = {
                    'active':
                    None,
                    'program':
                    program.index,
                    'program_name':
                    program.name,  # Save it because programs can be renamed
                    'fixed':
                    program.fixed,
                    'cut_off':
                    program.cut_off / 100.0,
                    'manual':
                    program.manual,
                    'blocked':
                    False,
                    'start':
                    interval['start'],
                    'original_start':
                    interval['start'],
                    'end':
                    interval['end'],
                    'uid':
                    '%s-%d-%d' %
                    (str(interval['start']), program.index, station),
                    'usage':
                    stations.get(station).usage
                }
                station_schedules[station].append(new_schedule)

    # Make lists sorted on start time, check usage
    for station in station_schedules:
        if 0 < max_usage < stations.get(station).usage:
            station_schedules[station] = []  # Impossible to schedule
        else:
            station_schedules[station].sort(key=lambda inter: inter['start'])

    all_intervals = []
    # Adjust for weather and remove overlap:
    for station, schedule in station_schedules.iteritems():
        for interval in schedule:
            if not interval['fixed']:
                time_delta = interval['end'] - interval['start']
                time_delta = datetime.timedelta(
                    seconds=(time_delta.days * 24 * 3600 +
                             time_delta.seconds) * adjustment)
                interval['end'] = interval['start'] + time_delta
                interval['adjustment'] = adjustment
            else:
                interval['adjustment'] = 1.0

        last_end = datetime.datetime(2000, 1, 1)
        for interval in schedule:
            if last_end > interval['start']:
                time_delta = last_end - interval['start']
                interval['start'] += time_delta
                interval['end'] += time_delta
            last_end = interval['end']

            new_interval = {'station': station}
            new_interval.update(interval)

            all_intervals.append(new_interval)

    # Make list of entries sorted on duration and time (stable sorted on station #)
    all_intervals.sort(key=lambda inter: inter['end'] - inter['start'])
    all_intervals.sort(key=lambda inter: inter['start'])

    # If we have processed some intervals before, we should skip all that were scheduled before them
    for to_skip in skip_intervals:
        index = 0
        while index < len(all_intervals):
            interval = all_intervals[index]

            if interval['original_start'] < to_skip['original_start'] and (
                    not to_skip['blocked'] or interval['blocked']):
                del all_intervals[index]
            elif interval['uid'] == to_skip['uid']:
                del all_intervals[index]
                break
            else:
                index += 1

    # And make sure manual programs get priority:
    all_intervals.sort(key=lambda inter: not inter['manual'])

    # Try to add each interval
    for interval in all_intervals:
        if not interval['manual'] and not options.scheduler_enabled:
            interval['blocked'] = 'disabled scheduler'
            continue
        elif not interval['manual'] and not stations.get(interval['station']).ignore_rain and \
                rain_block_start <= interval['start'] < rain_block_end:
            interval['blocked'] = 'rain delay'
            continue
        elif not interval['manual'] and not stations.get(
                interval['station']).ignore_rain and inputs.rain_sensed():
            interval['blocked'] = 'rain sensor'
            continue
        elif not interval[
                'fixed'] and interval['adjustment'] < interval['cut_off']:
            interval['blocked'] = 'cut-off'
            continue

        if max_usage > 0:
            usage_keys = sorted(usage_changes.keys())
            start_usage = 0
            start_key_index = -1

            for index, key in enumerate(usage_keys):
                if key > interval['start']:
                    break
                start_key_index = index
                start_usage += usage_changes[key]

            failed = False
            finished = False
            while not failed and not finished:
                parallel_usage = 0
                parallel_current = 0
                for index in range(start_key_index + 1, len(usage_keys)):
                    key = usage_keys[index]
                    if key >= interval['end']:
                        break
                    parallel_current += usage_changes[key]
                    parallel_usage = max(parallel_usage, parallel_current)

                if start_usage + parallel_usage + interval[
                        'usage'] <= max_usage:

                    start = interval['start']
                    end = interval['end']
                    if start not in usage_changes:
                        usage_changes[start] = 0
                    if end not in usage_changes:
                        usage_changes[end] = 0

                    usage_changes[start] += interval['usage']
                    usage_changes[end] -= interval['usage']
                    finished = True
                else:
                    while not failed:
                        # Shift this interval to next possibility
                        start_key_index += 1

                        # No more options
                        if start_key_index >= len(usage_keys):
                            failed = True
                        else:
                            next_option = usage_keys[start_key_index]
                            next_change = usage_changes[next_option]
                            start_usage += next_change

                            # Lower usage at this starting point:
                            if next_change < 0:
                                skip_delay = False
                                if options.min_runtime > 0:
                                    # Try to determine how long we have been running at this point:
                                    min_runtime_delta = datetime.timedelta(
                                        seconds=options.min_runtime)
                                    temp_usage = 0
                                    running_since = next_option
                                    not_running_since = next_option
                                    for temp_index in range(
                                            0, start_key_index):
                                        temp_usage_key = usage_keys[temp_index]
                                        if temp_usage < 0.01 and usage_changes[
                                                temp_usage_key] > 0 and temp_usage_key - not_running_since > datetime.timedelta(
                                                    seconds=3):
                                            running_since = temp_usage_key
                                        temp_usage += usage_changes[
                                            temp_usage_key]
                                        if temp_usage < 0.01 and usage_changes[
                                                temp_usage_key] < 0:
                                            not_running_since = temp_usage_key
                                    if next_option - running_since < min_runtime_delta:
                                        skip_delay = True

                                if skip_delay:
                                    time_to_next = next_option - interval[
                                        'start']
                                else:
                                    time_to_next = next_option + delay_delta - interval[
                                        'start']

                                interval['start'] += time_to_next
                                interval['end'] += time_to_next
                                break

            if failed:
                logging.warning('Could not schedule %s.', interval['uid'])
                interval['blocked'] = 'scheduler error'

    all_intervals.sort(key=lambda inter: inter['start'])

    return all_intervals
Exemple #4
0
def predicted_schedule(start_time, end_time):
    """Determines all schedules for the given time range.
    To calculate what should currently be active, a start time of some time (a day) ago should be used."""

    adjustment = level_adjustments.total_adjustment()
    max_usage = options.max_usage
    delay_delta = datetime.timedelta(seconds=options.station_delay)

    rain_block_start = datetime.datetime.now()
    rain_block_end = rain_blocks.block_end()

    skip_intervals = log.finished_runs() + log.active_runs()
    current_active = [interval for interval in skip_intervals if not interval['blocked']]

    usage_changes = {}
    for active in current_active:
        start = active['start']
        end = active['end']
        if start not in usage_changes:
            usage_changes[start] = 0
        if end not in usage_changes:
            usage_changes[end] = 0

        usage_changes[start] += active['usage']
        usage_changes[end] -= active['usage']

    station_schedules = {}

    # Get run-once information:
    for station in stations.enabled_stations():
        run_once_intervals = run_once.active_intervals(start_time, end_time, station.index)
        for interval in run_once_intervals:
            if station.index not in station_schedules:
                station_schedules[station.index] = []

            new_schedule = {
                'active': None,
                'program': -1,
                'program_name': "Run-Once",
                'fixed': True,
                'cut_off': 0,
                'manual': True,
                'blocked': False,
                'start': interval['start'],
                'original_start': interval['start'],
                'end': interval['end'],
                'uid': '%s-%s-%d' % (str(interval['start']), "Run-Once", station.index),
                'usage': station.usage
            }
            station_schedules[station.index].append(new_schedule)

    # Get run-now information:
    if programs.run_now_program is not None:
        program = programs.run_now_program
        for station in sorted(program.stations):
            run_now_intervals = program.active_intervals(start_time, end_time, station)
            for interval in run_now_intervals:
                if station >= stations.count() or stations.master == station or not stations[station].enabled:
                    continue

                if station not in station_schedules:
                    station_schedules[station] = []

                program_name = "%s (Run-Now)" % program.name

                new_schedule = {
                    'active': None,
                    'program': -1,
                    'program_name': program_name,
                    'fixed': True,
                    'cut_off': 0,
                    'manual': True,
                    'blocked': False,
                    'start': interval['start'],
                    'original_start': interval['start'],
                    'end': interval['end'],
                    'uid': '%s-%s-%d' % (str(interval['start']), program_name, station),
                    'usage': stations.get(station).usage
                }
                station_schedules[station].append(new_schedule)

    # Aggregate per station:
    for program in programs.get():
        if not program.enabled:
            continue

        for station in sorted(program.stations):
            program_intervals = program.active_intervals(start_time, end_time, station)

            if station >= stations.count() or stations.master == station or not stations[station].enabled:
                continue

            if station not in station_schedules:
                station_schedules[station] = []

            for interval in program_intervals:
                if current_active and current_active[-1]['original_start'] > interval['start']:
                    continue

                new_schedule = {
                    'active': None,
                    'program': program.index,
                    'program_name': program.name, # Save it because programs can be renamed
                    'fixed': program.fixed,
                    'cut_off': program.cut_off/100.0,
                    'manual': program.manual,
                    'blocked': False,
                    'start': interval['start'],
                    'original_start': interval['start'],
                    'end': interval['end'],
                    'uid': '%s-%d-%d' % (str(interval['start']), program.index, station),
                    'usage': stations.get(station).usage
                }
                station_schedules[station].append(new_schedule)

    # Make lists sorted on start time, check usage
    for station in station_schedules:
        if 0 < max_usage < stations.get(station).usage:
            station_schedules[station] = []  # Impossible to schedule
        else:
            station_schedules[station].sort(key=lambda inter: inter['start'])

    all_intervals = []
    # Adjust for weather and remove overlap:
    for station, schedule in station_schedules.iteritems():
        for interval in schedule:
            if not interval['fixed']:
                time_delta = interval['end'] - interval['start']
                time_delta = datetime.timedelta(seconds=(time_delta.days * 24 * 3600 + time_delta.seconds) * adjustment)
                interval['end'] = interval['start'] + time_delta
                interval['adjustment'] = adjustment
            else:
                interval['adjustment'] = 1.0

        last_end = datetime.datetime(2000, 1, 1)
        for interval in schedule:
            if last_end > interval['start']:
                time_delta = last_end - interval['start']
                interval['start'] += time_delta
                interval['end'] += time_delta
            last_end = interval['end']

            new_interval = {
                'station': station
            }
            new_interval.update(interval)

            all_intervals.append(new_interval)

    # Make list of entries sorted on duration and time (stable sorted on station #)
    all_intervals.sort(key=lambda inter: inter['end'] - inter['start'])
    all_intervals.sort(key=lambda inter: inter['start'])

    # If we have processed some intervals before, we should skip all that were scheduled before them
    for to_skip in skip_intervals:
        index = 0
        while index < len(all_intervals):
            interval = all_intervals[index]

            if interval['original_start'] < to_skip['original_start'] and (not to_skip['blocked'] or interval['blocked']):
                del all_intervals[index]
            elif interval['uid'] == to_skip['uid']:
                del all_intervals[index]
                break
            else:
                index += 1

    # And make sure manual programs get priority:
    all_intervals.sort(key=lambda inter: not inter['manual'])

    # Try to add each interval
    for interval in all_intervals:
        if not interval['manual'] and not options.scheduler_enabled:
            interval['blocked'] = 'disabled scheduler'
            continue
        elif not interval['manual'] and not stations.get(interval['station']).ignore_rain and \
                rain_block_start <= interval['start'] < rain_block_end:
            interval['blocked'] = 'rain delay'
            continue
        elif not interval['manual'] and not stations.get(interval['station']).ignore_rain and inputs.rain_sensed():
            interval['blocked'] = 'rain sensor'
            continue
        elif not interval['fixed'] and interval['adjustment'] < interval['cut_off']:
            interval['blocked'] = 'cut-off'
            continue

        if max_usage > 0:
            usage_keys = sorted(usage_changes.keys())
            start_usage = 0
            start_key_index = -1

            for index, key in enumerate(usage_keys):
                if key > interval['start']:
                    break
                start_key_index = index
                start_usage += usage_changes[key]

            failed = False
            finished = False
            while not failed and not finished:
                parallel_usage = 0
                parallel_current = 0
                for index in range(start_key_index+1, len(usage_keys)):
                    key = usage_keys[index]
                    if key >= interval['end']:
                        break
                    parallel_current += usage_changes[key]
                    parallel_usage = max(parallel_usage, parallel_current)

                if start_usage + parallel_usage + interval['usage'] <= max_usage:

                    start = interval['start']
                    end = interval['end']
                    if start not in usage_changes:
                        usage_changes[start] = 0
                    if end not in usage_changes:
                        usage_changes[end] = 0

                    usage_changes[start] += interval['usage']
                    usage_changes[end] -= interval['usage']
                    finished = True
                else:
                    while not failed:
                        # Shift this interval to next possibility
                        start_key_index += 1

                        # No more options
                        if start_key_index >= len(usage_keys):
                            failed = True
                        else:
                            next_option = usage_keys[start_key_index]
                            next_change = usage_changes[next_option]
                            start_usage += next_change

                            # Lower usage at this starting point:
                            if next_change < 0:
                                skip_delay = False
                                if options.min_runtime > 0:
                                    # Try to determine how long we have been running at this point:
                                    min_runtime_delta = datetime.timedelta(seconds=options.min_runtime)
                                    temp_usage = 0
                                    running_since = next_option
                                    not_running_since = next_option
                                    for temp_index in range(0, start_key_index):
                                        temp_usage_key = usage_keys[temp_index]
                                        if temp_usage < 0.01 and usage_changes[temp_usage_key] > 0 and temp_usage_key - not_running_since > datetime.timedelta(seconds=3):
                                            running_since = temp_usage_key
                                        temp_usage += usage_changes[temp_usage_key]
                                        if temp_usage < 0.01 and usage_changes[temp_usage_key] < 0:
                                            not_running_since = temp_usage_key
                                    if next_option - running_since < min_runtime_delta:
                                        skip_delay = True

                                if skip_delay:
                                    time_to_next = next_option - interval['start']
                                else:
                                    time_to_next = next_option + delay_delta - interval['start']

                                interval['start'] += time_to_next
                                interval['end'] += time_to_next
                                break

            if failed:
                logging.warning('Could not schedule %s.', interval['uid'])
                interval['blocked'] = 'scheduler error'



    all_intervals.sort(key=lambda inter: inter['start'])

    return all_intervals
Exemple #5
0
    def run(self):
        weather.add_callback(self.update)
        self._sleep(10)  # Wait for weather callback before starting
        while not self._stop_event.is_set():
            try:
                log.clear(NAME)
                if plugin_options['enabled']:
                    log.debug(NAME, _(u'Checking weather status') + '...')

                    info = []
                    days = 0
                    total_info = {'rain_mm': 0.0}
                    for day in range(-plugin_options['days_history'],
                                     plugin_options['days_forecast'] + 1):
                        check_date = datetime.date.today(
                        ) + datetime.timedelta(days=day)
                        hourly_data = weather.get_hourly_data(check_date)
                        if hourly_data:
                            days += 1
                        info += hourly_data

                        total_info['rain_mm'] += weather.get_rain(check_date)

                    log.info(
                        NAME,
                        _(u'Using') + ' %d ' % days +
                        _(u'days of information.'))

                    total_info.update({
                        'temp_c':
                        sum([val['temperature'] for val in info]) / len(info),
                        'wind_ms':
                        sum([val['windSpeed'] for val in info]) / len(info),
                        'humidity':
                        sum([val['humidity'] for val in info]) / len(info)
                    })

                    # We assume that the default 100% provides 4mm water per day (normal need)
                    # We calculate what we will need to provide using the mean data of X days around today

                    water_needed = 4 * days  # 4mm per day
                    water_needed *= 1 + (total_info['temp_c'] -
                                         20) / 15  # 5 => 0%, 35 => 200%
                    water_needed *= 1 + (total_info['wind_ms'] / 100
                                         )  # 0 => 100%, 20 => 120%
                    water_needed *= 1 - (total_info['humidity'] -
                                         50) / 200  # 0 => 125%, 100 => 75%
                    water_needed = round(water_needed, 1)

                    water_left = water_needed - total_info['rain_mm']
                    water_left = round(max(0, min(100, water_left)), 1)

                    water_adjustment = round((water_left / (4 * days)) * 100,
                                             1)

                    water_adjustment = float(
                        max(plugin_options['wl_min'],
                            min(plugin_options['wl_max'], water_adjustment)))

                    log.info(
                        NAME,
                        _(u'Water needed') + ' %d ' % days + _('days') +
                        ': %.1fmm' % water_needed)
                    log.info(
                        NAME,
                        _(u'Total rainfall') +
                        ': %.1fmm' % total_info['rain_mm'])
                    log.info(NAME, u'_______________________________')
                    log.info(NAME,
                             _(u'Irrigation needed') + ': %.1fmm' % water_left)
                    log.info(
                        NAME,
                        _(u'Weather Adjustment') +
                        ': %.1f%%' % water_adjustment)

                    level_adjustments[NAME] = water_adjustment / 100

                    if plugin_options['protect_enabled']:
                        current_data = weather.get_current_data()
                        temp_local_unit = current_data[
                            'temperature'] if options.temp_unit == "C" else 32.0 + 9.0 / 5.0 * current_data[
                                'temperature']
                        log.debug(
                            NAME,
                            _(u'Temperature') + ': %.1f %s' %
                            (temp_local_unit, options.temp_unit))
                        month = time.localtime().tm_mon  # Current month.
                        if temp_local_unit < plugin_options[
                                'protect_temp'] and month in plugin_options[
                                    'protect_months']:
                            station_seconds = {}
                            for station in stations.enabled_stations():
                                if station.index in plugin_options[
                                        'protect_stations']:
                                    station_seconds[
                                        station.index] = plugin_options[
                                            'protect_minutes'] * 60
                                else:
                                    station_seconds[station.index] = 0

                            for station in stations.enabled_stations():
                                if run_once.is_active(datetime.datetime.now(),
                                                      station.index):
                                    break
                            else:
                                log.debug(NAME, _(u'Protection activated.'))
                                run_once.set(station_seconds)

                    self._sleep(3600)

                else:
                    log.clear(NAME)
                    log.info(NAME, _(u'Plug-in is disabled.'))
                    if NAME in level_adjustments:
                        del level_adjustments[NAME]
                    self._sleep(24 * 3600)

            except Exception:
                log.error(
                    NAME,
                    _(u'Weather-based water level plug-in') + ':\n' +
                    traceback.format_exc())
                self._sleep(3600)
        weather.remove_callback(self.update)
Exemple #6
0
    def run(self):
        weather.add_callback(self.update)
        self._sleep(10)  # Wait for weather callback before starting
        while not self._stop.is_set():
            try:
                log.clear(NAME)
                if plugin_options['enabled']:
                    log.debug(NAME, _('Checking weather status') + '...')

                    history = weather.get_wunderground_history(
                        plugin_options['days_history'])
                    forecast = weather.get_wunderground_forecast(
                        plugin_options['days_forecast'])
                    today = weather.get_wunderground_conditions()

                    info = {}

                    for day in range(-20, 20):
                        if day in history:
                            day_info = history[day]
                        elif day in forecast:
                            day_info = forecast[day]
                        else:
                            continue

                        info[day] = day_info

                    if 0 in info and 'rain_mm' in today:
                        day_time = datetime.datetime.now().time()
                        day_left = 1.0 - (day_time.hour * 60 +
                                          day_time.minute) / 24.0 / 60
                        info[0]['rain_mm'] = info[0][
                            'rain_mm'] * day_left + today['rain_mm']

                    if not info:
                        log.info(NAME, str(history))
                        log.info(NAME, str(today))
                        log.info(NAME, str(forecast))
                        raise Exception(_('No information available!'))

                    log.info(
                        NAME,
                        _('Using') + ' %d ' % len(info) +
                        _('days of information.'))

                    total_info = {
                        'temp_c':
                        sum([val['temp_c']
                             for val in info.values()]) / len(info),
                        'rain_mm':
                        sum([val['rain_mm'] for val in info.values()]),
                        'wind_ms':
                        sum([val['wind_ms']
                             for val in info.values()]) / len(info),
                        'humidity':
                        sum([val['humidity']
                             for val in info.values()]) / len(info)
                    }

                    # We assume that the default 100% provides 4mm water per day (normal need)
                    # We calculate what we will need to provide using the mean data of X days around today

                    water_needed = 4 * len(info)  # 4mm per day
                    water_needed *= 1 + (total_info['temp_c'] -
                                         20) / 15  # 5 => 0%, 35 => 200%
                    water_needed *= 1 + (total_info['wind_ms'] / 100
                                         )  # 0 => 100%, 20 => 120%
                    water_needed *= 1 - (total_info['humidity'] -
                                         50) / 200  # 0 => 125%, 100 => 75%
                    water_needed = round(water_needed, 1)

                    water_left = water_needed - total_info['rain_mm']
                    water_left = round(max(0, min(100, water_left)), 1)

                    water_adjustment = round(
                        (water_left / (4 * len(info))) * 100, 1)

                    water_adjustment = float(
                        max(plugin_options['wl_min'],
                            min(plugin_options['wl_max'], water_adjustment)))

                    log.info(
                        NAME,
                        _('Water needed') + ' %d ' % len(info) + _('days') +
                        ': %.1fmm' % water_needed)
                    log.info(
                        NAME,
                        _('Total rainfall') +
                        ': %.1fmm' % total_info['rain_mm'])
                    log.info(NAME, '_______________________________')
                    log.info(NAME,
                             _('Irrigation needed') + ': %.1fmm' % water_left)
                    log.info(
                        NAME,
                        _('Weather Adjustment') +
                        ': %.1f%%' % water_adjustment)

                    level_adjustments[NAME] = water_adjustment / 100

                    if plugin_options['protect_enabled']:
                        log.debug(
                            NAME,
                            _('Temperature') +
                            ': %s' % today['temperature_string'])
                        month = time.localtime().tm_mon  # Current month.
                        cold = (today['temp_c'] < plugin_options['protect_temp']) if options.temp_unit == "C" else \
                            (today['temp_f'] < plugin_options['protect_temp'])
                        if cold and month in plugin_options['protect_months']:
                            station_seconds = {}
                            for station in stations.enabled_stations():
                                if station.index in plugin_options[
                                        'protect_stations']:
                                    station_seconds[
                                        station.index] = plugin_options[
                                            'protect_minutes'] * 60
                                else:
                                    station_seconds[station.index] = 0

                            for station in stations.enabled_stations():
                                if run_once.is_active(datetime.datetime.now(),
                                                      station.index):
                                    break
                            else:
                                log.debug(NAME, _('Protection activated.'))
                                run_once.set(station_seconds)

                    self._sleep(3600)

                else:
                    log.clear(NAME)
                    log.info(NAME, _('Plug-in is disabled.'))
                    if NAME in level_adjustments:
                        del level_adjustments[NAME]
                    self._sleep(24 * 3600)

            except Exception:
                log.error(
                    NAME,
                    _('Weather-based water level plug-in') + ':\n' +
                    traceback.format_exc())
                self._sleep(3600)
        weather.remove_callback(self.update)