def GET(self, index): qdict = web.input() try: index = int(index) delete = get_input(qdict, 'delete', False, lambda x: True) runnow = get_input(qdict, 'runnow', False, lambda x: True) enable = get_input(qdict, 'enable', None, lambda x: x == '1') if delete: programs.remove_program(index) Timer(0.1, programs.calculate_balances).start() raise web.seeother('/programs') elif runnow: programs.run_now(index) Timer(0.1, programs.calculate_balances).start() raise web.seeother('/programs') elif enable is not None: programs[index].enabled = enable Timer(0.1, programs.calculate_balances).start() raise web.seeother('/programs') except ValueError: pass if isinstance(index, int): program = programs.get(index) else: program = programs.create_program() program.set_days_simple(6*60, 30, 30, 0, []) return self.core_render.program(program)
def POST(self, program_id): logger.debug('POST /programs/{}'.format(program_id if program_id else '')) if program_id: action = web.input().get('do', '').lower() program_id = int(program_id) if action == 'runnow': logger.debug('Starting program {id} ("{name}")'.format(id=program_id, name=programs[program_id].name)) programs.run_now(program_id) elif action == 'stop': pass # TODO else: logger.error('Unknown program action: "%s"', action) raise badrequest() return self._program_to_dict(programs[program_id]) else: program_data = json.loads(web.data()) p = programs.create_program() p.type = program_data.get('type', ProgramType.DAYS_SIMPLE) if p.type == ProgramType.DAYS_SIMPLE: p.set_days_simple(0, 30, 0, 0, []) # some sane defaults self._dict_to_program(p, program_data) # some sane defaults # p.enabled = False programs.add_program(p) return self._program_to_dict(programs.get(p.index))
def GET(self, index): qdict = web.input() try: index = int(index) delete = get_input(qdict, 'delete', False, lambda x: True) runnow = get_input(qdict, 'runnow', False, lambda x: True) enable = get_input(qdict, 'enable', None, lambda x: x == '1') if delete: programs.remove_program(index) Timer(0.1, programs.calculate_balances).start() raise web.seeother('/programs') elif runnow: programs.run_now(index) Timer(0.1, programs.calculate_balances).start() raise web.seeother('/programs') elif enable is not None: programs[index].enabled = enable Timer(0.1, programs.calculate_balances).start() raise web.seeother('/programs') except ValueError: pass if isinstance(index, int): program = programs.get(index) else: program = programs.create_program() program.set_days_simple(6*60, 30, 30, 0, []) return self.core_render.program(program)
def POST(self, program_id): logger.debug( 'POST /programs/{}'.format(program_id if program_id else '')) if program_id: action = web.input().get('do', '').lower() program_id = int(program_id) if action == 'runnow': logger.debug('Starting program {id} ("{name}")'.format( id=program_id, name=programs[program_id].name)) programs.run_now(program_id) elif action == 'stop': pass # TODO else: logger.error('Unknown program action: "%s"', action) raise badrequest() return self._program_to_dict(programs[program_id]) else: program_data = json.loads(web.data()) p = programs.create_program() p.type = program_data.get('type', ProgramType.DAYS_SIMPLE) if p.type == ProgramType.DAYS_SIMPLE: p.set_days_simple(0, 30, 0, 0, []) # some sane defaults self._dict_to_program(p, program_data) # some sane defaults # p.enabled = False programs.add_program(p) return self._program_to_dict(programs.get(p.index))
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()
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()
def _save_logs(self): from ospy.programs import programs, ProgramType result = [] if options.run_log or any(program.type == ProgramType.WEEKLY_WEATHER for program in programs.get()): result = self._log['Run'] options.logged_runs = result
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)
def _botCmd_runOnce(self, bot, update, args): # run-now program xx chat_id = update.message.chat.id if chat_id in list(self._currentChats): txt = _(u'{} RunOnce: program {}.').format(options.name, args) for program in programs.get(): if (program.index == int(args-1)): options.manual_mode = False log.finish_run(None) stations.clear() programs.run_now(program.index) break program.index+1 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)
def run(self): log.clear(NAME) while not self._stop.is_set(): try: if plugin_options['use_button']: # if button plugin is enabled actual_buttons = read_buttons() #led_outputs(actual_buttons) for i in range(8): tb = "button" + str(i) if actual_buttons == i and plugin_options[tb]== "reboot": log.info(NAME, datetime_string() + ': ' + _('System reboot')) stations.clear() reboot() self._sleep(2) if actual_buttons == i and plugin_options[tb]== "pwrOff": log.info(NAME, datetime_string() + ': ' + _('System shutdown')) stations.clear() poweroff() self._sleep(2) if actual_buttons == i and plugin_options[tb]== "stopAll": log.info(NAME, datetime_string() + ': ' + _('All stations now stopped')) log.finish_run(None) # save log stations.clear() # set all station to off self._sleep(2) if actual_buttons == i and plugin_options[tb]== "schedEn": log.info(NAME, datetime_string() + ': ' + _('Scheduler is now enabled')) options.scheduler_enabled = True self._sleep(2) if actual_buttons == i and plugin_options[tb]== "schedDis": log.info(NAME, datetime_string() + ': ' + _('Scheduler is now disabled')) options.scheduler_enabled = False self._sleep(2) if actual_buttons == i and plugin_options[tb]== "RunP1": log.info(NAME, datetime_string() + ': ' + _('Run now program 1')) for program in programs.get(): if (program.index == 0): # Run-now program 1 options.manual_mode = False log.finish_run(None) stations.clear() programs.run_now(program.index) program.index+1 self._sleep(2) if actual_buttons == i and plugin_options[tb]== "RunP2": log.info(NAME, datetime_string() + ': ' + _('Run now program 2')) for program in programs.get(): if (program.index == 1): # Run-now program 2 options.manual_mode = False log.finish_run(None) stations.clear() programs.run_now(program.index) program.index+1 self._sleep(2) if actual_buttons == i and plugin_options[tb]== "RunP3": log.info(NAME, datetime_string() + ': ' + _('Run now program 3')) for program in programs.get(): if (program.index == 2): # Run-now program 3 options.manual_mode = False log.finish_run(None) stations.clear() programs.run_now(program.index) program.index+1 self._sleep(2) if actual_buttons == i and plugin_options[tb]== "RunP4": log.info(NAME, datetime_string() + ': ' + _('Run now program 4')) for program in programs.get(): if (program.index == 3): # Run-now program 4 options.manual_mode = False log.finish_run(None) stations.clear() programs.run_now(program.index) program.index+1 self._sleep(2) if actual_buttons == i and plugin_options[tb]== "RunP5": log.info(NAME, datetime_string() + ': ' + _('Run now program 5')) for program in programs.get(): if (program.index == 4): # Run-now program 5 options.manual_mode = False log.finish_run(None) stations.clear() programs.run_now(program.index) program.index+1 self._sleep(2) if actual_buttons == i and plugin_options[tb]== "RunP6": log.info(NAME, datetime_string() + ': ' + _('Run now program 6')) for program in programs.get(): if (program.index == 5): # Run-now program 6 options.manual_mode = False log.finish_run(None) stations.clear() programs.run_now(program.index) program.index+1 self._sleep(2) if actual_buttons == i and plugin_options[tb]== "RunP7": log.info(NAME, datetime_string() + ': ' + _('Run now program 7')) for program in programs.get(): if (program.index == 6): # Run-now program 7 options.manual_mode = False log.finish_run(None) stations.clear() programs.run_now(program.index) program.index+1 self._sleep(2) if actual_buttons == i and plugin_options[tb]== "RunP8": log.info(NAME, datetime_string() + ': ' + _('Run now program 8')) for program in programs.get(): if (program.index == 7): # Run-now program 8 options.manual_mode = False log.finish_run(None) stations.clear() programs.run_now(program.index) program.index+1 self._sleep(2) self._sleep(1) except Exception: log.clear(NAME) log.error(NAME, _('Button plug-in') + ':\n' + traceback.format_exc()) self._sleep(60) pass
def POST(self, index): qdict = web.input() try: index = int(index) program = programs.get(index) except ValueError: program = programs.create_program() qdict['schedule_type'] = int(qdict['schedule_type']) program.name = qdict['name'] program.stations = json.loads(qdict['stations']) program.enabled = True if qdict.get('enabled', 'off') == 'on' else False if qdict['schedule_type'] == ProgramType.WEEKLY_WEATHER: program.cut_off = 0 program.fixed = True else: program.cut_off = int(qdict['cut_off']) program.fixed = True if qdict.get('fixed', 'off') == 'on' else False simple = [int(qdict['simple_hour']) * 60 + int(qdict['simple_minute']), int(qdict['simple_duration']), int(qdict['simple_pause']), int(qdict['simple_rcount']) if qdict.get('simple_repeat', 'off') == 'on' else 0] repeat_start_date = datetime.datetime.combine(datetime.date.today(), datetime.time.min) + \ datetime.timedelta(days=int(qdict['interval_delay'])) if qdict['schedule_type'] == ProgramType.DAYS_SIMPLE: program.set_days_simple(*(simple + [ json.loads(qdict['days'])])) elif qdict['schedule_type'] == ProgramType.DAYS_ADVANCED: program.set_days_advanced(json.loads(qdict['advanced_schedule_data']), json.loads(qdict['days'])) elif qdict['schedule_type'] == ProgramType.REPEAT_SIMPLE: program.set_repeat_simple(*(simple + [ int(qdict['interval']), repeat_start_date])) elif qdict['schedule_type'] == ProgramType.REPEAT_ADVANCED: program.set_repeat_advanced(json.loads(qdict['advanced_schedule_data']), int(qdict['interval']), repeat_start_date) elif qdict['schedule_type'] == ProgramType.WEEKLY_ADVANCED: program.set_weekly_advanced(json.loads(qdict['weekly_schedule_data'])) elif qdict['schedule_type'] == ProgramType.WEEKLY_WEATHER: program.set_weekly_weather(int(qdict['weather_irrigation_min']), int(qdict['weather_irrigation_max']), int(qdict['weather_run_max']), int(qdict['weather_pause_ratio'])/100.0, json.loads(qdict['weather_pems_data']), ) elif qdict['schedule_type'] == ProgramType.CUSTOM: program.modulo = int(qdict['interval'])*1440 program.manual = False program.start = repeat_start_date program.schedule = json.loads(qdict['custom_schedule_data']) if program.index < 0: programs.add_program(program) Timer(0.1, programs.calculate_balances).start() raise web.seeother('/programs')
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)
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())
def POST(self, index): qdict = web.input() try: index = int(index) program = programs.get(index) except ValueError: program = programs.create_program() qdict['schedule_type'] = int(qdict['schedule_type']) program.name = qdict['name'] program.stations = json.loads(qdict['stations']) program.enabled = True if qdict.get('enabled', 'off') == 'on' else False if qdict['schedule_type'] == ProgramType.WEEKLY_WEATHER: program.cut_off = 0 program.fixed = True else: program.cut_off = int(qdict['cut_off']) program.fixed = True if qdict.get('fixed', 'off') == 'on' else False simple = [int(qdict['simple_hour']) * 60 + int(qdict['simple_minute']), int(qdict['simple_duration']), int(qdict['simple_pause']), int(qdict['simple_rcount']) if qdict.get('simple_repeat', 'off') == 'on' else 0] repeat_start_date = datetime.datetime.combine(datetime.date.today(), datetime.time.min) + \ datetime.timedelta(days=int(qdict['interval_delay'])) if qdict['schedule_type'] == ProgramType.DAYS_SIMPLE: program.set_days_simple(*(simple + [ json.loads(qdict['days'])])) elif qdict['schedule_type'] == ProgramType.DAYS_ADVANCED: program.set_days_advanced(json.loads(qdict['advanced_schedule_data']), json.loads(qdict['days'])) elif qdict['schedule_type'] == ProgramType.REPEAT_SIMPLE: program.set_repeat_simple(*(simple + [ int(qdict['interval']), repeat_start_date])) elif qdict['schedule_type'] == ProgramType.REPEAT_ADVANCED: program.set_repeat_advanced(json.loads(qdict['advanced_schedule_data']), int(qdict['interval']), repeat_start_date) elif qdict['schedule_type'] == ProgramType.WEEKLY_ADVANCED: program.set_weekly_advanced(json.loads(qdict['weekly_schedule_data'])) elif qdict['schedule_type'] == ProgramType.WEEKLY_WEATHER: program.set_weekly_weather(int(qdict['weather_irrigation_min']), int(qdict['weather_irrigation_max']), int(qdict['weather_run_max']), int(qdict['weather_pause_ratio'])/100.0, json.loads(qdict['weather_pems_data']), ) elif qdict['schedule_type'] == ProgramType.CUSTOM: program.modulo = int(qdict['interval'])*1440 program.manual = False program.start = repeat_start_date program.schedule = json.loads(qdict['custom_schedule_data']) if program.index < 0: programs.add_program(program) Timer(0.1, programs.calculate_balances).start() raise web.seeother('/programs')
def FTP_download(self): try: # read command file and save to ramdisk self.ftp.retrbinary("RETR " + plugin_options['loc'] + "data.txt", open("/home/pi/ramdisk/data.txt", 'wb').write) fs = file("/home/pi/ramdisk/data.txt", 'r') obsahaut = fs.readline() fs.close() log.debug( NAME, _(u'FTP received data from file data.txt') + ': ' + str(obsahaut)) change = False if (obsahaut == "AU"): # scheduller options.manual_mode = False log.info(NAME, _(u'Scheduler mode is activated.')) change = True if (obsahaut == "MA"): # manual options.manual_mode = True log.info(NAME, _(u'Manual mode is activated.')) change = True for num_output in range(0, options.output_count): s = 'Z' + str(num_output) if (obsahaut == s): # stations xx ON options.manual_mode = True log.info(NAME, _(u'Manual mode is activated.')) stations.activate(num_output) log.info(NAME, _(u'Activated stations') + ': ' + str(num_output + 1)) change = True s = 'V' + str(num_output) if (obsahaut == s): # stations xx OFF options.manual_mode = True log.info(NAME, _(u'Manual mode is activated.')) stations.deactivate(num_output) log.info( NAME, _(u'Deactivated stations') + ': ' + str(num_output + 1)) change = True for program in programs.get(): s = 'P' + str(program.index + 1) if (obsahaut == s): # Run-now program xx options.manual_mode = False log.info(NAME, _(u'Scheduler mode is activated.')) programs.run_now(program.index) log.info(NAME, _(u'Activated program') + ': ' + str(program.name)) self._sleep(2) change = True program.index + 1 if (obsahaut == "STOP"): # stop all stations and disable scheduler options.scheduler_enabled = False programs.run_now_program = None run_once.clear() log.finish_run(None) stations.clear() log.info(NAME, _(u'Stop all stations and disable system.')) change = True if (obsahaut == "START"): # enable scheduler options.scheduler_enabled = True log.info(NAME, _(u'Enable system.')) change = True if (change): # delete data.txt command now is OK fs = open('/home/pi/ramdisk/data.txt', 'w') fs.write('OK') fs.close() FTP_upload(self) # send to server actual data # TODO save_to_options except Exception: log.clear(NAME) log.info( NAME, _(u'Remote FTP control settings') + ':\n' + traceback.format_exc()) pass
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
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
def run(self): log.clear(NAME) disable_text = True while not self._stop_event.is_set(): try: if plugin_options['use_button']: # if button plugin is enabled disable_text = True actual_buttons = read_buttons() # led_outputs(actual_buttons) # for test for i in range(8): tb = "button" + str(i) if actual_buttons == i and plugin_options[ tb] == "reboot": log.info( NAME, datetime_string() + ': ' + _(u'System reboot')) stations.clear() report_rebooted() reboot(block=True) # Linux HW software self._sleep(1) if actual_buttons == i and plugin_options[ tb] == "pwrOff": log.info( NAME, datetime_string() + ': ' + _(u'System shutdown')) stations.clear() report_poweroff() poweroff(wait=10, block=True) # shutdown HW system self._sleep(1) if actual_buttons == i and plugin_options[ tb] == "stopAll": log.info( NAME, datetime_string() + ': ' + _(u'Selected stations now stopped')) set_stations_in_scheduler_off() self._sleep(1) if actual_buttons == i and plugin_options[ tb] == "schedEn": log.info( NAME, datetime_string() + ': ' + _(u'Scheduler is now enabled')) options.scheduler_enabled = True self._sleep(1) if actual_buttons == i and plugin_options[ tb] == "schedDis": log.info( NAME, datetime_string() + ': ' + _(u'Scheduler is now disabled')) options.scheduler_enabled = False self._sleep(1) if actual_buttons == i and plugin_options[ tb] == "RunP1": log.info( NAME, datetime_string() + ': ' + _(u'Run now program 1')) for program in programs.get(): if (program.index == 0): # Run-now program 1 options.manual_mode = False if plugin_options['first_stop']: log.finish_run(None) stations.clear() programs.run_now(program.index) program.index + 1 self._sleep(1) if actual_buttons == i and plugin_options[ tb] == "RunP2": log.info( NAME, datetime_string() + ': ' + _(u'Run now program 2')) for program in programs.get(): if (program.index == 1): # Run-now program 2 options.manual_mode = False if plugin_options['first_stop']: log.finish_run(None) stations.clear() programs.run_now(program.index) program.index + 1 self._sleep(1) if actual_buttons == i and plugin_options[ tb] == "RunP3": log.info( NAME, datetime_string() + ': ' + _(u'Run now program 3')) for program in programs.get(): if (program.index == 2): # Run-now program 3 options.manual_mode = False if plugin_options['first_stop']: log.finish_run(None) stations.clear() programs.run_now(program.index) program.index + 1 self._sleep(1) if actual_buttons == i and plugin_options[ tb] == "RunP4": log.info( NAME, datetime_string() + ': ' + _(u'Run now program 4')) for program in programs.get(): if (program.index == 3): # Run-now program 4 options.manual_mode = False if plugin_options['first_stop']: log.finish_run(None) stations.clear() programs.run_now(program.index) program.index + 1 self._sleep(1) if actual_buttons == i and plugin_options[ tb] == "RunP5": log.info( NAME, datetime_string() + ': ' + _('Run now program 5')) for program in programs.get(): if (program.index == 4): # Run-now program 5 options.manual_mode = False if plugin_options['first_stop']: log.finish_run(None) stations.clear() programs.run_now(program.index) program.index + 1 self._sleep(1) if actual_buttons == i and plugin_options[ tb] == "RunP6": log.info( NAME, datetime_string() + ': ' + _(u'Run now program 6')) for program in programs.get(): if (program.index == 5): # Run-now program 6 options.manual_mode = False if plugin_options['first_stop']: log.finish_run(None) stations.clear() programs.run_now(program.index) program.index + 1 self._sleep(1) if actual_buttons == i and plugin_options[ tb] == "RunP7": log.info( NAME, datetime_string() + ': ' + _(u'Run now program 7')) for program in programs.get(): if (program.index == 6): # Run-now program 7 options.manual_mode = False if plugin_options['first_stop']: log.finish_run(None) stations.clear() programs.run_now(program.index) program.index + 1 self._sleep(1) if actual_buttons == i and plugin_options[ tb] == "RunP8": log.info( NAME, datetime_string() + ': ' + _(u'Run now program 8')) for program in programs.get(): if (program.index == 7): # Run-now program 8 options.manual_mode = False if plugin_options['first_stop']: log.finish_run(None) stations.clear() programs.run_now(program.index) program.index + 1 self._sleep(1) self._sleep(1) else: # text on the web if plugin is disabled if disable_text: log.clear(NAME) log.info(NAME, _(u'Button plug-in is disabled.')) disable_text = False self._sleep(1) except Exception: log.clear(NAME) log.error( NAME, _(u'Button plug-in') + ':\n' + traceback.format_exc()) self._sleep(60) pass
def _save_logs(self): from ospy.programs import programs, ProgramType result = [] if options.run_log or any(program.type == ProgramType.WEEKLY_WEATHER for program in programs.get()): result = self._log['Run'] options.logged_runs = result