예제 #1
0
파일: scheduler.py 프로젝트: Rimco/OSPy
    def _check_schedule():
        current_time = datetime.datetime.now()
        check_start = current_time - datetime.timedelta(days=1)
        check_end = current_time + datetime.timedelta(days=1)

        rain = not options.manual_mode and (rain_blocks.block_end() > datetime.datetime.now() or
                                            inputs.rain_sensed())

        active = log.active_runs()
        for entry in active:
            ignore_rain = stations.get(entry['station']).ignore_rain
            if entry['end'] <= current_time or (rain and not ignore_rain and not entry['blocked'] and not entry['manual']):
                log.finish_run(entry)
                if not entry['blocked']:
                    stations.deactivate(entry['station'])

        if not options.manual_mode:
            schedule = predicted_schedule(check_start, check_end)
            #import pprint
            #logging.debug("Schedule: %s", pprint.pformat(schedule))
            for entry in schedule:
                if entry['start'] <= current_time < entry['end']:
                    log.start_run(entry)
                    if not entry['blocked']:
                        stations.activate(entry['station'])

        if stations.master is not None or options.master_relay:
            master_on = False

            # It's easy if we don't have to use delays:
            if options.master_on_delay == options.master_off_delay == 0:
                for entry in active:
                    if not entry['blocked'] and stations.get(entry['station']).activate_master:
                        master_on = True
                        break

            else:
                # In manual mode we cannot predict, we only know what is currently running and the history
                if options.manual_mode:
                    active = log.finished_runs() + active
                else:
                    active = combined_schedule(check_start, check_end)

                for entry in active:
                    if not entry['blocked'] and stations.get(entry['station']).activate_master:
                        if entry['start'] + datetime.timedelta(seconds=options.master_on_delay) \
                                <= current_time < \
                                entry['end'] + datetime.timedelta(seconds=options.master_off_delay):
                            master_on = True
                            break

            if stations.master is not None:
                master_station = stations.get(stations.master)

                if master_on != master_station.active:
                    master_station.active = master_on

            if options.master_relay:
                if master_on != outputs.relay_output:
                    outputs.relay_output = master_on
예제 #2
0
def get_station_is_on():  # return true if stations is ON
    if not options.manual_mode:  # if not manual control
        for station in stations.get():
            if station.active:  # if station is active
                return True
            else:
                return False
예제 #3
0
def get_master_is_on():
    if stations.master is not None or stations.master_two is not None and not options.manual_mode:  # if is use master station and not manual control
        for station in stations.get():
            if station.is_master or station.is_master_two:  # if station is master
                if station.active:  # if master is active
                    return True
                else:
                    return False
예제 #4
0
def station_names():
    """ Return station names as a list. """
    station_list = []

    for station in stations.get():
        station_list.append(station.name)

    return json.dumps(station_list)                            
예제 #5
0
def get_active_state():
    station_state = False
    station_result = ''
    for station in stations.get(): # check if station runing
      if station.active:
        station_state = True       # yes runing
        station_result += str(station.index+1) + ', ' # station number runing ex: 2, 6, 20,
    if station_state:
      return station_result
    else:
      return False
예제 #6
0
    def GET(self):
        statuslist = []
        epoch = datetime.date(1970, 1, 1)

        for station in stations.get():
            if station.enabled and any(station.index in program.stations for program in programs.get()):
                statuslist.append({
                    'station': station.name,
                    'balances': {int((key - epoch).total_seconds()): value for key, value in station.balance.iteritems()}})

        web.header('Content-Type', 'application/json')
        return json.dumps(statuslist, indent=2)
예제 #7
0
    def run(self):
        last_rain = False
        finished_count = len(
            [run for run in log.finished_runs() if not run['blocked']])

        if email_options[
                "emlpwron"]:  # if eml_power_on send email is enable (on)
            body = (datetime_string() + ': System was powered on.')

            if email_options["emllog"]:
                self.try_mail(body, EVENT_FILE)
            else:
                self.try_mail(body)

        while not self._stop_event.is_set():
            try:
                # Send E-amil if rain is detected
                if email_options["emlrain"]:
                    if inputs.rain_sensed() and not last_rain:
                        body = (datetime_string() + ': System detected rain.')
                        self.try_mail(body)
                    last_rain = inputs.rain_sensed()

                # Send E-mail if a new finished run is found
                if email_options["emlrun"]:
                    finished = [
                        run for run in log.finished_runs()
                        if not run['blocked']
                    ]
                    if len(finished) > finished_count:
                        body = datetime_string() + ':\n'
                        for run in finished[finished_count:]:
                            duration = (run['end'] -
                                        run['start']).total_seconds()
                            minutes, seconds = divmod(duration, 60)
                            body += "Finished run:\n"
                            body += "  Program: %s\n" % run['program_name']
                            body += "  Station: %s\n" % stations.get(
                                run['station']).name
                            body += "  Start time: %s \n" % datetime_string(
                                run['start'])
                            body += "  Duration: %02d:%02d\n\n" % (minutes,
                                                                   seconds)

                        self.try_mail(body)

                    finished_count = len(finished)

                self._sleep(5)

            except Exception:
                log.error(NAME, 'E-mail plug-in:\n' + traceback.format_exc())
                self._sleep(60)
예제 #8
0
파일: webpages.py 프로젝트: Rimco/OSPy
    def GET(self):
        statuslist = []
        epoch = datetime.date(1970, 1, 1)

        for station in stations.get():
            if station.enabled and any(station.index in program.stations for program in programs.get()):
                statuslist.append({
                    'station': station.name,
                    'balances': {int((key - epoch).total_seconds()): value for key, value in station.balance.iteritems()}})

        web.header('Content-Type', 'application/json')
        return json.dumps(statuslist, indent=2)
예제 #9
0
    def GET(self):
        statuslist = []
        for station in stations.get():
            if station.enabled or station.is_master or station.is_master_two:
                status = {
                    'station':
                    station.index,
                    'status':
                    'on' if station.active else 'off',
                    'reason':
                    'master'
                    if station.is_master or station.is_master_two else '',
                    'master':
                    1 if station.is_master else 0,
                    'master_two':
                    1 if station.is_master_two else 0,
                    'programName':
                    '',
                    'remaining':
                    0
                }

                if not station.is_master or not station.is_master_two:
                    if options.manual_mode:
                        status['programName'] = 'Manual Mode'
                    else:
                        if station.active:
                            active = log.active_runs()
                            for interval in active:
                                if not interval['blocked'] and interval[
                                        'station'] == station.index:
                                    status['programName'] = interval[
                                        'program_name']

                                    status['reason'] = 'program'
                                    status['remaining'] = max(
                                        0, (interval['end'] -
                                            datetime.datetime.now()
                                            ).total_seconds())
                        elif not options.scheduler_enabled:
                            status['reason'] = 'system_off'
                        elif not station.ignore_rain and inputs.rain_sensed():
                            status['reason'] = 'rain_sensed'
                        elif not station.ignore_rain and rain_blocks.seconds_left(
                        ):
                            status['reason'] = 'rain_delay'

                statuslist.append(status)

        web.header('Content-Type', 'application/json')
        return json.dumps(statuslist)
예제 #10
0
def notify_zone_change(name, **kw):
    if plugin_options['zoneChange']:
        txt =  _('There has been a Station Change.\n')
        for station in stations.get():
            txt += _('Station: {} State: ').format(station.name)
            if station.active:
                txt += _('ON') + u' ('
                if(station.remaining_seconds == -1):
                    txt += _('Forever') + _(u')')
                else:    
                    txt += '{}'.format(str(int(station.remaining_seconds))) + _(')')
            else:
                txt += _('OFF')
            txt += '\n'    
        sender._announce(txt)                   
예제 #11
0
    def run(self):
        # Activate outputs upon start if needed:
        current_time = datetime.datetime.now()
        rain = not options.manual_mode and (
            rain_blocks.block_end() > datetime.datetime.now()
            or inputs.rain_sensed())
        active = log.active_runs()
        for entry in active:
            ignore_rain = stations.get(entry['station']).ignore_rain
            if entry['end'] > current_time and (
                    not rain or ignore_rain) and not entry['blocked']:
                stations.activate(entry['station'])

        while True:
            self._check_schedule()
            time.sleep(1)
예제 #12
0
파일: scheduler.py 프로젝트: Rimco/OSPy
    def run(self):
        # Activate outputs upon start if needed:
        current_time = datetime.datetime.now()
        rain = not options.manual_mode and (rain_blocks.block_end() > datetime.datetime.now() or
                                            inputs.rain_sensed())
        active = log.active_runs()
        for entry in active:
            ignore_rain = stations.get(entry['station']).ignore_rain
            if entry['end'] > current_time and (not rain or ignore_rain) and not entry['blocked']:
                stations.activate(entry['station'])

        while True:
            try:
                self._check_schedule()
            except Exception:
                logging.warning('Scheduler error:\n' + traceback.format_exc())
            time.sleep(1)
예제 #13
0
def on_station_clear(name, **kw):
    """ Send all CMD to OFF when core program signals in station state."""
    log.clear(NAME)
    log.info(NAME, _(u'All station change to OFF'))
    for station in stations.get():
        command = plugin_options['off']
        data = command[station.index]
        if data:
            log.info(
                NAME,
                _(u'Im trying to send an OFF command: {}').format(
                    station.index + 1))
            run_command(data)
        else:
            log.info(
                NAME,
                _(u'No command is set for OFF: {}').format(station.index + 1))
    return
예제 #14
0
    def run(self):
        log.clear(NAME)
        log.info(NAME, 'Test started for ' + str(pulse_options['test_time']) + ' sec.')
        station = stations.get(pulse_options['test_output'])

        for x in range(0, pulse_options['test_time']):
            station.active = True
            time.sleep(0.5)
            station.active = False
            time.sleep(0.5)

            if self._stop_event.is_set():
                break

        log.info(NAME, 'Test stopped.')

        # Activate again if needed:
        if station.remaining_seconds != 0:
            station.active = True
예제 #15
0
파일: log.py 프로젝트: teodoryantcheff/OSPy
    def clear_runs(self, all_entries=True):
        from ospy.programs import programs, ProgramType
        from ospy.stations import stations
        if all_entries or not options.run_log:  # User request or logging is disabled
            minimum = 0
        elif options.run_entries > 0:
            minimum = options.run_entries
        else:
            return  # We should not prune in this case

        # determine the start of the first active run:
        first_start = min(
            [datetime.datetime.now()] +
            [interval['start'] for interval in self.active_runs()])
        min_eto = datetime.date.today() + datetime.timedelta(days=1)
        for program in programs.get():
            if program.type == ProgramType.WEEKLY_WEATHER:
                for station in program.stations:
                    min_eto = min(
                        min_eto,
                        min([
                            datetime.date.today() - datetime.timedelta(days=7)
                        ] + stations.get(station).balance.keys()))

        # Now try to remove as much as we can
        for index in reversed(xrange(len(self._log['Run']) - minimum)):
            interval = self._log['Run'][index]['data']

            delete = True
            # If this entry cannot have influence on the current state anymore:
            if (first_start - interval['end']).total_seconds() <= max(
                    options.station_delay + options.min_runtime,
                    options.master_off_delay, 60):
                delete = False
            elif interval['end'].date() >= min_eto:
                delete = False

            if delete:
                del self._log['Run'][index]

        self._save_logs()
예제 #16
0
def on_zone_change(name, **kw):
    """ Switch relays when core program signals a change in station state."""
    #   command = "wget http://xxx.xxx.xxx.xxx/relay1on"
    log.clear(NAME)
    log.info(NAME, _('Zone change signaled...'))

    for station in stations.get():
        if station.active:
            command = plugin_options['on']
            data = command[station.index]
            if data:
                #print 'data:', data
                run_command(data)
        else:
            command = plugin_options['off']
            data = command[station.index]
            if data:
                #print 'data:', data
                run_command(data)

    return
예제 #17
0
 def _botCmd_info(self, bot, update):
     chat_id = update.message.chat.id
     if chat_id in list(self._currentChats):
         txt =  _(u'Info from {}\n').format(options.name)
         for station in stations.get():
             txt += _(u'Station: {} State: ').format(station.name)
             if station.active:
                 txt += _(u'ON') + u' ('
                 if(station.remaining_seconds == -1):
                     txt += _(u'Forever') + u')'
                 else:    
                     txt += _(u'{}').format(str(int(station.remaining_seconds))) + u')'
             else:
                 txt += _(u'OFF')
             txt += '\n'    
         txt += _(u'Scheduler is {}.\n').format( _(u'enabled') if options.scheduler_enabled else _(u'disabled'))    
     else:
         txt = _(u'Sorry I can not do that.')
     if is_python2():
         bot.sendMessage(chat_id, text=txt.encode('utf-8'))
     else:    
         bot.sendMessage(chat_id, text=txt)
예제 #18
0
    def GET(self):
        statuslist = []
        for station in stations.get():
            if station.enabled or station.is_master or station.is_master_two: 
                status = {
                    'station': station.index,
                    'status': 'on' if station.active else 'off',
                    'reason': 'master' if station.is_master or station.is_master_two else '',
                    'master': 1 if station.is_master else 0,
                    'master_two': 1 if station.is_master_two else 0,
                    'programName': '',
                    'remaining': 0}

                if not station.is_master or not station.is_master_two:
                    if options.manual_mode:
                        status['programName'] = 'Manual Mode'
                    else:
                        if station.active:
                            active = log.active_runs()
                            for interval in active:
                                if not interval['blocked'] and interval['station'] == station.index:
                                    status['programName'] = interval['program_name']

                                    status['reason'] = 'program'
                                    status['remaining'] = max(0, (interval['end'] -
                                                                  datetime.datetime.now()).total_seconds())
                        elif not options.scheduler_enabled:
                            status['reason'] = 'system_off'
                        elif not station.ignore_rain and inputs.rain_sensed():
                            status['reason'] = 'rain_sensed'
                        elif not station.ignore_rain and rain_blocks.seconds_left():
                            status['reason'] = 'rain_delay'

                statuslist.append(status)

        web.header('Content-Type', 'application/json')
        return json.dumps(statuslist)
예제 #19
0
    def run(self):
        log.clear(NAME)
        log.info(NAME, datetime_string() + ' ' + _(u'Started for {} seconds.').format(plugin_options['open_time']))

        start = datetime.datetime.now()
        sid = int(plugin_options['open_output'])
        end = datetime.datetime.now() + datetime.timedelta(seconds=plugin_options['open_time'])
        new_schedule = {
            'active': True,
            'program': -1,
            'station': sid,
            'program_name': _(u'Door Opening'),
            'fixed': True,
            'cut_off': 0,
            'manual': True,
            'blocked': False,
            'start': start,
            'original_start': start,
            'end': end,
            'uid': '%s-%s-%d' % (str(start), "Manual", sid),
            'usage': stations.get(sid).usage
        }
        log.start_run(new_schedule)
        stations.activate(new_schedule['station'])
예제 #20
0
    def update_station_schedule(self):

        if self.type != ProgramType.WEEKLY_WEATHER:
            self._station_schedule = {}
            for station in self.stations:
                self._station_schedule[station] = self._schedule
        else:
            now = datetime.datetime.now()
            week_start = datetime.datetime.combine(
                now.date() - datetime.timedelta(days=now.weekday()),
                datetime.time.min)
            last_start = self._start
            self._start = week_start
            start_difference = int(
                round((week_start - last_start).total_seconds() / 60))
            irrigation_min, irrigation_max, run_max, pause_ratio, pem_mins = self.type_data

            try:
                pems = [(week_start + datetime.timedelta(minutes=x), y)
                        for x, y in pem_mins]
                pems += [(week_start + datetime.timedelta(days=7, minutes=x),
                          y) for x, y in pem_mins]
                pems += [(week_start + datetime.timedelta(days=-7, minutes=x),
                          y) for x, y in pem_mins]
                pems = sorted(pems)
                pems = [
                    x for x in pems
                    if x[0] >= now - datetime.timedelta(hours=1)
                ]
                pems = [
                    x for x in pems if (x[0].date() - now.date()).days < 10
                ]

                to_sprinkle = {}
                for station in self.stations:
                    to_sprinkle[station] = []
                    station_pems = pems[:]
                    # Make sure to keep whatever we were planning to do
                    if station in self._station_schedule:
                        for interval in self._station_schedule[station]:
                            if now - datetime.timedelta(
                                    hours=1) < last_start + datetime.timedelta(
                                        minutes=interval[1]
                                    ) and last_start + datetime.timedelta(
                                        minutes=interval[0]
                                    ) < now + datetime.timedelta(hours=1):
                                to_sprinkle[station].append([
                                    interval[0] + start_difference,
                                    interval[1] + start_difference
                                ])
                            elif to_sprinkle[
                                    station] and last_start + datetime.timedelta(
                                        minutes=interval[0]
                                    ) - (week_start + datetime.timedelta(
                                        minutes=to_sprinkle[station][-1][1])
                                         ) < datetime.timedelta(hours=3):
                                to_sprinkle[station].append([
                                    interval[0] + start_difference,
                                    interval[1] + start_difference
                                ])
                        if to_sprinkle[station]:
                            station_pems = [
                                x for x in pems
                                if x[0] > week_start + datetime.timedelta(
                                    minutes=to_sprinkle[station][-1][1])
                            ]

                    station_balance = {
                        -1:
                        stations.get(station).balance[now.date() -
                                                      datetime.timedelta(
                                                          days=1)]['total']
                    }
                    rain = {
                        -1:
                        stations.get(station).balance[now.date() -
                                                      datetime.timedelta(
                                                          days=1)]['rain']
                    }
                    for day_index in range(0, 10):
                        overall_balance = stations.get(station).balance[
                            now.date() + datetime.timedelta(days=day_index)]
                        station_balance[day_index] = station_balance[day_index-1] \
                                                     - overall_balance['eto'] \
                                                     + overall_balance['rain'] \
                                                     + sum(interval['irrigation'] for interval in overall_balance['intervals'] if
                                                           interval['done'] or interval['program'] != self.index)
                        station_balance[day_index] = max(
                            -100,
                            min(station_balance[day_index],
                                stations.get(station).capacity))
                        rain[day_index] = overall_balance['rain']

                    for index, (pem, prio) in enumerate(station_pems):
                        day_index = (pem.date() - now.date()).days
                        rain_today = max(rain[max(-1, day_index - 1)],
                                         rain[day_index],
                                         rain[min(day_index + 1, 9)])

                        better_days = [
                            x for x in station_pems[index + 1:] if x[1] > prio
                        ]
                        better_or_equal_days = [
                            x for x in station_pems[index + 1:]
                            if x[1] >= prio and x[0] > pem
                        ]
                        any_days = station_pems[index + 1:]

                        target_index, target_index_pref = 9, 9
                        if any_days:
                            target_index = (any_days[0][0].date() -
                                            now.date()).days

                        if not better_days:  # The best day:
                            amount = irrigation_max
                            if better_or_equal_days:
                                target_index_pref = (
                                    better_or_equal_days[0][0].date() -
                                    now.date()).days
                        else:  # A better day is possible:
                            amount = 0
                            target_index_pref = (better_days[0][0].date() -
                                                 now.date()).days

                        # Make sure not to overflow the capacity (and aim for 0 for today):
                        later_sprinkle_max = min(
                            [-station_balance[day_index]] + [
                                stations.get(station).capacity -
                                station_balance[later_day_index]
                                for later_day_index in range(
                                    day_index + 1, target_index_pref)
                            ])

                        # Make sure we sprinkle enough not to go above the maximum in the future:
                        later_sprinkle_min = max(
                            -station_balance[later_day_index] - irrigation_max
                            for later_day_index in range(
                                day_index, target_index + 1))
                        if later_sprinkle_min > 0:  # We need to do something to prevent going over the maximum, so:
                            later_sprinkle_min = max(
                                irrigation_min,
                                later_sprinkle_min)  # Make sure to sprinkle

                        # Try to go towards a better day:
                        later_sprinkle_min_pref = max(
                            -station_balance[later_day_index] - irrigation_max
                            for later_day_index in range(
                                day_index, target_index_pref + 1))
                        if later_sprinkle_min_pref > 0:  # We need to do something to prevent going over the maximum, so:
                            later_sprinkle_min_pref = max(
                                irrigation_min,
                                later_sprinkle_min)  # Make sure to sprinkle

                        # Calculate the final value based on the constraints that we have:
                        # print station, pem, amount, later_sprinkle_min, later_sprinkle_min_pref, later_sprinkle_max, irrigation_max-rain_today, irrigation_max, stations.get(station).capacity, [-station_balance[day_index]] + [(stations.get(station).capacity - station_balance[later_day_index]) for later_day_index in range(day_index+1, target_index_pref)]
                        amount = min(
                            max(
                                later_sprinkle_min,
                                min(max(later_sprinkle_min_pref,
                                        amount), later_sprinkle_max,
                                    irrigation_max - rain_today)),
                            irrigation_max)
                        if amount >= irrigation_min:
                            logging.debug(
                                'Weather based schedule for %s: PEM: %s, priority: %s, amount: %f.',
                                stations.get(station).name, str(pem), prio,
                                amount)
                            for later_day_index in range(day_index, 10):
                                station_balance[later_day_index] += amount
                            week_min = (pem - week_start).total_seconds() / 60

                            intervals = [amount]
                            while any(x > run_max for x in intervals):
                                new_len = len(intervals) + 1
                                intervals = [amount / new_len] * new_len

                            for interval in intervals:
                                station_duration = int(
                                    round(interval * 60 /
                                          stations.get(station).precipitation))
                                to_sprinkle[station] = self._update_schedule(
                                    to_sprinkle[station], self.modulo,
                                    week_min, week_min + station_duration)
                                week_min += station_duration + int(
                                    round(station_duration * pause_ratio))

                    logging.debug(
                        'Weather based deficit for %s: %s',
                        stations.get(station).name,
                        str(
                            sorted([((now.date() +
                                      datetime.timedelta(days=x)).isoformat(),
                                     y)
                                    for x, y in station_balance.iteritems()])))

                self._station_schedule = to_sprinkle
            except Exception:
                logging.warning('Could not create weather based schedule:\n' +
                                traceback.format_exc())
예제 #21
0
    def run(self):
        time.sleep(
            randint(3, 10)
        )  # Sleep some time to prevent printing before startup information

        send_interval = 10000 # default time for sending between e-mails (ms)
        last_millis   = 0     # timer for repeating sending e-mails (ms)
        last_rain = False
        body    = u''
        logtext = u''
        finished_count = len([run for run in log.finished_runs() if not run['blocked']])

        if email_options["emlpwron"]:  # if eml_power_on send email is enable (on)
            body += u'<b>' + _(u'System') + u'</b> ' + datetime_string()
            body += u'<br><p style="color:red;">' + _(u'System was powered on.') + u'</p>'
            logtext = _(u'System was powered on.')

            if email_options["emllog"]:
                file_exists = os.path.exists(EVENT_FILE)
                if file_exists:
                   try_mail(body, logtext, EVENT_FILE)
                else:
                   body += u'<br>' + _(u'Error -  events.log file not exists!')
                   try_mail(body, logtext)
            else:
                try_mail(body, logtext)

        while not self._stop_event.is_set():
            body    = u''
            logtext = u''
            try:
                # Send E-amil if rain is detected
                if email_options["emlrain"]:
                    if inputs.rain_sensed() and not last_rain:
                        body += u'<b>' + _(u'System') + u'</b> ' + datetime_string() 
                        body += u'<br><p style="color:red;">' + _(u'System detected rain.') + u'</p><br>'
                        logtext = _(u'System detected rain.')
                        try_mail(body, logtext)
                    last_rain = inputs.rain_sensed()

                # Send E-mail if a new finished run is found
                if email_options["emlrun"]:
                    finished = [run for run in log.finished_runs() if not run['blocked']]
                    if len(finished) > finished_count:
                        for run in finished[finished_count:]:
                            duration = (run['end'] - run['start']).total_seconds()
                            minutes, seconds = divmod(duration, 60)
                            pname = run['program_name']
                            sname = stations.get(run['station']).name
                            body += u'<br>'
                            body += u'<b>' + _(u'System') + u'</b> ' + datetime_string()
                            body += u'<br><b>'  + _(u'Finished run') + u'</b>'
                            body += u'<ul><li>' + _(u'Program') + u': %s \n' % pname + u'</li>'
                            body += u'<li>' + _(u'Station') + u': %s \n' % sname + u'</li>'
                            body += u'<li>' + _(u'Start time') + u': %s \n' % datetime_string(run['start']) + u'</li>'
                            body += u'<li>' + _(u'Duration') + u': %02d:%02d\n' % (minutes, seconds) + u'</li></ul>'
                            logtext  =  _(u'Finished run') + u'-> \n' + _(u'Program') + u': %s\n' % pname 
                            logtext +=  _(u'Station') + u': %s\n' % sname
                            logtext +=  _(u'Start time') + u': %s \n' % datetime_string(run['start'])
                            logtext +=  _(u'Duration') + u': %02d:%02d\n' % (minutes, seconds)

                            # Water Tank Monitor
                            try:
                                from plugins import tank_monitor

                                cm = tank_monitor.get_all_values()[0]
                                percent = tank_monitor.get_all_values()[1]
                                ping = tank_monitor.get_all_values()[2]
                                volume = tank_monitor.get_all_values()[3]
                                units = tank_monitor.get_all_values()[4]

                                msg = ' '
                                if cm > 0:
                                    msg =  _(u'Level') + u': ' + str(cm) + u' ' + _(u'cm')
                                    msg += u' (' + str(percent) + u' %), '
                                    msg += _(u'Ping') + u': ' + str(ping) + u' ' + _(u'cm')
                                    if units:
                                        msg += u', ' + _(u'Volume') + u': ' + str(volume) + u' ' + _(u'liters')
                                    else:
                                        msg += u', ' + _(u'Volume') + u': ' + str(volume) + u' ' + _(u'm3')
                                else: 
                                    msg = _(u'Error - I2C device not found!')

                                body += u'<b>'  + _(u'Water') + u'</b>'
                                body += u'<br><ul><li>' + _(u'Water level in tank') + u': %s \n</li></ul>' % (msg)
                                logtext += _(u'Water') + u'-> \n' + _(u'Water level in tank') + u': %s \n' % (msg)
                            
                            except ImportError:
                                log.debug(NAME, _(u'Cannot import plugin: tank monitor.'))
                                pass

                            # Water Consumption Counter
                            try:
                                from plugins import water_consumption_counter
                                self._sleep(2) # wait for the meter to save consumption

                                consum_from = water_consumption_counter.get_all_values()[0]
                                consum_one  = float(water_consumption_counter.get_all_values()[1])
                                consum_two  = float(water_consumption_counter.get_all_values()[2])

                                msg = u' '
                                msg +=  _(u'Measured from day') + u': ' + str(consum_from) + u', '
                                msg +=  _(u'Master Station') + u': '
                                if consum_one < 1000.0:
                                    msg += str(consum_one) + u' '
                                    msg += _(u'Liter') + u', '
                                else: 
                                    msg += str(round((consum_one/1000.0), 2)) + u' '
                                    msg += _(u'm3') + ', '

                                msg +=  _(u'Second Master Station') + u': '
                                if consum_two < 1000.0:
                                    msg += str(consum_two) + u' '
                                    msg += _(u'Liter') 
                                else:
                                    msg += str(round((consum_two/1000.0), 2)) + u' '
                                    msg += _(u'm3')

                                body += u'<br><b>'  + _(u'Water Consumption Counter') + u'</b>'
                                body += u'<br><ul><li>%s \n</li></ul>' % (msg)
                                logtext += _(u'Water Consumption Counter') + u': %s \n' % (msg)

                            except ImportError:
                                log.debug(NAME, _(u'Cannot import plugin: water consumption counter.'))
                                pass

                            # Air Temperature and Humidity Monitor
                            try:
                                from plugins import air_temp_humi

                                body += u'<br><b>' + _(u'Temperature DS1-DS6') + u'</b><ul>'
                                logtext += _(u'Temperature DS1-DS6') + u'-> \n'
                                for i in range(0, air_temp_humi.plugin_options['ds_used']):
                                    body += u'<li>' + u'%s' % air_temp_humi.plugin_options['label_ds%d' % i] + u': ' + u'%.1f \u2103' % air_temp_humi.DS18B20_read_probe(i) + u'\n</li>'  
                                    logtext += u'%s' % air_temp_humi.plugin_options['label_ds%d' % i] + u': ' + u'%.1f \u2103\n' % air_temp_humi.DS18B20_read_probe(i) 
                                body += u'</ul>'    

                            except ImportError:
                                log.debug(NAME, _(u'Cannot import plugin: air temp humi.'))
                                pass

                            # OSPy Sensors
                            try:
                                body += u'<br><b>' + _(u'Sensors') + u'</b>'
                                logtext += _(u'Sensors') + u'-> \n'
                                sensor_result = ''
                                if sensors.count() > 0:
                                    body += u'<ul>'
                                    for sensor in sensors.get():
                                        sensor_result = ''
                                        body += u'<li>'
                                        sensor_result += u'{}: '.format(sensor.name)
                                        if sensor.enabled:
                                            if sensor.response == 1:
                                                if sensor.sens_type == 1:                               # dry contact
                                                    if sensor.last_read_value[4] == 1:
                                                        sensor_result += _(u'Contact Closed')
                                                    elif sensor.last_read_value[4] == 0:
                                                        sensor_result += _(u'Contact Open')
                                                    else:
                                                        sensor_result += _(u'Probe Error')
                                                if sensor.sens_type == 2:                               # leak detector
                                                    if sensor.last_read_value[5] != -127:
                                                        sensor_result += str(sensor.last_read_value[5]) + ' ' + _(u'l/s')
                                                    else:
                                                        sensor_result += _(u'Probe Error')
                                                if sensor.sens_type == 3:                               # moisture
                                                    if sensor.last_read_value[6] != -127:
                                                        sensor_result += str(sensor.last_read_value[6]) + ' ' + _(u'%')
                                                    else:
                                                        sensor_result += _(u'Probe Error')
                                                if sensor.sens_type == 4:                               # motion
                                                    if sensor.last_read_value[7] != -127:
                                                        sensor_result += _(u'Motion Detected') if int(sensor.last_read_value[7]) == 1 else _(u'No Motion')
                                                    else:
                                                        sensor_result += _(u'Probe Error')
                                                if sensor.sens_type == 5:                               # temperature
                                                    if sensor.last_read_value[0] != -127:
                                                        sensor_result += u'%.1f \u2103' % sensor.last_read_value[0]
                                                    else:
                                                        sensor_result += _(u'Probe Error')
                                                if sensor.sens_type == 6:                               # multi sensor
                                                    if sensor.multi_type >= 0 and sensor.multi_type < 4:# multi temperature DS1-DS4
                                                        if sensor.last_read_value[sensor.multi_type] != -127: 
                                                            sensor_result += u'%.1f \u2103' % sensor.last_read_value[sensor.multi_type]
                                                        else:
                                                            sensor_result += _(u'Probe Error')
                                                    if sensor.multi_type == 4:                          #  multi dry contact
                                                        if sensor.last_read_value[4] != -127:
                                                            sensor_result += _(u'Contact Closed') if int(sensor.last_read_value[4]) == 1 else _(u'Contact Open')
                                                        else:
                                                            sensor_result += _(u'Probe Error')
                                                    if sensor.multi_type == 5:                          #  multi leak detector
                                                        if sensor.last_read_value[5] != -127:
                                                            sensor_result += str(sensor.last_read_value[5]) + ' ' + _(u'l/s')
                                                        else:
                                                            sensor_result += _(u'Probe Error')
                                                    if sensor.multi_type == 6:                          #  multi moisture
                                                        if sensor.last_read_value[6] != -127:
                                                            sensor_result += str(sensor.last_read_value[6]) + ' ' + _(u'%')
                                                        else:
                                                            sensor_result += _(u'Probe Error')
                                                    if sensor.multi_type == 7:                          #  multi motion
                                                        if sensor.last_read_value[7] != -127:
                                                            sensor_result += _(u'Motion Detected') if int(sensor.last_read_value[7])==1 else _(u'No Motion')
                                                        else:
                                                            sensor_result += _(u'Probe Error')
                                                    if sensor.multi_type == 8:                          #  multi ultrasonic
                                                        if sensor.last_read_value[8] != -127:
                                                            get_level = get_tank_cm(sensor.last_read_value[8], sensor.distance_bottom, sensor.distance_top)
                                                            get_perc = get_percent(get_level, sensor.distance_bottom, sensor.distance_top)
                                                            sensor_result += u'{} '.format(get_level) + _(u'cm') + u' ({} %)'.format(get_perc)
                                                        else:
                                                            sensor_result += _(u'Probe Error')
                                                    if sensor.multi_type == 9:                          #  multi soil moisture
                                                        err_check = 0
                                                        calculate_soil = [0.0]*16
                                                        state = [-127]*16
                                                        for i in range(0, 16):
                                                            if type(sensor.soil_last_read_value[i]) == float:
                                                                state[i] = sensor.soil_last_read_value[i]
                                                                ### voltage from probe to humidity 0-100% with calibration range (for footer info)
                                                                if sensor.soil_invert_probe_in[i]:                                  # probe: rotated state 0V=100%, 3V=0% humidity
                                                                    calculate_soil[i] = maping(state[i], float(sensor.soil_calibration_max[i]), float(sensor.soil_calibration_min[i]), 0.0, 100.0)
                                                                    calculate_soil[i] = round(calculate_soil[i], 1)                 # round to one decimal point
                                                                    calculate_soil[i] = 100.0 - calculate_soil[i]                   # ex: 90% - 90% = 10%, 10% is output in invert probe mode
                                                                else:                                                               # probe: normal state 0V=0%, 3V=100%
                                                                    calculate_soil[i] = maping(state[i], float(sensor.soil_calibration_min[i]), float(sensor.soil_calibration_max[i]), 0.0, 100.0)
                                                                    calculate_soil[i] = round(calculate_soil[i], 1)                 # round to one decimal point
                                                                if state[i] > 0.1:
                                                                    if sensor.soil_show_in_footer[i]:
                                                                        sensor_result += '{} {}% ({}V) '.format(sensor.soil_probe_label[i], round(calculate_soil[i], 2), round(state[i], 2))
                                                            else:
                                                                err_check += 1
                                                        if err_check > 15:
                                                            sensor_result += _(u'Probe Error')

                                                    if sensor.com_type == 0: # Wi-Fi/LAN
                                                        sensor_result += u' ' + _(u'Last Wi-Fi signal: {}%, Source: {}V.').format(sensor.rssi, sensor.last_battery)
                                                    if sensor.com_type == 1: # Radio
                                                        sensor_result += u' ' + _(u'Last Radio signal: {}%, Source: {}V.').format(sensor.rssi, sensor.last_battery)
                                            else:
                                                sensor_result += _(u'No response!')
                                        else:
                                            sensor_result += _(u'Disabled')

                                        body += sensor_result
                                        body += u'</li>'
                                        logtext += sensor_result
                                        logtext += u'\n'
                                    body += u'</ul>'
                                    body += u'<br>'
                                        
                                else:
                                    sensor_result += _(u'No sensors available')
                                    body += u'<ul><li>'
                                    body += sensor_result
                                    body += u'</li></ul>'
                                    body += u'<br>'
                                    logtext += sensor_result
                                    logtext += u'\n'
                                    
                            except:
                                log.debug(NAME, _(u'E-mail plug-in') + ':\n' + traceback.format_exc())
                                pass

                        try_mail(body, logtext)

                    finished_count = len(finished)

                ###Repeating sending e-mails###
                if email_options["emlrepeater"]:                        # saving e-mails is enabled 
                    try:
                        millis = int(round(time.time() * 1000))         # actual time in ms
                        if(millis - last_millis) > send_interval:       # sending timer
                            last_millis = millis                        # save actual time ms
                            try:                                        # exists file: saved_emails.json?
                                saved_emails = read_saved_emails()      # read from file                  
                            except:                                     # no! create empty file
                                write_email([])                         # create file
                                saved_emails = read_saved_emails()      # read from file 

                            len_saved_emails = len(saved_emails)
                            if len_saved_emails > 0:                    # if there is something in json
                                log.clear(NAME)
                                log.info(NAME, _(u'Unsent E-mails in queue (in file)') + ': ' + str(len_saved_emails))
                                try:                                    # try send e-mail
                                    sendtext = u'%s' % saved_emails[0]["text"]
                                    sendsubject = u'%s' % (saved_emails[0]["subject"] + '-' + _(u'sending from queue.'))
                                    sendattachment = u'%s' % saved_emails[0]["attachment"]
                                    email(sendtext, sendsubject, sendattachment) # send e-mail  
                                    send_interval = 10000               # repetition of 10 seconds
                                    del saved_emails[0]                 # delete sent email in file
                                    write_email(saved_emails)           # save to file after deleting an item
                                    if len(saved_emails) == 0:
                                        log.clear(NAME)
                                        log.info(NAME, _(u'All unsent E-mails in the queue have been sent.'))

                                except Exception:
                                    #print traceback.format_exc()
                                    send_interval = 60000               # repetition of 60 seconds   
                    except:
                        log.error(NAME, _(u'E-mail plug-in') + ':\n' + traceback.format_exc())  

                self._sleep(2)

            except Exception:
                log.error(NAME, _(u'E-mail plug-in') + ':\n' + traceback.format_exc())
                self._sleep(60)
예제 #22
0
def FTP_upload(self):
    try:
        # create a file "stavy.php"
        cas = time.strftime('%d.%m.%Y (%H:%M:%S)', time.localtime(time.time()))

        text = "<?php\r\n$cas = \""  # actual time (ex: cas = dd.mm.yyyy (HH:MM:SS)
        text += cas + "\";\r\n"
        text += "$rain = \'"  # rain state (ex: rain = 0/1)
        text += str(1 if inputs.rain_sensed() else 0) + "\';\r\n"
        text += "$output = \'"  # use xx outputs count (ex: output = 8)
        text += str(options.output_count) + "\';\r\n"
        text += "$program = \'"  # use xx programs count (ex: program = 3)
        text += str(programs.count()) + "\';\r\n"

        text += "$masterstat = "  # master stations index
        for station in stations.get():
            if station.is_master:
                text += "\'" + str(station.index) + "\';\r\n"
        if stations.master is None:
            text += "\'\';\r\n"

        text += "$raindel = "  # rain delay
        if rain_blocks.seconds_left():
            m, s = divmod(int(rain_blocks.seconds_left()), 60)
            h, m = divmod(m, 60)
            text += "\'" + "%dh:%02dm:%02ds" % (h, m, s) + "\';\r\n"
        else:
            text += "\'\';\r\n"

        import unicodedata  # for only asci text in PHP WEB (stations name, program name...)

        namestations = []
        for num in range(0, options.output_count):  # stations name as array
            namestations.append(
                unicodedata.normalize('NFKD',
                                      stations.get(num).name).encode(
                                          'ascii', 'ignore'))
        text += "$name" + " = array"
        text += str(namestations) + ";\r\n"

        statestations = []
        for num in range(0, options.output_count):  # stations state as array
            statestations.append(1 if stations.get(num).active else 0)
        text += "$state" + " = array"
        text += str(statestations) + ";\r\n"

        progrname = []
        for program in programs.get():  # program name as array
            progrname.append(
                unicodedata.normalize('NFKD',
                                      program.name).encode('ascii', 'ignore'))
        text += "$progname" + " = array"
        text += str(progrname) + ";\r\n"

        text = text.replace('[', '(')
        text = text.replace(']', ')')

        text += "$schedul = \'"  # scheduller state (ex: schedul = 0 manual/ 1 scheduler)
        text += str(0 if options.manual_mode else 1) + "\';\r\n"
        text += "$system = \'"  # scheduller state (ex: system = 0 stop/ 1 scheduler)
        text += str(0 if options.scheduler_enabled else 1) + "\';\r\n"
        text += "$cpu = \'"  # cpu temp (ex: cpu  = 45.2)
        text += str(helpers.get_cpu_temp(options.temp_unit)) + "\';\r\n"
        text += "$unit = \'"  # cpu temp unit(ex: unit  = C/F) in Celsius or Fahrenheit
        text += str(options.temp_unit) + "\';\r\n"
        text += "$ver = \'"  # software version date (ex: ver  = 2016-07-30)
        text += str(version.ver_date) + "\';\r\n"
        text += "$id = \'"  # software OSPy ID (ex: id  = "opensprinkler")
        text += str(options.name) + "\';\r\n"
        text += "$ip = \'"  # OSPy IP address (ex: ip  = "192.168.1.1")
        text += str(helpers.get_ip()) + "\';\r\n"
        text += "$port = \'"  # OSPy port (ex: port  = "8080")
        text += str(options.web_port) + "\';\r\n"
        text += "$ssl = \'"  # OSPy use ssl port (ex: ssl  = "1" or "0")
        text += str(1 if options.use_ssl else 0) + "\';\r\n"

        tank = ''
        try:
            from plugins import tank_humi_monitor
            tank = tank_humi_monitor.get_sonic_tank_cm()
            if tank < 0:  # -1 is error I2C device for ping not found in tank_humi_monitor
                tank = ''
        except Exception:
            tank = ''

        text = text + "$tank = \'"  # from tank humi monitor plugins check water level (ex: tank  = "100" in cm or "")
        text = text + str(tank) + "\';\r\n"

        press = ''
        try:
            from plugins import pressure_monitor
            press = pressure_monitor.get_check_pressure()
        except Exception:
            press = ''

        text = text + "$press = \'"  # from pressure plugins check press (ex: press  = "1" or "0")
        text = text + str(press) + "\';\r\n"

        ups = ''
        try:
            from plugins import ups_adj
            ups = ups_adj.get_check_power(
            )  # read state power line from plugin ups adj
        except Exception:
            ups = ''

        text = text + "$ups = \'"
        text = text + str(ups) + "\';\r\n"

        result = ''
        finished = [run for run in log.finished_runs() if not run['blocked']]
        if finished:
            result = finished[-1]['start'].strftime(
                '%d-%m-%Y v %H:%M:%S program: ') + finished[-1]['program_name']
        else:
            result = ''

        text = text + "$lastrun = \'"  # last run program (ex: start d-m-r v h:m:s program: jmemo programu)
        text = text + str(result) + "\';\r\n"

        text = text + "$up = \'"  # system run uptime
        text = text + str(helpers.uptime()) + "\';\r\n"

        text = text + "?>\r\n"
        #print text
        """ example php code -----------
      <?php
      $cas = "09.08.2016 (15:28:10)";
      $rain = '0';
      $output = '3';
      $program = '3';
      $masterstat = '0';
      $raindel = '1:26:22'; // hod:min:sec
      $name = array('cerpadlo','test','out2');
      $state = array('0', '0', '0');
      $progname = array('cerpadlo v 5','test','out2');   
      $schedul = '1';
      $system = '1';
      $cpu = '40.1';
      $unit = 'C';
      $ver = '2016-07-30';
      $id = 'Zalevac chata';
      $ip = '192.168.1.253';
      $port = '80';
      $ssl = '1';
      $tank = '20';
      $press = '0';
      $ups = '1';
      $lastrun = '09.08.2016 v 15:28:10 program: bezi 5 minut)';
      $up = '6 days, 0:48:01'
      ?>
      ------------------------------- """

        try:
            fs = file("/home/pi/ramdisk/stavy.php", 'w')
            fs.write(text)
            fs.close()

        except:
            log.error(NAME, _(u'Could not save stavy.php to ramdisk!'))
            pass

        self.ftp.storbinary("STOR " + plugin_options['loc'] + 'stavy.php',
                            open("/home/pi/ramdisk/stavy.php", 'rb'))
        log.info(
            NAME,
            _(u'Data file stavy.php has send on to FTP server') + ': ' +
            str(cas))

        self.ftp.storbinary("STOR " + plugin_options['loc'] + 'data.txt',
                            open("/home/pi/ramdisk/data.txt", 'rb'))
        log.info(
            NAME,
            _(u'Data file data.txt has send on to FTP server') + ': ' +
            str(cas))

        self.ftp.close  # FTP end

    except Exception:
        log.info(
            NAME,
            _(u'Remote FTP control settings') + ':\n' + traceback.format_exc())
예제 #23
0
파일: webpages.py 프로젝트: Rimco/OSPy
    def GET(self):
        from ospy import server
        qdict = web.input()

        stop_all = get_input(qdict, 'stop_all', False, lambda x: True)
        scheduler_enabled = get_input(qdict, 'scheduler_enabled', None, lambda x: x == '1')
        manual_mode = get_input(qdict, 'manual_mode', None, lambda x: x == '1')
        rain_block = get_input(qdict, 'rain_block', None, float)
        level_adjustment = get_input(qdict, 'level_adjustment', None, float)
        toggle_temp = get_input(qdict, 'toggle_temp', False, lambda x: True)

        if stop_all:
            if not options.manual_mode:
                options.scheduler_enabled = False
                programs.run_now_program = None
                run_once.clear()
            log.finish_run(None)
            stations.clear()

        if scheduler_enabled is not None:
            options.scheduler_enabled = scheduler_enabled

        if manual_mode is not None:
            options.manual_mode = manual_mode

        if rain_block is not None:
            options.rain_block = datetime.datetime.now() + datetime.timedelta(hours=rain_block)

        if level_adjustment is not None:
            options.level_adjustment = level_adjustment / 100

        if toggle_temp:
            options.temp_unit = "F" if options.temp_unit == "C" else "C"

        set_to = get_input(qdict, 'set_to', None, int)
        sid = get_input(qdict, 'sid', 0, int) - 1
        set_time = get_input(qdict, 'set_time', 0, int)

        if set_to is not None and 0 <= sid < stations.count() and options.manual_mode:
            if set_to:  # if status is on
                start = datetime.datetime.now()
                new_schedule = {
                    'active': True,
                    'program': -1,
                    'station': sid,
                    'program_name': "Manual",
                    'fixed': True,
                    'cut_off': 0,
                    'manual': True,
                    'blocked': False,
                    'start': start,
                    'original_start': start,
                    'end': start + datetime.timedelta(days=3650),
                    'uid': '%s-%s-%d' % (str(start), "Manual", sid),
                    'usage': stations.get(sid).usage
                }
                if set_time > 0:  # if an optional duration time is given
                    new_schedule['end'] = datetime.datetime.now() + datetime.timedelta(seconds=set_time)

                log.start_run(new_schedule)
                stations.activate(new_schedule['station'])

            else:  # If status is off
                stations.deactivate(sid)
                active = log.active_runs()
                for interval in active:
                    if interval['station'] == sid:
                        log.finish_run(interval)

        self._redirect_back()
예제 #24
0
def stop_onrain():
    """Stop stations that do not ignore rain."""
    from ospy.stations import stations
    for station in stations.get():
        if not station.ignore_rain:
            station.activated = False
예제 #25
0
    def run(self):
       old_statuslist = ' '
       log.clear(NAME) 
       log.info(NAME, _('MQTT Zones Plugin started.'))
       once = True

       while not self._stop.is_set(): 
          if plugin_options['use_mqtt']:  
            once = True
            try:                    
               statuslist = []

               for station in stations.get():
                   if station.enabled or station.is_master or station.is_master_two: 
                      status = {
                        'station': station.index,
                        'status':  'on' if station.active else 'off',
                        'name':    station.name,
                        'reason':  'master' if station.is_master or station.is_master_two else ''}

                      if not station.is_master or not station.is_master_two:
                           if station.active:
                              active = log.active_runs()
                              for interval in active:
                                 if not interval['blocked'] and interval['station'] == station.index:
                                    status['reason'] = 'program'                              
                           elif not options.scheduler_enabled:
                              status['reason'] = 'system_off'
                           elif not station.ignore_rain and inputs.rain_sensed():
                              status['reason'] = 'rain_sensed'
                           elif not station.ignore_rain and rain_blocks.seconds_left():
                              status['reason'] = 'rain_delay'

                      statuslist.append(status)
               

               zone_topic = plugin_options['zone_topic']
               
               if zone_topic:
                  try:
                     from plugins import mqtt
                  except ImportError:
                     log.error(NAME, _('MQTT Zones Plugin requires MQTT plugin.'))

                  if statuslist != old_statuslist:
                     old_statuslist = statuslist
                    
                     client = mqtt.get_client()
                     if client:           
                       client.publish(zone_topic, json.dumps(statuslist), qos=1, retain=True)
                       log.clear(NAME) 
                       log.info(NAME, _('MQTT Zones Plugin public') + ':\n' + str(statuslist))

               else:
                  log.clear(NAME) 
                  log.error(NAME, _('Not setup Zone Topic'))
                  self._sleep(10)
                        
               self._sleep(1)         

            except Exception:
                log.error(NAME, _('MQTT Zones plug-in') + ':\n' + traceback.format_exc())
                self._sleep(60)

          else:
             if once: 
                log.info(NAME, _('MQTT Zones Plugin is disabled.'))
                once = False
예제 #26
0
파일: log.py 프로젝트: Rimco/OSPy
    def clear_runs(self, all_entries=True):
        from ospy.programs import programs, ProgramType
        from ospy.stations import stations
        if all_entries or not options.run_log:  # User request or logging is disabled
            minimum = 0
        elif options.run_entries > 0:
            minimum = options.run_entries
        else:
            return  # We should not prune in this case

        # determine the start of the first active run:
        first_start = min([datetime.datetime.now()] + [interval['start'] for interval in self.active_runs()])
        min_eto = datetime.date.today() + datetime.timedelta(days=1)
        for program in programs.get():
            if program.type == ProgramType.WEEKLY_WEATHER:
                for station in program.stations:
                    min_eto = min(min_eto, min([datetime.date.today() - datetime.timedelta(days=7)] + stations.get(station).balance.keys()))

        # Now try to remove as much as we can
        for index in reversed(xrange(len(self._log['Run']) - minimum)):
            interval = self._log['Run'][index]['data']

            delete = True
            # If this entry cannot have influence on the current state anymore:
            if (first_start - interval['end']).total_seconds() <= max(options.station_delay + options.min_runtime,
                                                                      options.master_off_delay,
                                                                      60):
                delete = False
            elif interval['end'].date() >= min_eto:
                delete = False

            if delete:
                del self._log['Run'][index]

        self._save_logs()
예제 #27
0
    def run(self):
        Temperature = 0
        Humidity = 0

        last_millis = 0  # timer for save log
        var1 = True  # Auxiliary variable for once on
        var2 = True  # Auxiliary variable for once off

        air_temp = None

        if plugin_options['use_footer']:
            air_temp = showInFooter(
            )  #  instantiate class to enable data in footer
            air_temp.button = "air_temp_humi/settings"  # button redirect on footer
            air_temp.label = _(u'Temperature')  # label on footer

        regulation_text = ''

        #flow = showOnTimeline()  #  instantiate class to enable data display
        #flow.unit = _(u'Liters')
        #flow.val = 10
        #flow.clear

        while not self._stop_event.is_set():
            try:
                if plugin_options['enabled']:  # if plugin is enabled
                    log.clear(NAME)
                    log.info(NAME, datetime_string())
                    tempText = ""

                    if plugin_options[
                            'enable_dht']:  # if DHTxx probe is enabled
                        try:
                            result = 1  # 1=ERR_MISSING_DATA
                            if plugin_options['dht_type'] == 0:  # DHT11
                                result = instance.read()
                            if plugin_options['dht_type'] == 1:  # DHT22
                                result = instance22.read()

                            if result.is_valid(
                            ):  # 0=ERR_NO_ERROR, 1=ERR_MISSING_DATA, 2=ERR_CRC
                                Temperature = result.temperature
                                Humidity = result.humidity

                                global tempDHT, humiDHT

                                tempDHT = Temperature
                                humiDHT = Humidity

                        except:
                            log.clear(NAME)
                            log.info(NAME, datetime_string())
                            if plugin_options['dht_type'] == 0:  # DHT11
                                log.info(NAME, _(u'DHT11 data is not valid'))
                                tempText += ' ' + _(u'DHT11 data is not valid')
                            if plugin_options['dht_type'] == 1:  # DHT22
                                log.info(NAME, _(u'DHT22 data is not valid'))
                                tempText += ' ' + _(u'DHT22 data is not valid')

                        if Humidity and Temperature != 0:
                            self.status['temp'] = Temperature
                            self.status['humi'] = Humidity
                            log.info(
                                NAME,
                                _(u'Temperature') + ' ' + _(u'DHT') + ': ' +
                                u'%.1f \u2103' % Temperature)
                            log.info(
                                NAME,
                                _(u'Humidity') + ' ' + _(u'DHT') + ': ' +
                                u'%.1f' % Humidity + ' %RH')
                            tempText += ' ' + _(
                                u'DHT'
                            ) + ': ' + u'%.1f \u2103' % Temperature + ' ' + u'%.1f' % Humidity + ' %RH' + ' '

                            if plugin_options['enabled_reg']:
                                log.info(NAME, regulation_text)

                            station = stations.get(
                                plugin_options['control_output'])

                            if plugin_options[
                                    'enabled_reg']:  # if regulation is enabled
                                if Humidity > (plugin_options['humidity_on'] +
                                               plugin_options['hysteresis'] /
                                               2) and var1 is True:
                                    start = datetime.datetime.now()
                                    sid = station.index
                                    end = datetime.datetime.now(
                                    ) + datetime.timedelta(
                                        seconds=plugin_options['reg_ss'],
                                        minutes=plugin_options['reg_mm'])
                                    new_schedule = {
                                        'active':
                                        True,
                                        'program':
                                        -1,
                                        'station':
                                        sid,
                                        'program_name':
                                        _('Air Temperature'),
                                        'fixed':
                                        True,
                                        'cut_off':
                                        0,
                                        'manual':
                                        True,
                                        'blocked':
                                        False,
                                        'start':
                                        start,
                                        'original_start':
                                        start,
                                        'end':
                                        end,
                                        'uid':
                                        '%s-%s-%d' %
                                        (str(start), "Manual", sid),
                                        'usage':
                                        stations.get(sid).usage
                                    }

                                    log.start_run(new_schedule)
                                    stations.activate(new_schedule['station'])
                                    var1 = False
                                    var2 = True
                                    self.status['outp'] = 1
                                    regulation_text = datetime_string(
                                    ) + ' ' + _(
                                        u'Regulation set ON.'
                                    ) + ' ' + ' (' + _('Output') + ' ' + str(
                                        station.index + 1) + ').'
                                    update_log(self.status)

                                if Humidity < (plugin_options['humidity_off'] -
                                               plugin_options['hysteresis'] /
                                               2) and var2 is True:
                                    sid = station.index
                                    stations.deactivate(sid)
                                    active = log.active_runs()
                                    for interval in active:
                                        if interval['station'] == sid:
                                            log.finish_run(interval)
                                    var1 = True
                                    var2 = False
                                    self.status['outp'] = 0
                                    regulation_text = datetime_string(
                                    ) + ' ' + _(
                                        u'Regulation set OFF.'
                                    ) + ' ' + ' (' + _('Output') + ' ' + str(
                                        station.index + 1) + ').'
                                    update_log(self.status)

                    if plugin_options[
                            'ds_enabled']:  # if in plugin is enabled DS18B20
                        DS18B20_read_data(
                        )  # get read DS18B20 temperature data to global tempDS[xx]
                        tempText += _(u'DS') + ': '
                        for i in range(0, plugin_options['ds_used']):
                            self.status['DS%d' % i] = tempDS[i]
                            log.info(
                                NAME,
                                _(u'Temperature') + ' ' + _(u'DS') +
                                str(i + 1) + ' (' +
                                u'%s' % plugin_options['label_ds%d' % i] +
                                '): ' +
                                u'%.1f \u2103' % self.status['DS%d' % i])
                            tempText += u' %s' % plugin_options[
                                'label_ds%d' %
                                i] + u' %.1f \u2103' % self.status['DS%d' % i]

                    if plugin_options['enabled_reg']:
                        tempText += ' ' + regulation_text

                    if plugin_options['use_footer']:
                        if air_temp is not None:
                            if is_python2():
                                air_temp.val = tempText  # value on footer
                            else:
                                air_temp.val = tempText.encode('utf8').decode(
                                    'utf8')  # value on footer

                    if plugin_options['enable_log']:  # enabled logging
                        millis = int(round(time.time() * 1000))
                        interval = (plugin_options['log_interval'] * 60000)
                        if (millis - last_millis) > interval:
                            last_millis = millis
                            update_log(self.status)

                self._sleep(5)

            except Exception:
                log.error(
                    NAME,
                    _(u'Air Temperature and Humidity Monitor plug-in') +
                    ':\n' + traceback.format_exc())
                self._sleep(60)
예제 #28
0
    def run(self):
        send_msg = False  # send get data if change (rain, end program ....
        last_rain = False
        en_rain = True
        en_line = True
        en_line2 = True

        rain = 0  # rain status for rain=0 or rain=1 in get data
        lastrun = ""  # date and time for lastrun=xxxxx in get data
        tank = ""  # actual level cm in water tank
        percent = ""  # actual level % in water tank
        ping = ""  # actual level ping cm water level
        volume = ""  # actual level volume in m3 water level
        duration = ""  # duration in last program for duration=xx:yy in get data
        station = ""  # name end station for station=abcde in get data
        humi = ""  # humidity in station for humi=xx in get data
        line = ""  # actual state from UPS plugin for line=0 or line=1 in get data
        temp1 = ""  # temperature 1 from air temp plugin DS18B20
        temp2 = ""  # temperature 2 from air temp plugin DS18B20
        temp3 = ""  # temperature 3 from air temp plugin DS18B20
        temp4 = ""  # temperature 4 from air temp plugin DS18B20
        temp5 = ""  # temperature 5 from air temp plugin DS18B20
        temp6 = ""  # temperature 6 from air temp plugin DS18B20
        tempDHT = ""  # temperature  from air temp plugin DHT probe
        humiDHT = ""  # humidity from air temp plugin DHT probe

        finished_count = len(
            [run for run in log.finished_runs() if not run['blocked']])

        while not self._stop_event.is_set():
            try:

                # Send data if rain detected, power line state a new finished run is found
                if remote_options["use"]:
                    ### water tank level ###
                    try:
                        from plugins import tank_monitor
                        tank = tank_monitor.get_all_values()[0]
                        percent = tank_monitor.get_all_values()[1]
                        ping = tank_monitor.get_all_values()[2]
                        volume = tank_monitor.get_all_values()[3]

                    except Exception:
                        tank = ""
                        percent = ""
                        ping = ""
                        volume = ""

                    ### power line state ###
                    try:
                        from plugins import ups_adj
                        lin = ups_adj.get_check_power(
                        )  # read state power line from plugin
                        if lin == 1:
                            if en_line:  # send only if change
                                send_msg = True
                                en_line = False
                                en_line2 = True
                            line = 0  # no power on web

                        if lin == 0:
                            if en_line2:  # send only if change
                                send_msg = True
                                en_line2 = False
                                en_line = True
                            line = 1  # power ok on web

                    except Exception:
                        line = ""

                    ### rain state ###
                    if inputs.rain_sensed() and not last_rain:
                        send_msg = True
                        last_rain = inputs.rain_sensed()
                    if inputs.rain_sensed():
                        rain = 1
                        en_rain = True
                    else:
                        rain = 0
                        if en_rain:  # send data if no rain (only if change rain/norain...)
                            send_msg = True
                            en_rain = False

                    if not options.rain_sensor_enabled:  # if rain sensor not used
                        rain = ""

                    ### program and station ###
                    finished = [
                        run for run in log.finished_runs()
                        if not run['blocked']
                    ]
                    if len(finished) > finished_count:
                        las = datetime_string()
                        lastrun = re.sub(
                            " ", "_", las)  # eliminate gap in the title to _
                        send_msg = True
                        ### humidity in station ###
                        try:
                            from plugins import humi_monitor
                            humi = int(
                                humi_monitor.get_humidity(
                                    (stations.get(run['station']).index) +
                                    1))  # 0-7 to 1-8 humidity
                            if humi < 0:
                                humi = ""

                        except Exception:
                            humi = ""

                        ### temperature ###
                        try:
                            from plugins import air_temp_humi
                            temp1 = air_temp_humi.DS18B20_read_probe(0)
                            temp2 = air_temp_humi.DS18B20_read_probe(1)
                            temp3 = air_temp_humi.DS18B20_read_probe(2)
                            temp4 = air_temp_humi.DS18B20_read_probe(3)
                            temp5 = air_temp_humi.DS18B20_read_probe(4)
                            temp6 = air_temp_humi.DS18B20_read_probe(5)
                            tempDHT = air_temp_humi.DHT_read_temp_value()
                            humiDHT = air_temp_humi.DHT_read_humi_value()
                        except Exception:
                            temp1 = ""
                            temp2 = ""
                            temp3 = ""
                            temp4 = ""
                            temp5 = ""
                            temp6 = ""
                            tempDHT = ""
                            humiDHT = ""

                        for run in finished[finished_count:]:
                            dur = (run['end'] - run['start']).total_seconds()
                            minutes, seconds = divmod(dur, 60)
                            sta = "%s" % stations.get(run['station']).name
                            station = re.sub(
                                " ", "_",
                                sta)  # eliminate gap in the title to _
                            duration = "%02d:%02d" % (minutes, seconds)

                    finished_count = len(finished)

                if (send_msg):  # if enabled send data
                    body = ('tank=' + str(tank))
                    body += ('&percent=' + str(percent))
                    body += ('&ping=' + str(ping))
                    body += ('&volume=' + str(volume))
                    body += ('&rain=' + str(rain))
                    body += ('&humi=' + str(humi))
                    body += ('&line=' + str(line))
                    body += ('&lastrun=' + str(lastrun))
                    body += ('&station=' + sanity_msg(station))
                    body += ('&duration=' + str(duration))
                    body += ('&temp1=' + str(temp1))
                    body += ('&temp2=' + str(temp2))
                    body += ('&temp3=' + str(temp3))
                    body += ('&temp4=' + str(temp4))
                    body += ('&temp5=' + str(temp5))
                    body += ('&temp6=' + str(temp6))
                    body += ('&tempDHT=' + str(tempDHT))
                    body += ('&humiDHT=' + str(humiDHT))
                    body += ('&api=' + remote_options['api'])  # API password
                    log.clear(NAME)
                    log.info(NAME, _(u'Test data...'))
                    self.try_send(body)  # Send GET data to remote server
                    send_msg = False  # Disable send data

                self._sleep(2)

            except Exception:
                log.error(
                    NAME,
                    _(u'Remote plug-in') + ':\n' + traceback.format_exc())
                self._sleep(60)
예제 #29
0
    def run(self):
        last_rain = False
        finished_count = len(
            [run for run in log.finished_runs() if not run['blocked']])

        if email_options[
                "emlpwron"]:  # if eml_power_on send email is enable (on)
            body = (datetime_string() + ': ' + _('System was powered on.'))

            if email_options["emllog"]:
                file_exists = os.path.exists(EVENT_FILE)
                if file_exists:
                    self.try_mail(body, EVENT_FILE)
                else:
                    body += '\n' + _('Error -  events.log file not exists!')
                    print body
                    self.try_mail(body)
            else:
                self.try_mail(body)

        while not self._stop.is_set():
            try:
                # Send E-amil if rain is detected
                if email_options["emlrain"]:
                    if inputs.rain_sensed() and not last_rain:
                        body = (datetime_string() + ': ' +
                                _('System detected rain.'))
                        self.try_mail(body)
                    last_rain = inputs.rain_sensed()

                # Send E-mail if a new finished run is found
                if email_options["emlrun"]:
                    finished = [
                        run for run in log.finished_runs()
                        if not run['blocked']
                    ]
                    if len(finished) > finished_count:
                        body = datetime_string() + ':\n'
                        for run in finished[finished_count:]:
                            duration = (run['end'] -
                                        run['start']).total_seconds()
                            minutes, seconds = divmod(duration, 60)
                            cm = None
                            try:
                                from plugins import tank_humi_monitor
                                cm = tank_humi_monitor.get_sonic_tank_cm()
                                if cm > 0:
                                    cm = str(cm) + " cm"
                                else:
                                    cm = _('Error - I2C device not found!')
                            except Exception:
                                cm = _('Not available')

                            body += _('Finished run') + ':\n'
                            body += '<br>' + _(
                                'Program') + ': %s\n' % run['program_name']
                            body += '<br>' + _(
                                'Station') + ': %s\n' % stations.get(
                                    run['station']).name
                            body += '<br>' + _(
                                'Start time') + ': %s \n' % datetime_string(
                                    run['start'])
                            body += '<br>' + _(
                                'Duration') + ': %02d:%02d\n\n' % (minutes,
                                                                   seconds)
                            body += '<br>' + _(
                                'Water level in tank') + ': %s \n\n' % (cm)

                        self.try_mail(body)

                    self._sleep(3)
                    finished_count = len(finished)

                self._sleep(5)

            except Exception:
                log.error(NAME,
                          _('E-mail plug-in') + ':\n' + traceback.format_exc())
                self._sleep(60)
예제 #30
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
예제 #31
0
파일: programs.py 프로젝트: Rimco/OSPy
    def calculate_balances(self):
        from scheduler import predicted_schedule
        now = datetime.datetime.now()
        for station in stations.get():
            station.balance = {key: value for key, value in station.balance.iteritems()
                               if key >= now.date() - datetime.timedelta(days=21)}

            if not station.balance or (now.date() - datetime.timedelta(days=21)) not in station.balance:
                station.balance[now.date() - datetime.timedelta(days=21)] = {
                    'eto': 0.0,
                    'rain': 0.0,
                    'intervals': [],
                    'total': 0.0,
                    'valid': True
                }

            runs = log.finished_runs() + log.active_runs()
            calc_day = now.date() - datetime.timedelta(days=20)
            while calc_day < now.date() + datetime.timedelta(days=7):
                if calc_day not in station.balance:
                    station.balance[calc_day] = {
                        'eto': 4.0,
                        'rain': 0.0,
                        'intervals': [],
                        'total': 0.0,
                        'valid': False
                    }
                try:
                    if not station.balance[calc_day]['valid'] or calc_day >= now.date() - datetime.timedelta(days=1):
                        station.balance[calc_day]['eto'] = station.eto_factor * weather.get_eto(calc_day)
                        station.balance[calc_day]['rain'] = 0.0 if station.ignore_rain else weather.get_rain(calc_day)
                        station.balance[calc_day]['valid'] = True
                except Exception:
                    station.balance[calc_day]['valid'] = False
                    logging.warning('Could not get weather information, using fallbacks:\n' + traceback.format_exc())

                intervals = []
                while runs and runs[0]['start'].date() <= calc_day:
                    run = runs[0]
                    if runs[0]['start'].date() == calc_day and not run['blocked'] and run['station'] == station.index:
                        irrigation = (run['end'] - run['start']).total_seconds() / 3600 * station.precipitation
                        if run['manual']:
                            irrigation *= 0.5  # Only count half in case of manual runs
                        intervals.append({
                            'program': run['program'],
                            'program_name': run['program_name'],
                            'done': True,
                            'irrigation': irrigation
                        })
                    del runs[0]

                if calc_day >= now.date():
                    if calc_day == now.date():
                        date_time_start = now
                    else:
                        date_time_start = datetime.datetime.combine(calc_day, datetime.time.min)
                    date_time_end = datetime.datetime.combine(calc_day, datetime.time.max)
                    for run in predicted_schedule(date_time_start, date_time_end):
                        if not run['blocked'] and run['station'] == station.index:
                            irrigation = (run['end'] - run['start']).total_seconds() / 3600 * station.precipitation
                            intervals.append({
                                'program': run['program'],
                                'program_name': run['program_name'],
                                'done': False,
                                'irrigation': irrigation
                            })

                if len(intervals) > len(station.balance[calc_day]['intervals']) or calc_day >= now.date():
                    station.balance[calc_day]['intervals'] = intervals

                station.balance[calc_day]['total'] = station.balance[calc_day - datetime.timedelta(days=1)]['total'] \
                                                     - station.balance[calc_day]['eto'] \
                                                     + station.balance[calc_day]['rain'] \
                                                     + sum(interval['irrigation'] for interval in station.balance[calc_day]['intervals'])

                station.balance[calc_day]['total'] = max(-100, min(station.balance[calc_day]['total'], station.capacity))

                calc_day += datetime.timedelta(days=1)

            station.balance = station.balance # Force saving
예제 #32
0
파일: programs.py 프로젝트: Rimco/OSPy
    def update_station_schedule(self):

        if self.type != ProgramType.WEEKLY_WEATHER:
            self._station_schedule = {}
            for station in self.stations:
                self._station_schedule[station] = self._schedule
        else:
            now = datetime.datetime.now()
            week_start = datetime.datetime.combine(now.date() -
                                                   datetime.timedelta(days=now.weekday()),
                                                   datetime.time.min)
            last_start = self._start
            self._start = week_start
            start_difference = int(round((week_start - last_start).total_seconds() / 60))
            irrigation_min, irrigation_max, run_max, pause_ratio, pem_mins = self.type_data

            try:
                pems = [(week_start + datetime.timedelta(minutes=x), y) for x, y in pem_mins]
                pems += [(week_start + datetime.timedelta(days=7, minutes=x), y) for x, y in pem_mins]
                pems += [(week_start + datetime.timedelta(days=-7, minutes=x), y) for x, y in pem_mins]
                pems = sorted(pems)
                pems = [x for x in pems if x[0] >= now - datetime.timedelta(hours=3)]
                pems = [x for x in pems if (x[0].date() - now.date()).days < 7]

                to_sprinkle = {}
                for station in self.stations:
                    to_sprinkle[station] = []
                    station_pems = pems[:]
                    # Make sure to keep whatever we were planning to do
                    if station in self._station_schedule:
                        for interval in self._station_schedule[station]:
                            if now - datetime.timedelta(hours=3) < last_start + datetime.timedelta(minutes=interval[1]) and last_start + datetime.timedelta(minutes=interval[0]) < now + datetime.timedelta(hours=1):
                                to_sprinkle[station].append([interval[0] + start_difference, interval[1] + start_difference])
                            elif to_sprinkle[station] and last_start + datetime.timedelta(minutes=interval[0]) - (week_start + datetime.timedelta(minutes=to_sprinkle[station][-1][1])) < datetime.timedelta(hours=3):
                                to_sprinkle[station].append([interval[0] + start_difference, interval[1] + start_difference])
                        if to_sprinkle[station]:
                            station_pems = [x for x in pems if x[0] > week_start + datetime.timedelta(minutes=to_sprinkle[station][-1][1])]

                    station_balance = {
                        -1: stations.get(station).balance[now.date() - datetime.timedelta(days=1)]['total']
                    }
                    rain = {
                        -1: stations.get(station).balance[now.date() - datetime.timedelta(days=1)]['rain']
                    }
                    for day_index in range(0, 7):
                        overall_balance = stations.get(station).balance[now.date() + datetime.timedelta(days=day_index)]
                        station_balance[day_index] = station_balance[day_index-1] \
                                                     - overall_balance['eto'] \
                                                     + overall_balance['rain'] \
                                                     + sum(interval['irrigation'] for interval in overall_balance['intervals'] if
                                                           interval['done'] or interval['program'] != self.index)
                        station_balance[day_index] = max(-100, min(station_balance[day_index], stations.get(station).capacity))
                        rain[day_index] = overall_balance['rain']


                    for index, (pem, prio) in enumerate(station_pems):
                        day_index = (pem.date() - now.date()).days
                        rain_today = max(rain[max(-1, day_index-1)], rain[day_index], rain[min(day_index+1, 6)])

                        better_days = [x for x in station_pems[index+1:] if x[1] > prio]
                        better_or_equal_days = [x for x in station_pems[index+1:] if x[1] >= prio and x[0] > pem]
                        any_days = station_pems[index+1:]

                        target_index, target_index_pref = 6, 6
                        if any_days:
                            target_index = (any_days[0][0].date() - now.date()).days

                        if not better_days: # The best day:
                            amount = irrigation_max
                            if better_or_equal_days:
                                target_index_pref = (better_or_equal_days[0][0].date() - now.date()).days
                        else: # A better day is possible:
                            amount = 0
                            target_index_pref = (better_days[0][0].date() - now.date()).days

                        # Make sure not to overflow the capacity (and aim for 0 for today):
                        later_sprinkle_max = min([-station_balance[day_index]] + [stations.get(station).capacity - station_balance[later_day_index] for later_day_index in range(day_index+1, target_index_pref)])

                        # Make sure we sprinkle enough not to go above the maximum in the future:
                        later_sprinkle_min = max(-station_balance[later_day_index] - irrigation_max for later_day_index in range(day_index, target_index + 1))
                        if later_sprinkle_min > 0: # We need to do something to prevent going over the maximum, so:
                            later_sprinkle_min = max(irrigation_min, later_sprinkle_min) # Make sure to sprinkle

                        # Try to go towards a better day:
                        later_sprinkle_min_pref = max(-station_balance[later_day_index] - irrigation_max for later_day_index in range(day_index, target_index_pref + 1))
                        if later_sprinkle_min_pref > 0: # We need to do something to prevent going over the maximum, so:
                            later_sprinkle_min_pref = max(irrigation_min, later_sprinkle_min) # Make sure to sprinkle

                        # Calculate the final value based on the constraints that we have:
                        # print station, pem, amount, later_sprinkle_min, later_sprinkle_min_pref, later_sprinkle_max, irrigation_max-rain_today, irrigation_max, stations.get(station).capacity, [-station_balance[day_index]] + [(stations.get(station).capacity - station_balance[later_day_index]) for later_day_index in range(day_index+1, target_index_pref)]
                        amount = min(max(later_sprinkle_min, min(max(later_sprinkle_min_pref, amount), later_sprinkle_max, irrigation_max-rain_today)), irrigation_max)
                        if amount >= irrigation_min:
                            logging.debug('Weather based schedule for %s: PEM: %s, priority: %s, amount: %f.', stations.get(station).name, str(pem), prio, amount)
                            for later_day_index in range(day_index, 7):
                                station_balance[later_day_index] += amount
                            week_min = (pem - week_start).total_seconds() / 60

                            intervals = [amount]
                            while any(x > run_max for x in intervals):
                                new_len = len(intervals) + 1
                                intervals = [amount / new_len] * new_len

                            for interval in intervals:
                                station_duration = int(round(interval*60/stations.get(station).precipitation))
                                to_sprinkle[station] = self._update_schedule(to_sprinkle[station], self.modulo, week_min, week_min+station_duration)
                                week_min += station_duration + int(round(station_duration*pause_ratio))

                    logging.debug('Weather based deficit for %s: %s', stations.get(station).name, str(sorted([((now.date() + datetime.timedelta(days=x)).isoformat(), y) for x, y in station_balance.iteritems()])))

                self._station_schedule = to_sprinkle
            except Exception:
                logging.warning('Could not create weather based schedule:\n' + traceback.format_exc())
예제 #33
0
파일: helpers.py 프로젝트: Rimco/OSPy
def stop_onrain():
    """Stop stations that do not ignore rain."""
    from ospy.stations import stations
    for station in stations.get():
        if not station.ignore_rain:
            station.activated = False
예제 #34
0
    def run(self):
        temperature_ds = [-127, -127, -127, -127, -127, -127]
        msg_a_on = True
        msg_a_off = True
        last_text = ''

        send = False

        temp_sw = None

        if plugin_options['use_footer']:
            temp_sw = showInFooter(
            )  #  instantiate class to enable data in footer
            temp_sw.button = "pool_heating/settings"  # button redirect on footer
            temp_sw.label = _('Pool Heating')  # label on footer

        ds_a_on = -127.0
        ds_a_off = -127.0

        millis = 0  # timer for clearing status on the web pages after 5 sec
        last_millis = int(round(time.time() * 1000))

        safety_start = datetime.datetime.now()
        safety_end = datetime.datetime.now() + datetime.timedelta(
            minutes=plugin_options['safety_mm'])

        a_state = -3  # for state in footer "Waiting."
        regulation_text = _(u'Waiting to turned on or off.')

        if not plugin_options['enabled_a']:
            a_state = -1  # for state in footer "Waiting (not enabled regulation in options)."

        log.info(NAME, datetime_string() + ' ' + _('Waiting.'))
        end = datetime.datetime.now()

        while not self._stop_event.is_set():
            try:
                if plugin_options[
                        "sensor_probe"] == 2:  # loading probe name from plugin air_temp_humi
                    try:
                        from plugins.air_temp_humi import plugin_options as air_temp_data
                        self.status['ds_name_0'] = air_temp_data['label_ds0']
                        self.status['ds_name_1'] = air_temp_data['label_ds1']
                        self.status['ds_name_2'] = air_temp_data['label_ds2']
                        self.status['ds_name_3'] = air_temp_data['label_ds3']
                        self.status['ds_name_4'] = air_temp_data['label_ds4']
                        self.status['ds_name_5'] = air_temp_data['label_ds5']
                        self.status['ds_count'] = air_temp_data['ds_used']

                        from plugins.air_temp_humi import DS18B20_read_probe  # value with temperature from probe DS1-DS6
                        temperature_ds = [
                            DS18B20_read_probe(0),
                            DS18B20_read_probe(1),
                            DS18B20_read_probe(2),
                            DS18B20_read_probe(3),
                            DS18B20_read_probe(4),
                            DS18B20_read_probe(5)
                        ]

                    except:
                        log.error(
                            NAME,
                            _('Unable to load settings from Air Temperature and Humidity Monitor plugin! Is the plugin Air Temperature and Humidity Monitor installed and set up?'
                              ))
                        pass

                # regulation
                if plugin_options['enabled_a'] and plugin_options[
                        "sensor_probe"] != 0:  # enabled regulation and selected input for probes sensor/airtemp plugin
                    if plugin_options["sensor_probe"] == 1 and sensors.count(
                    ) > 0:
                        sensor_on = sensors.get(
                            int(plugin_options['probe_A_on_sens']))  #  pool
                        if sensor_on.sens_type == 5:  # temperature sensor
                            ds_a_on = self._try_read(sensor_on.last_read_value)
                        elif sensor_on.sens_type == 6 and sensor_on.multi_type == 0:  # multitemperature sensor DS1
                            ds_a_on = self._try_read(
                                sensor_on.last_read_value[0])
                        elif sensor_on.sens_type == 6 and sensor_on.multi_type == 1:  # multitemperature sensor DS2
                            ds_a_on = self._try_read(
                                sensor_on.last_read_value[1])
                        elif sensor_on.sens_type == 6 and sensor_on.multi_type == 2:  # multitemperature sensor DS3
                            ds_a_on = self._try_read(
                                sensor_on.last_read_value[2])
                        elif sensor_on.sens_type == 6 and sensor_on.multi_type == 3:  # multitemperature sensor DS4
                            ds_a_on = self._try_read(
                                sensor_on.last_read_value[3])
                        else:
                            ds_a_on = -127.0

                        sensor_off = sensors.get(
                            int(plugin_options['probe_A_off_sens']))  #  solar
                        if sensor_off.sens_type == 5:  # temperature sensor
                            ds_a_off = self._try_read(
                                sensor_off.last_read_value)
                        elif sensor_off.sens_type == 6 and sensor_off.multi_type == 0:  # multitemperature sensor DS1
                            ds_a_off = self._try_read(
                                sensor_off.last_read_value[0])
                        elif sensor_off.sens_type == 6 and sensor_off.multi_type == 1:  # multitemperature sensor DS2
                            ds_a_off = self._try_read(
                                sensor_off.last_read_value[1])
                        elif sensor_off.sens_type == 6 and sensor_off.multi_type == 2:  # multitemperature sensor DS3
                            ds_a_off = self._try_read(
                                sensor_off.last_read_value[2])
                        elif sensor_off.sens_type == 6 and sensor_off.multi_type == 3:  # multitemperature sensor DS4
                            ds_a_off = self._try_read(
                                sensor_off.last_read_value[3])
                        else:
                            ds_a_off = -127.0

                    elif plugin_options["sensor_probe"] == 2:
                        ds_a_on = temperature_ds[
                            plugin_options['probe_A_on']]  #  pool
                        ds_a_off = temperature_ds[
                            plugin_options['probe_A_off']]  #  solar

                    station_a = stations.get(
                        plugin_options['control_output_A'])

                    # only for testing... without airtemp plugin or sensors
                    #ds_a_on = 15
                    #ds_a_off = 25

                    probes_ok = True
                    if ds_a_on == -127.0 or ds_a_off == -127.0:
                        probes_ok = False
                        a_state = -2
                        # The station switches off if the sensors has a fault
                        sid = station_a.index
                        active = log.active_runs()
                        for interval in active:
                            if interval['station'] == sid:
                                stations.deactivate(sid)
                                log.finish_run(interval)
                                regulation_text = datetime_string() + ' ' + _(
                                    'Regulation set OFF.') + ' ' + ' (' + _(
                                        'Output') + ' ' + str(station_a.index +
                                                              1) + ').'
                                log.clear(NAME)
                                log.info(NAME, regulation_text)
                        # release msg_a_on and msg_a_off to true for future regulation (after probes is ok)
                        msg_a_on = True
                        msg_a_off = True

                    if (ds_a_off - ds_a_on
                        ) > plugin_options['temp_a_on'] and probes_ok:  # ON
                        a_state = 1
                        if msg_a_on:
                            msg_a_on = False
                            msg_a_off = True
                            regulation_text = datetime_string() + ' ' + _(
                                'Regulation set ON.') + ' ' + ' (' + _(
                                    'Output') + ' ' + str(station_a.index +
                                                          1) + ').'
                            log.clear(NAME)
                            log.info(NAME, regulation_text)
                            start = datetime.datetime.now()
                            sid = station_a.index
                            end = datetime.datetime.now() + datetime.timedelta(
                                seconds=plugin_options['reg_ss'],
                                minutes=plugin_options['reg_mm'])
                            new_schedule = {
                                'active': True,
                                'program': -1,
                                'station': sid,
                                'program_name': _(u'Pool Heating'),
                                'fixed': True,
                                'cut_off': 0,
                                'manual': True,
                                'blocked': False,
                                'start': start,
                                'original_start': start,
                                'end': end,
                                'uid':
                                '%s-%s-%d' % (str(start), "Manual", sid),
                                'usage': stations.get(sid).usage
                            }

                            log.start_run(new_schedule)
                            stations.activate(new_schedule['station'])

                            if plugin_options[
                                    'enabled_safety']:  # safety check
                                safety_end = datetime.datetime.now(
                                ) + datetime.timedelta(
                                    minutes=plugin_options['safety_mm'])

                    if (ds_a_off - ds_a_on
                        ) < plugin_options['temp_a_off'] and probes_ok:  # OFF
                        a_state = 0
                        if msg_a_off:
                            msg_a_off = False
                            msg_a_on = True
                            regulation_text = datetime_string() + ' ' + _(
                                'Regulation set OFF.') + ' ' + ' (' + _(
                                    'Output') + ' ' + str(station_a.index +
                                                          1) + ').'
                            log.clear(NAME)
                            log.info(NAME, regulation_text)
                            sid = station_a.index
                            stations.deactivate(sid)
                            active = log.active_runs()
                            for interval in active:
                                if interval['station'] == sid:
                                    log.finish_run(interval)

                            if plugin_options[
                                    'enabled_safety']:  # safety check
                                safety_end = datetime.datetime.now(
                                ) + datetime.timedelta(
                                    minutes=plugin_options['safety_mm'])

                    ### if "pool" end in schedule release msg_a_on to true in regulation for next scheduling ###
                    now = datetime.datetime.now()
                    if now > end:
                        msg_a_off = False
                        msg_a_on = True
                        if probes_ok:
                            a_state = -3

                    if plugin_options['enabled_safety']:  # safety check
                        safety_start = datetime.datetime.now()
                        if safety_start > safety_end and probes_ok:  # is time for check
                            if (ds_a_off - ds_a_on) > plugin_options[
                                    'temp_safety']:  # temperature difference is bigger
                                a_state = -4
                                msg_a_off = False
                                msg_a_on = True
                                regulation_text = datetime_string() + ' ' + _(
                                    'Safety shutdown!'
                                ) + ' ' + ' (' + _('Output') + ' ' + str(
                                    station_a.index +
                                    1) + ').\n' + _('Regulation disabled!')
                                log.clear(NAME)
                                log.info(NAME, regulation_text)
                                sid = station_a.index
                                stations.deactivate(sid)
                                active = log.active_runs()
                                for interval in active:  # stop stations
                                    if interval['station'] == sid:
                                        log.finish_run(interval)
                                send = True  # send e-mail
                                plugin_options[
                                    'enabled_a'] = False  # disabling plugin

                else:
                    if a_state != -4:  # if safety error not print waiting also safety shutdown!
                        a_state = -1

                # footer text
                tempText = ' '

                if a_state == 0:
                    tempText += regulation_text
                if a_state == 1:
                    tempText += regulation_text
                if a_state == -1:
                    tempText = _(
                        'Waiting (not enabled regulation in options, or not selected input).'
                    )
                if a_state == -2:
                    tempText = _(
                        'Some probe shows a fault, regulation is blocked!')
                if a_state == -3:
                    tempText = _('Waiting.')
                if a_state == -4:
                    tempText = _('Safety shutdown!')

                if plugin_options['use_footer']:
                    if temp_sw is not None:
                        temp_sw.val = tempText.encode('utf8').decode(
                            'utf8')  # value on footer

                self._sleep(2)

                millis = int(round(time.time() * 1000))
                if (millis - last_millis
                    ) > 5000:  # 5 second to clearing status on the webpage
                    last_millis = millis
                    log.clear(NAME)
                    if plugin_options["sensor_probe"] == 1:
                        try:
                            if options.temp_unit == 'C':
                                log.info(
                                    NAME,
                                    datetime_string() + '\n' + sensor_on.name +
                                    ' (' + _('Pool') +
                                    ') %.1f \u2103 \n' % ds_a_on +
                                    sensor_off.name + ' (' + _('Solar') +
                                    ') %.1f \u2103' % ds_a_off)
                            else:
                                log.info(
                                    NAME,
                                    datetime_string() + '\n' + sensor_on.name +
                                    ' (' + _('Pool') +
                                    ') %.1f \u2109 \n' % ds_a_on +
                                    sensor_off.name + ' (' + _('Solar') +
                                    ') %.1f \u2103' % ds_a_off)
                        except:
                            pass
                    elif plugin_options["sensor_probe"] == 2:
                        log.info(
                            NAME,
                            datetime_string() + '\n' + _('Pool') +
                            u' %.1f \u2103 \n' % ds_a_on + _('Solar') +
                            ' %.1f \u2103' % ds_a_off)

                    if last_text != tempText:
                        log.info(NAME, tempText)
                        last_text = tempText

                if send:
                    msg = '<b>' + _(
                        'Pool Heating plug-in'
                    ) + '</b> ' + '<br><p style="color:red;">' + _(
                        'System detected error: The temperature did not drop when the pump was switched on after the setuped time. Stations set to OFF. Safety shutdown!'
                    ) + '</p>'
                    msglog = _(
                        'System detected error: The temperature did not drop when the pump was switched on after the setuped time. Stations set to OFF. Safety shutdown!'
                    )
                    send = False
                    try:
                        try_mail = None
                        if plugin_options['eplug'] == 0:  # email_notifications
                            from plugins.email_notifications import try_mail
                        if plugin_options[
                                'eplug'] == 1:  # email_notifications SSL
                            from plugins.email_notifications_ssl import try_mail
                        if try_mail is not None:
                            try_mail(
                                msg,
                                msglog,
                                attachment=None,
                                subject=plugin_options['emlsubject']
                            )  # try_mail(text, logtext, attachment=None, subject=None)

                    except Exception:
                        log.error(
                            NAME,
                            _('Pool Heating plug-in') + ':\n' +
                            traceback.format_exc())
                        self._sleep(2)

            except Exception:
                log.error(
                    NAME,
                    _('Pool Heating plug-in') + ':\n' + traceback.format_exc())
                self._sleep(60)
예제 #35
0
    def run(self):
        last_millis = 0  # timer for save log
        once_text = True  # once_text to mini is auxiliary for blocking
        two_text = True
        three_text = True
        five_text = True
        six_text = True
        send = False
        mini = True

        sonic_cm = -1
        level_in_tank = 0
        regulation_text = _(u'Regulation NONE.')

        if NAME in rain_blocks:
            del rain_blocks[NAME]
        self._sleep(2)

        tank_mon = None

        if tank_options['use_footer']:
            tank_mon = showInFooter(
            )  #  instantiate class to enable data in footer
            tank_mon.button = "tank_monitor/settings"  # button redirect on footer
            tank_mon.label = _(u'Tank')  # label on footer

        end = datetime.datetime.now()

        avg_lst = [0] * tank_options['avg_samples']
        avg_cnt = 0
        avg_rdy = False

        while not self._stop_event.is_set():
            try:
                if tank_options['use_sonic']:
                    if two_text:
                        log.clear(NAME)
                        log.info(NAME, _(u'Water tank monitor is enabled.'))
                        once_text = True
                        two_text = False

                    ping_read = get_sonic_cm()

                    if tank_options[
                            'use_avg'] and ping_read > 0:  # use averaging
                        try:
                            avg_lst[avg_cnt] = ping_read
                        except:
                            avg_lst.append(ping_read)
                        avg_cnt += 1
                        if avg_cnt > tank_options['avg_samples']:
                            avg_cnt = 0
                            avg_rdy = True
                        if avg_rdy:
                            sonic_cm = average_list(avg_lst)
                            level_in_tank = get_sonic_tank_cm(sonic_cm)
                        else:
                            sonic_cm = 0
                            log.clear(NAME)
                            log.info(
                                NAME,
                                _(u'Waiting for {} samples to be read from the sensor (when using averaging).'
                                  ).format(tank_options['avg_samples']))
                    else:  # without averaging
                        if ping_read > 0:  # if sonic value is bad (-1) not use these
                            sonic_cm = ping_read
                            level_in_tank = get_sonic_tank_cm(sonic_cm)
                        else:
                            sonic_cm = 0
                            level_in_tank = 0

                    tempText = ""

                    if level_in_tank > 0 and sonic_cm != 0:  # if level is ok and sonic is ok
                        three_text = True
                        status['level'] = level_in_tank
                        status['ping'] = sonic_cm
                        status['volume'] = get_volume(level_in_tank)
                        status['percent'] = get_tank(level_in_tank)

                        log.clear(NAME)
                        log.info(
                            NAME,
                            datetime_string() + ' ' + _(u'Water level') +
                            ': ' + str(status['level']) + ' ' + _(u'cm') +
                            ' (' + str(status['percent']) + ' ' + (u'%).'))
                        if tank_options['check_liters']:  # display in liters
                            tempText = str(
                                status['volume']
                            ) + ' ' + _(u'liters') + ', ' + str(
                                status['level']) + ' ' + _(u'cm') + ' (' + str(
                                    status['percent']) + ' ' + (u'%)')
                            log.info(
                                NAME,
                                _(u'Ping') + ': ' + str(status['ping']) + ' ' +
                                _(u'cm') + ', ' + _(u'Volume') + ': ' +
                                str(status['volume']) + ' ' + _(u'liters') +
                                '.')
                        else:
                            tempText = str(
                                status['volume']
                            ) + ' ' + _(u'm3') + ', ' + str(
                                status['level']) + ' ' + _(u'cm') + ' (' + str(
                                    status['percent']) + ' ' + (u'%)')
                            log.info(
                                NAME,
                                _(u'Ping') + ': ' + str(status['ping']) + ' ' +
                                _(u'cm') + ', ' + _(u'Volume') + ': ' +
                                str(status['volume']) + ' ' + _(u'm3') + '.')
                        log.info(
                            NAME,
                            str(status['maxlevel_datetime']) + ' ' +
                            _(u'Maximum Water level') + ': ' +
                            str(status['maxlevel']) + ' ' + _(u'cm') + '.')
                        log.info(
                            NAME,
                            str(status['minlevel_datetime']) + ' ' +
                            _(u'Minimum Water level') + ': ' +
                            str(status['minlevel']) + ' ' + _(u'cm') + '.')
                        log.info(NAME, regulation_text)

                        if tank_options[
                                'enable_reg']:  # if enable regulation "maximum water level"
                            reg_station = stations.get(
                                tank_options['reg_output'])
                            if level_in_tank > tank_options[
                                    'reg_max']:  # if actual level in tank > set maximum water level
                                if five_text:
                                    five_text = False
                                    six_text = True
                                    regulation_text = datetime_string(
                                    ) + ' ' + _(
                                        u'Regulation set ON.'
                                    ) + ' ' + ' (' + _(u'Output') + ' ' + str(
                                        reg_station.index + 1) + ').'

                                    start = datetime.datetime.now()
                                    sid = reg_station.index
                                    end = datetime.datetime.now(
                                    ) + datetime.timedelta(
                                        seconds=tank_options['reg_ss'],
                                        minutes=tank_options['reg_mm'])
                                    new_schedule = {
                                        'active':
                                        True,
                                        'program':
                                        -1,
                                        'station':
                                        sid,
                                        'program_name':
                                        _('Tank Monitor'),
                                        'fixed':
                                        True,
                                        'cut_off':
                                        0,
                                        'manual':
                                        True,
                                        'blocked':
                                        False,
                                        'start':
                                        start,
                                        'original_start':
                                        start,
                                        'end':
                                        end,
                                        'uid':
                                        '%s-%s-%d' %
                                        (str(start), "Manual", sid),
                                        'usage':
                                        stations.get(sid).usage
                                    }

                                    log.start_run(new_schedule)
                                    stations.activate(new_schedule['station'])

                            if level_in_tank < tank_options[
                                    'reg_min']:  # if actual level in tank < set minimum water level
                                if six_text:  # blocking for once
                                    five_text = True
                                    six_text = False
                                    regulation_text = datetime_string(
                                    ) + ' ' + _(
                                        u'Regulation set OFF.'
                                    ) + ' ' + ' (' + _(u'Output') + ' ' + str(
                                        reg_station.index + 1) + ').'

                                    sid = reg_station.index
                                    stations.deactivate(sid)
                                    active = log.active_runs()
                                    for interval in active:
                                        if interval['station'] == sid:
                                            log.finish_run(interval)

                            now = datetime.datetime.now()
                            if now > end:  # if program end in schedule release five_text to true in regulation for next scheduling #
                                five_text = True
                                six_text = False
                                regulation_text = datetime_string() + ' ' + _(
                                    u'Waiting.')

                        if status['level'] > status[
                                'maxlevel']:  # maximum level check
                            status['maxlevel'] = status['level']
                            tank_options.__setitem__('saved_max',
                                                     status['level'])
                            status['maxlevel_datetime'] = datetime_string()
                            log.info(
                                NAME,
                                datetime_string() + ': ' +
                                _(u'Maximum has updated.'))
                            update_log()

                        if status['level'] < status['minlevel'] and status[
                                'level'] > 2:  # minimum level check
                            status['minlevel'] = status['level']
                            tank_options.__setitem__('saved_min',
                                                     status['level'])
                            status['minlevel_datetime'] = datetime_string()
                            log.info(
                                NAME,
                                datetime_string() + ': ' +
                                _(u'Minimum has updated.'))
                            update_log()

                        if status['level'] <= int(
                                tank_options['water_minimum']
                        ) and mini and not options.manual_mode and status[
                                'level'] > 2:  # level value is lower
                            if tank_options[
                                    'use_send_email']:  # if email is enabled
                                send = True  # send
                                mini = False

                            if tank_options['use_stop']:  # if stop scheduler
                                set_stations_in_scheduler_off()
                                log.info(
                                    NAME,
                                    datetime_string() + ' ' +
                                    _(u'ERROR: Water in Tank') + ' < ' +
                                    str(tank_options['water_minimum']) + ' ' +
                                    _(u'cm') + _(u'!'))
                                delaytime = int(tank_options['delay_duration'])
                                if delaytime > 0:  # if there is no water in the tank and the stations stop, then we set the rain delay for this time for blocking
                                    rain_blocks[NAME] = datetime.datetime.now(
                                    ) + datetime.timedelta(
                                        hours=float(delaytime))

                        if level_in_tank > int(
                                tank_options['water_minimum']
                        ) + 5 and not mini:  # refresh send email if actual level > options minimum +5
                            mini = True

                        if NAME in rain_blocks and level_in_tank > int(
                                tank_options['water_minimum']):
                            del rain_blocks[NAME]

                        if tank_options['enable_log']:
                            millis = int(round(time.time() * 1000))
                            interval = (tank_options['log_interval'] * 60000)
                            if (millis - last_millis) > interval:
                                last_millis = millis
                                update_log()
                    elif level_in_tank == -1 and sonic_cm == 0:  # waiting for samples
                        tempText = _('Waiting for samples')
                    else:  # error probe
                        tempText = _('FAULT')
                        log.clear(NAME)
                        log.info(
                            NAME,
                            datetime_string() + ' ' +
                            _(u'Water level: Error.'))
                        if tank_options[
                                'use_water_err'] and three_text:  # if probe has error send email
                            three_text = False
                            log.info(
                                NAME,
                                datetime_string() + ' ' +
                                _(u'ERROR: Water probe has fault?'))

                            msg = '<b>' + _(
                                u'Water Tank Monitor plug-in'
                            ) + '</b> ' + '<br><p style="color:red;">' + _(
                                u'System detected error: Water probe has fault?'
                            ) + '</p>'
                            msglog = _(
                                u'Water Tank Monitor plug-in'
                            ) + ': ' + _(
                                u'System detected error: Water probe has fault?'
                            )
                            try:
                                from plugins.email_notifications import try_mail
                                try_mail(
                                    msg,
                                    msglog,
                                    attachment=None,
                                    subject=tank_options['emlsubject']
                                )  # try_mail(text, logtext, attachment=None, subject=None)

                            except Exception:
                                log.error(
                                    NAME,
                                    _(u'Water Tank Monitor plug-in') + ':\n' +
                                    traceback.format_exc())

                        if tank_options['use_water_stop']:
                            if NAME not in rain_blocks:
                                set_stations_in_scheduler_off()
                                delaytime = int(tank_options['delay_duration'])
                                if delaytime > 0:  # if probe has fault, then we set the rain delay for this time for blocking
                                    rain_blocks[NAME] = datetime.datetime.now(
                                    ) + datetime.timedelta(
                                        hours=float(delaytime))

                    if tank_options['enable_reg']:
                        tempText += ', ' + regulation_text

                    if tank_options['use_footer']:
                        if tank_mon is not None:
                            tank_mon.val = tempText.encode('utf8').decode(
                                'utf8')  # value on footer

                    self._sleep(3)

                else:
                    if once_text:
                        log.clear(NAME)
                        log.info(NAME, _(u'Water tank monitor is disabled.'))
                        once_text = False
                        two_text = True
                        last_level = 0
                    self._sleep(1)

                if send:
                    msg = '<b>' + _(
                        u'Water Tank Monitor plug-in'
                    ) + '</b> ' + '<br><p style="color:red;">' + _(
                        u'System detected error: Water Tank has minimum Water Level'
                    ) + ': ' + str(tank_options['water_minimum']) + _(
                        u'cm') + '.\n' + '</p>'
                    msglog = _(u'Water Tank Monitor plug-in') + ': ' + _(
                        u'System detected error: Water Tank has minimum Water Level'
                    ) + ': ' + str(
                        tank_options['water_minimum']) + _(u'cm') + '. '
                    send = False
                    try:
                        try_mail = None
                        if tank_options['eplug'] == 0:  # email_notifications
                            from plugins.email_notifications import try_mail
                        if tank_options[
                                'eplug'] == 1:  # email_notifications SSL
                            from plugins.email_notifications_ssl import try_mail
                        if try_mail is not None:
                            try_mail(
                                msg,
                                msglog,
                                attachment=None,
                                subject=tank_options['emlsubject']
                            )  # try_mail(text, logtext, attachment=None, subject=None)

                    except Exception:
                        log.info(
                            NAME,
                            _(u'E-mail not send! The Email Notifications plug-in is not found in OSPy or not correctly setuped.'
                              ))
                        log.error(
                            NAME,
                            _(u'Water Tank Monitor plug-in') + ':\n' +
                            traceback.format_exc())

            except Exception:
                log.clear(NAME)
                log.error(
                    NAME,
                    _(u'Water Tank Monitor plug-in') + ':\n' +
                    traceback.format_exc())
                self._sleep(60)
예제 #36
0
    def _check_schedule():
        current_time = datetime.datetime.now()
        check_start = current_time - datetime.timedelta(days=1)
        check_end = current_time + datetime.timedelta(days=1)

        rain = not options.manual_mode and (
            rain_blocks.block_end() > datetime.datetime.now()
            or inputs.rain_sensed())

        active = log.active_runs()
        for entry in active:
            ignore_rain = stations.get(entry['station']).ignore_rain
            if entry['end'] <= current_time or (rain and not ignore_rain
                                                and not entry['blocked']
                                                and not entry['manual']):
                log.finish_run(entry)
                if not entry['blocked']:
                    stations.deactivate(entry['station'])

        if not options.manual_mode:
            schedule = predicted_schedule(check_start, check_end)
            #import pprint
            #logging.debug("Schedule: %s", pprint.pformat(schedule))
            for entry in schedule:
                if entry['start'] <= current_time < entry['end']:
                    log.start_run(entry)
                    if not entry['blocked']:
                        stations.activate(entry['station'])

        if stations.master is not None:
            master_on = False

            # It's easy if we don't have to use delays:
            if options.master_on_delay == options.master_off_delay == 0:
                for entry in active:
                    if not entry['blocked'] and stations.get(
                            entry['station']).activate_master:
                        master_on = True
                        break

            else:
                # In manual mode we cannot predict, we only know what is currently running and the history
                if options.manual_mode:
                    active = log.finished_runs() + active
                else:
                    active = combined_schedule(check_start, check_end)

                for entry in active:
                    if not entry['blocked'] and stations.get(
                            entry['station']).activate_master:
                        if entry['start'] + datetime.timedelta(seconds=options.master_on_delay) \
                                <= current_time < \
                                entry['end'] + datetime.timedelta(seconds=options.master_off_delay):
                            master_on = True
                            break

            if stations.master is not None:
                master_station = stations.get(stations.master)

                if master_on != master_station.active:
                    master_station.active = master_on

            if options.master_relay:
                outputs.relay_output = master_on

        if stations.master_two is not None:
            master_two_on = False

            # It's easy if we don't have to use delays:
            if options.master_on_delay_two == options.master_off_delay_two == 0:
                for entry in active:
                    if not entry['blocked'] and stations.get(
                            entry['station']).activate_master_two:
                        master_two_on = True
                        break

            else:
                # In manual mode we cannot predict, we only know what is currently running and the history
                if options.manual_mode:
                    active = log.finished_runs() + active
                else:
                    active = combined_schedule(check_start, check_end)

                for entry in active:
                    if not entry['blocked'] and stations.get(
                            entry['station']).activate_master_two:
                        if entry['start'] + datetime.timedelta(seconds=options.master_on_delay) \
                                <= current_time < \
                                entry['end'] + datetime.timedelta(seconds=options.master_off_delay):
                            master_two_on = True
                            break

            if stations.master_two is not None:
                master_station_two = stations.get(stations.master_two)

                if master_two_on != master_station_two.active:
                    master_station_two.active = master_two_on
예제 #37
0
    def run(self):
        log.clear(NAME)
        error_check = False  # error signature
        relay_pins = [0]
        relay_count = -1
        msg_debug_err = True
        msg_debug_on = [
            True, True, True, True, True, True, True, True, True, True, True,
            True, True, True, True, True
        ]
        msg_debug_off = [
            True, True, True, True, True, True, True, True, True, True, True,
            True, True, True, True, True
        ]
        time_cleaner = 0

        if not plugin_options['enabled']:
            log.info(NAME, _('Relay 16 plug-in') + ': ' + _('is disabled.'))

        while not self._stop_event.is_set():
            try:
                if relay_count != plugin_options['relays'] and plugin_options[
                        'enabled']:
                    relay_count = plugin_options['relays']
                    log.clear(NAME)
                    log.info(
                        NAME,
                        _('Relay 16 plug-in') + ': ' + datetime_string() +
                        ' ' + str(plugin_options['relays']) + ' ' +
                        _('Outputs set.'))

                    #### define the GPIO pins that will be used ####
                    try:
                        if determine_platform(
                        ) == 'pi':  # If this will run on Raspberry Pi:

                            import RPi.GPIO as GPIO  # RPi hardware
                            GPIO.setmode(
                                GPIO.BOARD
                            )  #IO channels are identified by header connector pin numbers. Pin numbers are

                            if get_rpi_revision() >= 2:

                                relay_pins = [
                                    22, 24, 26, 32, 36, 38, 40, 21, 23, 29, 31,
                                    33, 35, 37, 18, 19
                                ]  ### associations for outputs HW connector PINs

                                log.info(
                                    NAME,
                                    _('Relay 16 plug-in') + ': ' +
                                    _('Possible GPIO pins') + str(relay_pins) +
                                    '.')
                                for i in range(
                                        plugin_options['relays']
                                ):  # count 1 or 2, 4, 8, 16 outputs
                                    try:
                                        GPIO.setup(
                                            relay_pins[i],
                                            GPIO.OUT)  # set pin as outputs
                                    except:
                                        error_check = True  # not set pins -> this is error
                                        log.error(
                                            NAME,
                                            _('Relay 16 plug-in') + ':\n' +
                                            traceback.format_exc())
                            else:
                                log.info(
                                    NAME,
                                    _('Relay 16 plug-in') + ': ' +
                                    _('Sorry Raspberry Pi 1 is old version.'))
                                error_check = True
                        else:
                            log.info(
                                NAME,
                                _('Relay 16 plug-in') + ': ' +
                                _('Relay board plugin only supported on Raspberry Pi.'
                                  ))

                    except:
                        if msg_debug_err:
                            log.error(
                                NAME,
                                _('Relay 16 plug-in') + ':\n' +
                                traceback.format_exc())
                            msg_debug_err = False
                        error_check = False
                        pass

                #### plugin
                if plugin_options['enabled']:  # if plugin is enabled
                    if error_check == False:  # if not check error
                        for station in stations.get():
                            if station.index + 1 <= plugin_options[
                                    'relays']:  # only if station count is < count relay outputs

                                ### ON
                                if station.active:  # stations is on
                                    if plugin_options[
                                            'active'] == 'high':  # normal high logic
                                        # relay output on to 3.3V
                                        GPIO.output(relay_pins[station.index],
                                                    GPIO.HIGH)
                                        if msg_debug_on[station.index]:
                                            log.info(
                                                NAME,
                                                _('Relay 16 plug-in') + ': ' +
                                                datetime_string() + ' ' +
                                                _('Setings Relay Output') +
                                                ' ' +
                                                str(relay_pins[station.index])
                                                + ' ' + _('to HIGH') + ' (' +
                                                _('Station') + ' ' +
                                                str(station.index + 1) + ' ' +
                                                _('ON') + ').')
                                            msg_debug_on[station.index] = False
                                            msg_debug_off[station.index] = True
                                    else:  # inversion low logic
                                        # relay output on to 0V
                                        GPIO.output(relay_pins[station.index],
                                                    GPIO.LOW)
                                        if msg_debug_on[station.index]:
                                            log.info(
                                                NAME,
                                                _('Relay 16 plug-in') + ': ' +
                                                datetime_string() + ' ' +
                                                _('Setings Relay Output') +
                                                ' ' +
                                                str(relay_pins[station.index])
                                                + ' ' + _('to LOW') + ' (' +
                                                _('Station') + ' ' +
                                                str(station.index + 1) + ' ' +
                                                _('ON') + ').')
                                            msg_debug_on[station.index] = False
                                            msg_debug_off[station.index] = True

                                ### OFF
                                else:  # stations is off
                                    if plugin_options[
                                            'active'] == 'high':  # normal high logic
                                        # relay output off to 0V
                                        GPIO.output(relay_pins[station.index],
                                                    GPIO.LOW)
                                        if msg_debug_off[station.index]:
                                            log.info(
                                                NAME,
                                                _('Relay 16 plug-in') + ': ' +
                                                datetime_string() + ' ' +
                                                _('Setings Relay Output') +
                                                ' ' +
                                                str(relay_pins[station.index])
                                                + ' ' + _('to LOW') + ' (' +
                                                _('Station') + ' ' +
                                                str(station.index + 1) + ' ' +
                                                _('OFF') + ').')
                                            msg_debug_off[
                                                station.index] = False
                                            msg_debug_on[station.index] = True
                                    else:  # inversion low logic
                                        # relay output off to 3.3V
                                        GPIO.output(relay_pins[station.index],
                                                    GPIO.HIGH)
                                        if msg_debug_off[station.index]:
                                            log.info(
                                                NAME,
                                                _('Relay 16 plug-in') + ': ' +
                                                datetime_string() + ' ' +
                                                _('Setings Relay Output') +
                                                ' ' +
                                                str(relay_pins[station.index])
                                                + ' ' + _('to HIGH') + ' (' +
                                                _('Station') + ' ' +
                                                str(station.index + 1) + ' ' +
                                                _('OFF') + ').')
                                            msg_debug_off[
                                                station.index] = False
                                            msg_debug_on[station.index] = True

                time.sleep(0.5)

                time_cleaner += 1

                if time_cleaner >= 120:  # 60 sec timer (ex: 120 * time.sleep(0.5) is 60 sec)
                    time_cleaner = 0
                    relay_count = -1
                    msg_debug_err = True
                    msg_debug_on = [
                        True, True, True, True, True, True, True, True, True,
                        True, True, True, True, True, True, True
                    ]
                    msg_debug_off = [
                        True, True, True, True, True, True, True, True, True,
                        True, True, True, True, True, True, True
                    ]

            except Exception:
                log.error(
                    NAME,
                    _('Relay 16 plug-in') + ':\n' + traceback.format_exc())
                msg = -1
                self._sleep(60)
예제 #38
0
파일: scheduler.py 프로젝트: Rimco/OSPy
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
예제 #39
0
    def run(self):
        log.clear(NAME)

        if not plugin_options['enabled']:
            log.info(NAME, _('Pressurizer is disabled.'))
        else:
            log.info(NAME, _('Pressurizer is enabled.'))

        start_master = False  # for master station ON/OFF
        pressurizer_master_relay_off.send(
        )  # send signal relay off from this plugin

        while not self._stop_event.is_set():
            try:
                if plugin_options['enabled']:  # plugin is enabled
                    current_time = datetime.datetime.now()
                    user_pre_time = current_time + datetime.timedelta(
                        seconds=int(plugin_options['pre_time']))
                    check_start = current_time - datetime.timedelta(days=1)
                    check_end = current_time + datetime.timedelta(days=1)

                    schedule = predicted_schedule(check_start, check_end)
                    rain = not options.manual_mode and (
                        rain_blocks.block_end() > datetime.datetime.now()
                        or inputs.rain_sensed())

                    if stations.master is None:
                        start_master = False
                        log.clear(NAME)
                        log.info(
                            NAME,
                            datetime_string() + ' ' +
                            _('This plugin requires setting master station to enabled. Setup this in options! And also enable the relay as master station in options!'
                              ))
                        self._sleep(10)

                    for entry in schedule:
                        if entry['start'] <= user_pre_time < entry[
                                'end']:  # is possible program in this interval?
                            if not rain and not entry[
                                    'blocked']:  # is not blocked and not ignored rain?
                                if stations.master is not None:
                                    log.clear(NAME)
                                    log.info(
                                        NAME,
                                        datetime_string() + ' ' +
                                        _('Is time for pump running...'))
                                    start_master = True

                    if start_master:  # is time for run relay
                        pname = _('Pressurizer plug-in')
                        program_name = "%s " % pname.encode(
                            "utf-8", errors="ignore").decode(
                                "utf-8")  # program name

                        sname = 0

                        for station in stations.get():
                            if station.is_master:
                                sname = station.index  # master pump index

                        pend = current_time + datetime.timedelta(
                            seconds=int(plugin_options['run_time']))

                        _entry = {
                            'active':
                            None,
                            'program':
                            -1,
                            'program_name':
                            program_name,
                            'fixed':
                            True,
                            'cut_off':
                            0,
                            'manual':
                            True,
                            'blocked':
                            False,
                            'start':
                            datetime.datetime.now(),
                            'original_start':
                            datetime.datetime.now(),
                            'end':
                            pend,
                            'uid':
                            '%s-%s-%d' % (datetime.datetime.now(),
                                          str(program_name), sname),
                            'usage':
                            2.0,
                            'station':
                            sname
                        }

                        if plugin_options['relay']:
                            pressurizer_master_relay_on.send(
                            )  # send signal relay on from this plugin
                            self._sleep(0.5)
                            outputs.relay_output = True  # activate relay
                            log.info(NAME, _('Activating relay.'))
                            log.start_run(_entry)

                            wait_for_run = plugin_options[
                                'run_time']  # pump run time
                            if wait_for_run > plugin_options[
                                    'pre_time']:  # is not run time > pre run time?
                                wait_for_run = plugin_options[
                                    'pre_time']  # scheduller tick is 1 second

                            log.info(
                                NAME,
                                datetime_string() + ' ' + _('Waiting') + ' ' +
                                str(wait_for_run) + ' ' + _('second.'))
                            self._sleep(
                                int(wait_for_run))  # waiting on run time

                            pressurizer_master_relay_off.send(
                            )  # send signal relay off from this plugin
                            self._sleep(0.5)
                            outputs.relay_output = False  # deactivate relay
                            log.info(NAME, _('Deactivating relay.'))
                            log.finish_run(_entry)

                            log.info(NAME,
                                     datetime_string() + ' ' + _('Ready.'))
                            start_master = False

                            seconds = int(
                                plugin_options['mm'] or 0) * 60 + int(
                                    plugin_options['ss'] or 0)  # (mm:ss)
                            log.info(
                                NAME,
                                datetime_string() + ' ' + _('Waiting') + ' ' +
                                str(seconds) + ' ' + _('second.'))
                            self._sleep(
                                seconds
                            )  # How long after the relay is activated wait for another stations

                    else:
                        self._sleep(2)

                else:
                    self._sleep(5)

                self._sleep(1)

            except Exception:
                log.error(
                    NAME,
                    _('Pressurizer plug-in') + ':\n' + traceback.format_exc())
                self._sleep(60)
예제 #40
0
def on_message(client, userdata, message):
    log.clear(NAME) 
    log.info(NAME, datetime_string() + ' ' + _('Message received') + ': ' + str(message.payload.decode("utf-8")))
    #print("Message topic=",message.topic)
    #print("Message qos=",message.qos)
    #print("Message retain flag=",message.retain)

    try:
        cmd = json.loads(message.payload)
    except:
        cmd = []
        status = "RunOnce Error"
        client.publish(plugin_options['publish_up_down'], status)
        pass

    try:
        log.info(NAME, datetime_string() + ' ' + _(u'Try-ing to processing command.'))
        num_sta = options.output_count
        if type(cmd) is list:            # cmd is list
            if len(cmd) < num_sta:
                log.info(NAME, datetime_string() + ' ' + _(u'Not enough stations specified, assuming first {} of {}').format(len(cmd), num_sta))
                rovals = cmd + ([0] * (num_sta - len(cmd)))              
            elif len(cmd) > num_sta:
                log.info(NAME, datetime_string() + ' ' + _(u'Too many stations specified, truncating to {}').format(num_sta))
                rovals = cmd[0:num_sta]
            else:
                rovals = cmd

        elif type(cmd) is dict:          # cmd is dictionary
            rovals = [0] * num_sta
            snames = station_names()     # Load station names from file
            jnames = json.loads(snames)  # Load as json
                                                
            for k, v in list(cmd.items()):
                if k not in snames:      # station name in dict is not in OSPy stations name (ERROR)
                    log.warning(NAME, _(u'No station named') + (u': %s') % k)
                                                    
                else:                    # station name in dict is in OSPy stations name (OK)
                    # v is value for time, k is station name in dict
                    rovals[jnames.index(k)] = v         

        else:
            log.error(NAME, datetime_string() + ' ' + _(u'Unexpected command') + (u': %s') % message.payload)
            rovals = None
            status = "RunOnce Error"
            client.publish(plugin_options['publish_up_down'], status)

        if rovals is not None and any(rovals):  
            for i in range(0, len(rovals)):     
                sid = i                                                                                
                start = datetime.datetime.now()
                try:
                    end = datetime.datetime.now() + datetime.timedelta(seconds=int(rovals[i]))
                except:
                    end = datetime.datetime.now()
                    log.error(NAME, _(u'MQTT Run-once plug-in') + ':\n' + traceback.format_exc())
                    pass

                new_schedule = {
                    'active': True,
                    'program': -1,
                    'station': sid,
                    'program_name': _(u'MQTT Run-once'),
                    'fixed': True,
                    'cut_off': 0,
                    'manual': True,
                    'blocked': False,
                    'start': start,
                    'original_start': start,
                    'end': end,
                    'uid': '%s-%s-%d' % (str(start), "Manual", sid),
                    'usage': stations.get(sid).usage
                }
                log.start_run(new_schedule)
                stations.activate(new_schedule['station'])

                if int(rovals[i]) < 1:                 # station has no time for run (stoping)
                    stations.deactivate(sid)
                    active = log.active_runs()
                    for interval in active:
                        if interval['station'] == sid:
                            log.finish_run(interval)

            status = "RunOnce Processing"
            client.publish(plugin_options['publish_up_down'], status)                                                                    

    except Exception:
        log.clear(NAME)
        log.error(NAME, _(u'MQTT Run-once plug-in') + ':\n' + traceback.format_exc())
        status = "RunOnce Error"
        client.publish(plugin_options['publish_up_down'], status)
        pass