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)
def GET(self): qdict = web.input() if 'clear' in qdict: log.clear_runs() raise web.seeother('/log') if 'clearEM' in qdict: logEM.clear_email() raise web.seeother('/log') if 'csv' in qdict: events = log.finished_runs() + log.active_runs() data = "Date, Start Time, Zone, Duration, Program\n" for interval in events: # return only records that are visible on this day: duration = (interval['end'] - interval['start']).total_seconds() minutes, seconds = divmod(duration, 60) data += ', '.join([ interval['start'].strftime("%Y-%m-%d"), interval['start'].strftime("%H:%M:%S"), str(interval['station']), "%02d:%02d" % (minutes, seconds), interval['program_name'] ]) + '\n' web.header('Content-Type', 'text/csv') web.header('Content-Disposition', 'attachment; filename="log.csv"') return data if 'csvEM' in qdict: events = logEM.finished_email() data = "Date, Time, Subject, Body, Status\n" for interval in events: data += ', '.join([ interval['time'], interval['date'], str(interval['subject']), str(interval['body']), str(interval['status']), ]) + '\n' web.header('Content-Type', 'text/csv') web.header('Content-Disposition', 'attachment; filename="log_email.csv"') return data watering_records = log.finished_runs() email_records = logEM.finished_email() return self.core_render.log(watering_records, email_records)
def combined_schedule(start_time, end_time): current_time = datetime.datetime.now() if current_time < start_time: result = predicted_schedule(start_time, end_time) elif current_time > end_time: result = [entry for entry in log.finished_runs() if start_time <= entry['start'] <= end_time or start_time <= entry['end'] <= end_time] else: result = log.finished_runs() result += log.active_runs() predicted = predicted_schedule(start_time, end_time) result += [entry for entry in predicted if current_time <= entry['start'] <= end_time] return result
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
def set_stations_in_scheduler_off(): """Stoping selected station in scheduler.""" current_time = datetime.datetime.now() check_start = current_time - datetime.timedelta(days=1) check_end = current_time + datetime.timedelta(days=1) # In manual mode we cannot predict, we only know what is currently running and the history if options.manual_mode: active = log.finished_runs() + log.active_runs() else: active = combined_schedule(check_start, check_end) ending = False # active stations for entry in active: for used_stations in tank_options[ 'used_stations']: # selected stations for stoping if entry[ 'station'] == used_stations: # is this station in selected stations? log.finish_run(entry) # save end in log stations.deactivate(entry['station']) # stations to OFF ending = True if ending: log.info(NAME, _(u'Stoping stations in scheduler'))
def GET(self): qdict = web.input() if 'clear' in qdict: log.clear_runs() raise web.seeother('/log') if 'csv' in qdict: events = log.finished_runs() + log.active_runs() data = "Date, Start Time, Zone, Duration, Program\n" for interval in events: # return only records that are visible on this day: duration = (interval['end'] - interval['start']).total_seconds() minutes, seconds = divmod(duration, 60) data += ', '.join([ interval['start'].strftime("%Y-%m-%d"), interval['start'].strftime("%H:%M:%S"), str(interval['station']), "%02d:%02d" % (minutes, seconds), interval['program_name'] ]) + '\n' web.header('Content-Type', 'text/csv') web.header('Content-Disposition', 'attachment; filename="log.csv"') return data return self.core_render.log()
def combined_schedule(start_time, end_time): current_time = datetime.datetime.now() if current_time < start_time: result = predicted_schedule(start_time, end_time) elif current_time > end_time: result = [ entry for entry in log.finished_runs() if start_time <= entry['start'] <= end_time or start_time <= entry['end'] <= end_time ] else: result = log.finished_runs() result += log.active_runs() predicted = predicted_schedule(start_time, end_time) result += [ entry for entry in predicted if current_time <= entry['start'] <= end_time ] return result
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)
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 GET(self): logger.debug('GET logs ' + self.__class__.__name__) return [self._runlog_to_dict(fr) for fr in log.finished_runs()]
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
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): send_msg = False # send get data if change (rain, end program .... last_rain = False en_rain = True en_line = True en_line2 = True # ex: tank=100&rain=1&humi=55&line=1&lastrun=15.4.2016&station=kurnik&duration=5min 3sec&api=a1b2v5f4 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 %0-100 in water tank for tank=xx in get data 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 temper = "" # temperature from air temp plugin finished_count = len([run for run in log.finished_runs() if not run['blocked']]) while not self._stop.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_humi_monitor tank = tank_humi_monitor.get_tank() if tank < 0: # -1 is error I2C device for ping not found in tank_humi_monitor tank = "" except Exception: tank = "" ### 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 tank_humi_monitor humi = int(tank_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 temper = air_temp_humi.DS18B20_read_string_data() except Exception: temper = " " 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 += ('&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 += ('&temper=' + sanity_msg(temper)) body += ('&api=' + remote_options['api']) # API password self.try_send(body) # Send GET data to remote server send_msg = False # Disable send data self._sleep(2) except Exception: log.error(NAME, _('Remote plug-in') + ':\n' + traceback.format_exc()) self._sleep(60)
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)
def get_report(index): result = None 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 (options.lang == 'cs_CZ'): if station_state and lcd_options[ 'd_running_stations']: # print on LCD only this text if index == 0 or index == 2 or index == 4 or index == 6 or index == 8 or index == 10 or index == 12 or index == 14 or index == 16 or index == 18 or index == 20: # start text to 16x1 result = "Stanice v chodu:" elif index == 1 or index == 3 or index == 5 or index == 7 or index == 9 or index == 11 or index == 13 or index == 15 or index == 17 or index == 19 or index == 21: result = station_result return result else: if index == 0: # start text to 16x1 if lcd_options['d_system_name']: result = "ID systemu:" else: result = None elif index == 1: if lcd_options['d_system_name']: result = options.name else: result = None elif index == 2: if lcd_options['d_sw_version_date']: result = "FW Verze:" else: result = None elif index == 3: if lcd_options['d_sw_version_date']: result = version.ver_str + ' (' + version.ver_date + ')' else: result = None elif index == 4: if lcd_options['d_ip']: result = "IP adresa:" else: result = None elif index == 5: if lcd_options['d_ip']: ip = helpers.get_ip() result = str(ip) else: result = None elif index == 6: if lcd_options['d_port']: result = "Port:" else: result = None elif index == 7: if lcd_options['d_port']: result = str(options.web_port) else: result = None elif index == 8: if lcd_options['d_cpu_temp']: result = "Teplota CPU:" else: result = None elif index == 9: if lcd_options['d_cpu_temp']: result = helpers.get_cpu_temp( options.temp_unit) + ' ' + options.temp_unit else: result = None elif index == 10: if lcd_options['d_time_date']: result = datetime.now().strftime('Dat %d.%m.%Y') else: result = None elif index == 11: if lcd_options['d_time_date']: result = datetime.now().strftime('Cas %H:%M:%S') else: result = None elif index == 12: if lcd_options['d_uptime']: result = "V provozu:" else: result = None elif index == 13: if lcd_options['d_uptime']: result = helpers.uptime() else: result = None elif index == 14: if lcd_options['d_rain_sensor']: result = "Cidlo deste:" else: result = None elif index == 15: if lcd_options['d_rain_sensor']: if inputs.rain_sensed(): result = "aktivni" else: result = "neaktivni" else: result = None elif index == 16: if lcd_options['d_last_run']: result = 'Naposledy bezel' else: result = None elif index == 17: if lcd_options['d_last_run']: 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'] result = result.replace('Run-Once', 'Jednorazovy') result = result.replace('Manual', 'Rucne') else: result = 'zadny program' else: result = None elif index == 18: if lcd_options['d_pressure_sensor']: result = "Cidlo tlaku:" else: result = None elif index == 19: if lcd_options['d_pressure_sensor']: try: from plugins import pressure_monitor state_press = pressure_monitor.get_check_pressure() if state_press: result = "neaktivni" else: result = "aktivni" except Exception: result = "neni k dispozici" else: result = None elif index == 20: if lcd_options['d_water_tank_level']: result = "Nadrz s vodou:" else: result = None elif index == 21: if lcd_options['d_water_tank_level']: try: from plugins import tank_humi_monitor cm = tank_humi_monitor.get_sonic_tank_cm() if cm > 0: result = str(cm) + ' cm' else: result = "chyba - I2C zarizeni nenalezeno!" except Exception: result = "neni k dispozici" else: result = None return ASCI_convert(result) if (options.lang == 'en_US') or (options.lang == 'default'): if station_state and lcd_options[ 'd_running_stations']: # print on LCD only this text if index == 0 or index == 2 or index == 4 or index == 6 or index == 8 or index == 10 or index == 12 or index == 14 or index == 16 or index == 18 or index == 20: # start text to 16x1 result = "Running stations:" elif index == 1 or index == 3 or index == 5 or index == 7 or index == 9 or index == 11 or index == 13 or index == 15 or index == 17 or index == 19 or index == 21: result = station_result return result else: if index == 0: if lcd_options['d_system_name']: result = options.name else: result = None elif index == 1: if lcd_options['d_system_name']: result = "Irrigation system" else: result = None elif index == 2: if lcd_options['d_sw_version_date']: result = "SW Version:" else: result = None elif index == 3: if lcd_options['d_sw_version_date']: result = version.ver_str + ' (' + version.ver_date + ')' else: result = None elif index == 4: if lcd_options['d_ip']: result = "My IP is:" else: result = None elif index == 5: if lcd_options['d_ip']: ip = helpers.get_ip() result = str(ip) else: result = None elif index == 6: if lcd_options['d_port']: result = "My Port is:" else: result = None elif index == 7: if lcd_options['d_port']: result = str(options.web_port) else: result = None elif index == 8: if lcd_options['d_cpu_temp']: result = "CPU Temperature:" else: result = None elif index == 9: if lcd_options['d_cpu_temp']: result = helpers.get_cpu_temp( options.temp_unit) + ' ' + options.temp_unit else: result = None elif index == 10: if lcd_options['d_time_date']: result = datetime.now().strftime('Date: %d.%m.%Y') else: result = None elif index == 11: if lcd_options['d_time_date']: result = datetime.now().strftime('Time: %H:%M:%S') else: result = None elif index == 12: if lcd_options['d_uptime']: result = "System Uptime:" else: result = None elif index == 13: if lcd_options['d_uptime']: result = helpers.uptime() else: result = None elif index == 14: if lcd_options['d_rain_sensor']: result = "Rain Sensor:" else: result = None elif index == 15: if lcd_options['d_rain_sensor']: if inputs.rain_sensed(): result = "Active" else: result = "Inactive" else: result = None elif index == 16: if lcd_options['d_last_run']: result = 'Last Program:' else: result = None elif index == 17: if lcd_options['d_last_run']: finished = [ run for run in log.finished_runs() if not run['blocked'] ] if finished: result = finished[-1]['start'].strftime( '%H:%M: ') + finished[-1]['program_name'] result = result.replace('Run-Once', 'Jednorazovy') else: result = 'None' else: result = None elif index == 18: if lcd_options['d_pressure_sensor']: result = "Pressure Sensor:" else: result = None elif index == 19: if lcd_options['d_pressure_sensor']: try: from plugins import pressure_monitor state_press = pressure_monitor.get_check_pressure() if state_press: result = "GPIO is HIGH" else: result = "GPIO is LOW" except Exception: result = "Not Available" else: result = None elif index == 20: if lcd_options['d_water_tank_level']: result = "Water Tank Level:" else: result = None elif index == 21: if lcd_options['d_water_tank_level']: try: from plugins import tank_humi_monitor cm = tank_humi_monitor.get_sonic_tank_cm() if cm > 0: result = str(cm) + ' cm' else: result = "Error - I2C Device Not Found!" except Exception: result = "Not Available" else: result = None return result
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 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
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)
def get_report(index): result = None if (options.lang == 'cs_CZ'): if index == 0: # start text to 16x1 if lcd_options['d_system_name']: result = "ID systemu:" else: result = None elif index == 1: if lcd_options['d_system_name']: result = options.name else: result = None elif index == 2: if lcd_options['d_sw_version_date']: result = "FW Verze:" else: result = None elif index == 3: if lcd_options['d_sw_version_date']: result = version.ver_str + ' (' + version.ver_date + ')' else: result = None elif index == 4: if lcd_options['d_ip']: result = "IP adresa:" else: result = None elif index == 5: if lcd_options['d_ip']: ip = helpers.get_ip() result = str(ip) else: result = None elif index == 6: if lcd_options['d_port']: result = "Port:" else: result = None elif index == 7: if lcd_options['d_port']: result = str(options.web_port) else: result = None elif index == 8: if lcd_options['d_cpu_temp']: result = "Teplota CPU:" else: result = None elif index == 9: if lcd_options['d_cpu_temp']: result = helpers.get_cpu_temp(options.temp_unit) + ' ' + options.temp_unit else: result = None elif index == 10: if lcd_options['d_time_date']: result = datetime.now().strftime('Dat %d.%m.%Y') else: result = None elif index == 11: if lcd_options['d_time_date']: result = datetime.now().strftime('Cas %H:%M:%S') else: result = None elif index == 12: if lcd_options['d_uptime']: result = "V provozu:" else: result = None elif index == 13: if lcd_options['d_uptime']: result = helpers.uptime() else: result = None elif index == 14: if lcd_options['d_rain_sensor']: result = "Cidlo deste:" else: result = None elif index == 15: if lcd_options['d_rain_sensor']: if inputs.rain_sensed(): result = "aktivni" else: result = "neaktivni" else: result = None elif index == 16: if lcd_options['d_last_run']: result = 'Naposledy bezel' else: result = None elif index == 17: if lcd_options['d_last_run']: 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'] result = result.replace('Run-Once', 'Jednorazovy') result = result.replace('Manual', 'Rucne') else: result = 'zadny program' else: result = None elif index == 18: if lcd_options['d_pressure_sensor']: result = "Cidlo tlaku:" else: result = None elif index == 19: if lcd_options['d_pressure_sensor']: try: from plugins import pressure_monitor state_press = pressure_monitor.get_check_pressure() if state_press: result = "neaktivni" else: result = "aktivni" except Exception: result = "neni k dispozici" else: result = None elif index == 20: if lcd_options['d_water_tank_level']: result = "Nadrz s vodou:" else: result = None elif index == 21: if lcd_options['d_water_tank_level']: try: from plugins import tank_humi_monitor cm = tank_humi_monitor.get_sonic_tank_cm() if cm > 0: result = str(cm) + ' cm' else: result = "chyba - I2C zarizeni nenalezeno!" except Exception: result = "neni k dispozici" else: result = None elif index == 22: if lcd_options['d_temperature']: result = "Teplota DS1-6:" else: result = None elif index == 23: if lcd_options['d_temperature']: try: from plugins import air_temp_humi result = air_temp_humi.DS18B20_read_string_data() except Exception: result = "neni k dispozici" else: result = None elif index == 24: if lcd_options['d_running_stations']: result = "Stanice v chodu:" else: result = None elif index == 25: if get_active_state()==False: result = "nic nebezi" else: result = get_active_state() return ASCI_convert(result) if (options.lang == 'sk_SK'): if index == 0: # start text to 16x1 if lcd_options['d_system_name']: result = "ID systemu:" else: result = None elif index == 1: if lcd_options['d_system_name']: result = options.name else: result = None elif index == 2: if lcd_options['d_sw_version_date']: result = "FW Verzia:" else: result = None elif index == 3: if lcd_options['d_sw_version_date']: result = version.ver_str + ' (' + version.ver_date + ')' else: result = None elif index == 4: if lcd_options['d_ip']: result = "IP adresa:" else: result = None elif index == 5: if lcd_options['d_ip']: ip = helpers.get_ip() result = str(ip) else: result = None elif index == 6: if lcd_options['d_port']: result = "Port:" else: result = None elif index == 7: if lcd_options['d_port']: result = str(options.web_port) else: result = None elif index == 8: if lcd_options['d_cpu_temp']: result = "Teplota CPU:" else: result = None elif index == 9: if lcd_options['d_cpu_temp']: result = helpers.get_cpu_temp(options.temp_unit) + ' ' + options.temp_unit else: result = None elif index == 10: if lcd_options['d_time_date']: result = datetime.now().strftime('Dat %d.%m.%Y') else: result = None elif index == 11: if lcd_options['d_time_date']: result = datetime.now().strftime('Cas %H:%M:%S') else: result = None elif index == 12: if lcd_options['d_uptime']: result = "V prevadzke:" else: result = None elif index == 13: if lcd_options['d_uptime']: result = helpers.uptime() else: result = None elif index == 14: if lcd_options['d_rain_sensor']: result = "Cidlo dazda:" else: result = None elif index == 15: if lcd_options['d_rain_sensor']: if inputs.rain_sensed(): result = "aktivny" else: result = "neaktivny" else: result = None elif index == 16: if lcd_options['d_last_run']: result = 'Naposledy bezal' else: result = None elif index == 17: if lcd_options['d_last_run']: 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'] result = result.replace('Run-Once', 'Jednorazovy') result = result.replace('Manual', 'Rucne') else: result = 'ziadny program' else: result = None elif index == 18: if lcd_options['d_pressure_sensor']: result = "Cidlo tlaku:" else: result = None elif index == 19: if lcd_options['d_pressure_sensor']: try: from plugins import pressure_monitor state_press = pressure_monitor.get_check_pressure() if state_press: result = "neaktivny" else: result = "aktivny" except Exception: result = "neni k dispozicii" else: result = None elif index == 20: if lcd_options['d_water_tank_level']: result = "Nadrz s vodou:" else: result = None elif index == 21: if lcd_options['d_water_tank_level']: try: from plugins import tank_humi_monitor cm = tank_humi_monitor.get_sonic_tank_cm() if cm > 0: result = str(cm) + ' cm' else: result = "chyba - I2C zarizeni nenajdene!" except Exception: result = "neni k dispozicii" else: result = None elif index == 22: if lcd_options['d_temperature']: result = "Teplota DS1-6:" else: result = None elif index == 23: if lcd_options['d_temperature']: try: from plugins import air_temp_humi result = air_temp_humi.DS18B20_read_string_data() except Exception: result = "neni k dispozicii" else: result = None elif index == 24: if lcd_options['d_running_stations']: result = "Stanice v chode:" else: result = None elif index == 25: if get_active_state()==False: result = "nic nebezi" else: result = get_active_state() return ASCI_convert(result) if (options.lang == 'en_US') or (options.lang == 'default'): if index == 0: if lcd_options['d_system_name']: result = options.name else: result = None elif index == 1: if lcd_options['d_system_name']: result = "Irrigation system" else: result = None elif index == 2: if lcd_options['d_sw_version_date']: result = "SW Version:" else: result = None elif index == 3: if lcd_options['d_sw_version_date']: result = version.ver_str + ' (' + version.ver_date + ')' else: result = None elif index == 4: if lcd_options['d_ip']: result = "My IP is:" else: result = None elif index == 5: if lcd_options['d_ip']: ip = helpers.get_ip() result = str(ip) else: result = None elif index == 6: if lcd_options['d_port']: result = "My Port is:" else: result = None elif index == 7: if lcd_options['d_port']: result = str(options.web_port) else: result = None elif index == 8: if lcd_options['d_cpu_temp']: result = "CPU Temperature:" else: result = None elif index == 9: if lcd_options['d_cpu_temp']: result = helpers.get_cpu_temp(options.temp_unit) + ' ' + options.temp_unit else: result = None elif index == 10: if lcd_options['d_time_date']: result = datetime.now().strftime('Date: %d.%m.%Y') else: result = None elif index == 11: if lcd_options['d_time_date']: result = datetime.now().strftime('Time: %H:%M:%S') else: result = None elif index == 12: if lcd_options['d_uptime']: result = "System Uptime:" else: result = None elif index == 13: if lcd_options['d_uptime']: result = helpers.uptime() else: result = None elif index == 14: if lcd_options['d_rain_sensor']: result = "Rain Sensor:" else: result = None elif index == 15: if lcd_options['d_rain_sensor']: if inputs.rain_sensed(): result = "Active" else: result = "Inactive" else: result = None elif index == 16: if lcd_options['d_last_run']: result = 'Last Program:' else: result = None elif index == 17: if lcd_options['d_last_run']: finished = [run for run in log.finished_runs() if not run['blocked']] if finished: result = finished[-1]['start'].strftime('%H:%M: ') + finished[-1]['program_name'] result = result.replace('Run-Once', 'Jednorazovy') else: result = 'None' else: result = None elif index == 18: if lcd_options['d_pressure_sensor']: result = "Pressure Sensor:" else: result = None elif index == 19: if lcd_options['d_pressure_sensor']: try: from plugins import pressure_monitor state_press = pressure_monitor.get_check_pressure() if state_press: result = "GPIO is HIGH" else: result = "GPIO is LOW" except Exception: result = "Not Available" else: result = None elif index == 20: if lcd_options['d_water_tank_level']: result = "Water Tank Level:" else: result = None elif index == 21: if lcd_options['d_water_tank_level']: try: from plugins import tank_humi_monitor cm = tank_humi_monitor.get_sonic_tank_cm() if cm > 0: result = str(cm) + ' cm' else: result = "Error - I2C Device Not Found!" except Exception: result = "Not Available" else: result = None elif index == 22: if lcd_options['d_temperature']: result = "DS Temperature:" else: result = None elif index == 23: if lcd_options['d_temperature']: try: from plugins import air_temp_humi result = air_temp_humi.DS18B20_read_string_data() except Exception: result = "Not Available" else: result = None elif index == 24: if lcd_options['d_running_stations']: result = "Station running:" else: result = None elif index == 25: if get_active_state()==False: result = "nothing running" else: result = get_active_state() return result
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=10): 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(): station.balance[calc_day]['eto'] = weather.get_eto( calc_day) station.balance[calc_day]['rain'] = 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
def sms_check(self): """Control and processing SMS""" try: import gammu except Exception: log.error(NAME, _('SMS Modem plug-in') + ':\n' + traceback.format_exc()) tel1 = sms_options['tel1'] tel2 = sms_options['tel2'] comm1 = sms_options['txt1'] comm2 = sms_options['txt2'] comm3 = sms_options['txt3'] comm4 = sms_options['txt4'] comm5 = sms_options['txt5'] comm6 = sms_options['txt6'] comm7 = sms_options['txt7'] comm8 = sms_options['txt8'] comm9 = sms_options['txt9'] sm = gammu.StateMachine() sm.ReadConfig() try: sm.Init() log.debug(NAME, datetime_string() + ': ' + _('Checking SMS...')) except: log.error(NAME, _('SMS Modem plug-in') + ':\n' + traceback.format_exc()) self._sleep(60) if sms_options[ "use_strength"]: # print strength signal in status Window every check SMS signal = sm.GetSignalQuality( ) # list: SignalPercent, SignalStrength, BitErrorRate log.info( NAME, datetime_string() + ': ' + _('Signal') + ': ' + str(signal['SignalPercent']) + '% ' + str(signal['SignalStrength']) + 'dB') status = sm.GetSMSStatus() remain = status['SIMUsed'] + status['PhoneUsed'] + status['TemplatesUsed'] sms = [] start = True while remain > 0: if start: cursms = sm.GetNextSMS(Start=True, Folder=0) start = False else: cursms = sm.GetNextSMS(Location=cursms[0]['Location'], Folder=0) remain = remain - len(cursms) sms.append(cursms) data = gammu.LinkSMS(sms) for x in data: v = gammu.DecodeSMS(x) m = x[0] print '%-15s: %s' % ('Sender', m['Number']) print '%-15s: %s' % ('Date', str(m['DateTime'])) print '%-15s: %s' % ('State', m['State']) print '%-15s: %s' % ('SMS command', m['Text']) if (m['Number'] == tel1) or (m['Number'] == tel2): # If telephone is admin 1 or admin 2 log.info(NAME, datetime_string() + ': ' + _('SMS from admin')) if m['State'] == "UnRead": # If SMS is unread log.clear(NAME) if m['Text'] == comm1: # If command = comm1 (info - send SMS to admin phone1 and phone2) log.info( NAME, _('Command') + ' ' + comm1 + ' ' + _('is processed')) # send 1/2 SMS with text 1 up = helpers.uptime() temp = helpers.get_cpu_temp( options.temp_unit) + ' ' + options.temp_unit ip = str(helpers.get_ip()) ver = version.ver_date datastr = ('SMS 1/2. ' + datetime_string() + ', TEMP: ' + temp + ', IP: ' + ip + ', SW: ' + ver + ', UP: ' + up) message = { 'Text': datastr, 'SMSC': { 'Location': 1 }, 'Number': m['Number'], } sm.SendSMS(message) # send sms 1/2 log.info(NAME, datastr) # send 2/2 SMS with text 2 if inputs.rain_sensed(): rain = "Active" else: rain = "Inactive" try: from plugins import pressure_monitor state_press = pressure_monitor.get_check_pressure() if state_press: press = "High" else: press = "Low" except Exception: press = "N/A" finished = [ run for run in log.finished_runs() if not run['blocked'] ] if finished: last_prog = finished[-1]['start'].strftime( '%H:%M: ') + finished[-1]['program_name'] else: last_prog = 'None' datastr = ('SMS 2/2. ' + 'RAIN: ' + rain + ', PRESS: ' + press + ', LAST: ' + last_prog) message = { 'Text': datastr, 'SMSC': { 'Location': 1 }, 'Number': m['Number'], } sm.SendSMS(message) # send sms 2/2 log.info(NAME, datastr) log.info( NAME, _('Command') + ': ' + comm1 + ' ' + _('was processed and confirmation was sent as SMS to') + ': ' + m['Number']) sm.DeleteSMS(m['Folder'], m['Location']) # SMS deleted log.info(NAME, _('Received SMS was deleted')) elif m['Text'] == comm2: # If command = comm2 (stop - scheduler) log.info( NAME, _('Command') + ' ' + comm2 + ' ' + _('is processed')) options.scheduler_enabled = False log.finish_run(None) stations.clear() message = { 'Text': 'Command: ' + comm2 + ' was processed', 'SMSC': { 'Location': 1 }, 'Number': m['Number'], } sm.SendSMS(message) log.info( NAME, _('Command') + ': ' + comm2 + ' ' + _('was processed and confirmation was sent as SMS to') + ': ' + m['Number']) sm.DeleteSMS(m['Folder'], m['Location']) log.info(NAME, _('Received SMS was deleted')) elif m['Text'] == comm3: # If command = comm3 (start - scheduler) log.info( NAME, _('Command') + ' ' + comm3 + ' ' + _('is processed')) options.scheduler_enabled = True message = { 'Text': 'Command: ' + comm3 + ' was processed', 'SMSC': { 'Location': 1 }, 'Number': m['Number'], } sm.SendSMS(message) log.info( NAME, _('Command') + ': ' + comm3 + ' ' + _('was processed and confirmation was sent as SMS to') + ': ' + m['Number']) sm.DeleteSMS(m['Folder'], m['Location']) log.info(NAME, _('Received SMS was deleted')) elif m['Text'] == comm4: # If command = comm4 (reboot system) log.info( NAME, _('Command') + ' ' + comm4 + ' ' + _('is processed')) message = { 'Text': 'Command: ' + comm4 + ' was processed', 'SMSC': { 'Location': 1 }, 'Number': m['Number'], } sm.SendSMS(message) log.info( NAME, _('Command') + ': ' + comm4 + ' ' + _('was processed and confirmation was sent as SMS to') + ': ' + m['Number']) sm.DeleteSMS(m['Folder'], m['Location']) log.info( NAME, _('Received SMS was deleted and system is now reboot')) reboot() # restart linux system elif m['Text'] == comm5: # If command = comm5 (poweroff system) log.info( NAME, _('Command') + ' ' + comm5 + ' ' + _('is processed')) message = { 'Text': 'Command: ' + comm5 + ' was processed', 'SMSC': { 'Location': 1 }, 'Number': m['Number'], } sm.SendSMS(message) log.info( NAME, _('Command') + ': ' + comm5 + ' ' + _('was processed and confirmation was sent as SMS to') + ': ' + m['Number']) sm.DeleteSMS(m['Folder'], m['Location']) log.info( NAME, _('Received SMS was deleted and system is now poweroff' )) poweroff() # poweroff linux system elif m['Text'] == comm6: # If command = comm6 (update ospi system) log.info( NAME, _('Command') + ' ' + comm6 + ' ' + _('is processed')) message = { 'Text': 'Command: ' + comm6 + ' was processed', 'SMSC': { 'Location': 1 }, 'Number': m['Number'], } sm.SendSMS(message) log.info( NAME, _('Command') + ': ' + comm6 + ' ' + _('was processed and confirmation was sent as SMS to') + ': ' + m['Number']) try: from plugins.system_update import perform_update perform_update() log.info( NAME, _('Received SMS was deleted, update was performed and program will restart' )) except ImportError: log.info( NAME, _('Received SMS was deleted, but could not perform update' )) sm.DeleteSMS(m['Folder'], m['Location']) elif m['Text'] == comm7: # If command = comm7 (send email with foto from webcam) log.info( NAME, _('Command') + ' ' + comm7 + ' ' + _('is processed')) message = { 'Text': 'Command: ' + comm7 + ' was processed', 'SMSC': { 'Location': 1 }, 'Number': m['Number'], } sm.SendSMS(message) log.info( NAME, _('Command') + ': ' + comm7 + ' ' + _('was processed and confirmation was sent as SMS to') + ': ' + m['Number']) try: from plugins.webcam import get_run_cam, get_image_location get_run_cam() # process save foto to ./data/image.jpg msg = _('SMS plug-in send image file from webcam.') send_email(msg, attach=get_image_location()) except ImportError: log.info( NAME, _('Received SMS was deleted, but could not send email with photo from webcam' )) message = { 'Text': 'Error: not send foto from webcam', 'SMSC': { 'Location': 1 }, 'Number': m['Number'], } sm.SendSMS(message) sm.DeleteSMS(m['Folder'], m['Location']) elif m['Text'] == comm8: # If command = comm8 (send SMS with available commands) log.info( NAME, _('Command') + ' ' + comm8 + ' ' + _('is processed')) message = { 'Text': 'Available commands: ' + comm1 + ',' + comm2 + ',' + comm3 + ',' + comm4 + ',' + comm5 + ',' + comm6 + ',' + comm7 + ',' + comm8 + ',' + comm9 + 'xx', 'SMSC': { 'Location': 1 }, 'Number': m['Number'], } sm.SendSMS(message) log.info( NAME, _('Command') + ': ' + comm8 + ' ' + _('was processed and confirmation was sent as SMS to') + ': ' + m['Number']) sm.DeleteSMS(m['Folder'], m['Location']) log.info(NAME, _('Received SMS was deleted')) elif m['Text'][0:len( comm9 )] == comm9: # If command = lenght char comm9 (run now program xx) num = m['Text'][len( comm9 ):] # number from sms text example: run36 -> num=36 log.info( NAME, _('Command') + ' ' + comm9 + ' ' + _('is processed')) index = int(num) if index <= programs.count( ): # if program number from sms text exists in program db log.finish_run(None) stations.clear() prog = int(index - 1) programs.run_now(prog) log.info( NAME, _('Program') + ': ' + str(index) + ' ' + _('now run')) message = { 'Text': 'Program: ' + str(index) + ' now run', 'SMSC': { 'Location': 1 }, 'Number': m['Number'], } else: message = { 'Text': 'Program: ' + str(index) + ' no exists!', 'SMSC': { 'Location': 1 }, 'Number': m['Number'], } sm.SendSMS(message) log.info( NAME, _('Command') + ': ' + str(m['Text']) + ' ' + _('was processed and confirmation was sent as SMS to') + ': ' + m['Number']) sm.DeleteSMS(m['Folder'], m['Location']) log.info(NAME, _('Received SMS was deleted')) else: # If SMS command is not defined sm.DeleteSMS(m['Folder'], m['Location']) log.info( NAME, _('Received command') + ' ' + m['Text'] + ' ' + _('is not defined!')) else: # If SMS was read sm.DeleteSMS(m['Folder'], m['Location']) log.info(NAME, _('Received SMS was deleted - SMS was read')) else: # If telephone number is not admin 1 or admin 2 phone number sm.DeleteSMS(m['Folder'], m['Location']) log.info(NAME, _('Received SMS was deleted - SMS was not from admin'))
def get_report(index): result = None if index == 0: if lcd_options['d_system_name']: result = ASCI_convert(_('System:')) else: result = None elif index == 1: if lcd_options['d_system_name']: result = ASCI_convert(options.name) else: result = None elif index == 2: if lcd_options['d_sw_version_date']: result = ASCI_convert(_('SW Version:')) else: result = None elif index == 3: if lcd_options['d_sw_version_date']: result = ASCI_convert('{} {}'.format(version.ver_str, version.ver_date)) else: result = None elif index == 4: if lcd_options['d_ip']: result = ASCI_convert(_('My IP is:')) else: result = None elif index == 5: if lcd_options['d_ip']: if options.use_ssl: result = ASCI_convert( ('https://{}:{}').format(helpers.get_ip(), options.web_port)) else: result = ASCI_convert( ('http://{}:{}').format(helpers.get_ip(), options.web_port)) else: result = None elif index == 6: if lcd_options['d_port']: result = ASCI_convert(_('My Port is:')) else: result = None elif index == 7: if lcd_options['d_port']: result = ASCI_convert('{}'.format(options.web_port)) else: result = None elif index == 8: if lcd_options['d_cpu_temp']: result = ASCI_convert(_('CPU Temperature:')) else: result = None elif index == 9: if lcd_options['d_cpu_temp']: result = ASCI_convert('{} {}'.format( helpers.get_cpu_temp(options.temp_unit), options.temp_unit)) else: result = None elif index == 10: if lcd_options['d_time_date']: result = ASCI_convert( _('Date:') + ' {}'.format(datetime.now().strftime('%d.%m.%Y'))) else: result = None elif index == 11: if lcd_options['d_time_date']: result = ASCI_convert( _('Time:') + ' {}'.format(datetime.now().strftime('%H:%M:%S'))) else: result = None elif index == 12: if lcd_options['d_uptime']: result = ASCI_convert(_('System Uptime:')) else: result = None elif index == 13: if lcd_options['d_uptime']: result = ASCI_convert(helpers.uptime()) else: result = None elif index == 14: if lcd_options['d_rain_sensor']: result = ASCI_convert(_('Rain Sensor:')) else: result = None elif index == 15: if lcd_options['d_rain_sensor']: if inputs.rain_sensed(): result = ASCI_convert(_('Active')) else: result = ASCI_convert(_('Inactive')) else: result = None elif index == 16: if lcd_options['d_last_run']: result = ASCI_convert(_('Last Program:')) else: result = None elif index == 17: if lcd_options['d_last_run']: finished = [ run for run in log.finished_runs() if not run['blocked'] ] if finished: str_fin = '{}{}'.format( finished[-1]['program_name'], finished[-1]['start'].strftime(' %d.%m.%Y %H:%M:%S')) result = ASCI_convert(str_fin) else: result = ASCI_convert(_('None')) else: result = None elif index == 18: if lcd_options['d_pressure_sensor']: result = ASCI_convert(_('Pressure Sensor:')) else: result = None elif index == 19: if lcd_options['d_pressure_sensor']: try: from plugins import pressure_monitor state_press = pressure_monitor.get_check_pressure() if state_press: result = ASCI_convert(_('Inactive')) else: result = ASCI_convert(_('Active')) except Exception: result = ASCI_convert(_('Not Available')) else: result = None elif index == 20: if lcd_options['d_water_tank_level']: result = ASCI_convert(_('Water Tank Level:')) else: result = None elif index == 21: if lcd_options['d_water_tank_level']: 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] except Exception: cm = -1 percent = 0 ping = -1 volume = 0 units = -1 if cm > 0 and units != -1: result = ASCI_convert( _('Level') + ' {}'.format(cm) + _('cm') + ' {}'.format(percent) + _('%') + ' {}'.format(int(volume))) if units: result += ASCI_convert(_('liter')) else: result += ASCI_convert(_('m3')) elif units == -1: result = ASCI_convert(_('Not Available')) else: result = ASCI_convert(_('Error - I2C Device Not Found!')) else: result = None elif index == 22: if lcd_options['d_temperature']: result = ASCI_convert(_('DS Temperature:')) else: result = None elif index == 23: if lcd_options['d_temperature']: try: from plugins import air_temp_humi air_options = air_temp_humi.plugin_options if air_options['ds_enabled']: air_result = '' for i in range(0, air_options['ds_used']): air_result += '{}:{} '.format( air_options['label_ds%d' % i], air_temp_humi.DS18B20_read_probe(i)) result = ASCI_convert(air_result) else: result = ASCI_convert(_('DS temperature not use')) except Exception: result = ASCI_convert(_('Not Available')) else: result = None elif index == 24: if lcd_options['d_running_stations']: result = ASCI_convert(_('Station running:')) else: result = None elif index == 25: if lcd_options['d_running_stations']: if get_active_state() == False: result = ASCI_convert(_('Nothing running')) else: result = ASCI_convert(get_active_state()) else: result = None elif index == 26: if lcd_options['d_sched_manu']: result = ASCI_convert(_('Control:')) else: result = None elif index == 27: if lcd_options['d_sched_manu']: if options.manual_mode: result = ASCI_convert(_('Manual mode')) else: result = ASCI_convert(_('Scheduler')) else: result = None elif index == 28: if lcd_options['d_syst_enabl']: result = ASCI_convert(_('Scheduler:')) else: result = None elif index == 29: if lcd_options['d_syst_enabl']: if options.scheduler_enabled: result = ASCI_convert(_('Enabled')) else: result = ASCI_convert(_('Disabled')) else: result = None ### OSPy sensors ### elif index == 30: if lcd_options['d_sensors']: result = ASCI_convert(_('Sensors:')) else: result = None elif index == 31: if lcd_options['d_sensors']: try: if sensors.count() > 0: sensor_result = '' for sensor in sensors.get(): if sensor.enabled: if sensor.response == 1: sensor_result += sensor.name + ': ' if sensor.sens_type == 1: # dry contact if sensor.last_read_value[4] == 1: sensor_result += _('Contact Closed') elif sensor.last_read_value[4] == 0: sensor_result += _('Contact Open') else: sensor_result += _('Probe Error') if sensor.sens_type == 2: # leak detector if sensor.last_read_value[5] != -127: sensor_result += '{}'.format( sensor.last_read_value[5] ) + ' ' + _('l/s') else: sensor_result += _('Probe Error') if sensor.sens_type == 3: # moisture if sensor.last_read_value[6] != -127: sensor_result += '{}'.format( sensor.last_read_value[6]) + _('%') else: sensor_result += _('Probe Error') if sensor.sens_type == 4: # motion if sensor.last_read_value[7] != -127: sensor_result += _( 'Motion Detected') if int( sensor.last_read_value[7] ) == 1 else _('No Motion') else: sensor_result += _('Probe Error') if sensor.sens_type == 5: # temperature if sensor.last_read_value[0] != -127: sensor_result += '{}'.format( sensor.last_read_value[0]) else: sensor_result += _('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 += '{}'.format( sensor.last_read_value[ sensor.multi_type]) else: sensor_result += _('Probe Error') if sensor.multi_type == 4: # multi dry contact if sensor.last_read_value[4] != -127: sensor_result += _( 'Contact Closed') if int( sensor.last_read_value[4] ) == 1 else _('Contact Open') else: sensor_result += _('Probe Error') if sensor.multi_type == 5: # multi leak detector if sensor.last_read_value[5] != -127: sensor_result += '{}'.format( sensor.last_read_value[5] ) + ' ' + _('l/s') else: sensor_result += _('Probe Error') if sensor.multi_type == 6: # multi moisture if sensor.last_read_value[6] != -127: sensor_result += '{}'.format( sensor.last_read_value[6] ) + ' ' + _('%') else: sensor_result += _('Probe Error') if sensor.multi_type == 7: # multi motion if sensor.last_read_value[7] != -127: sensor_result += _( 'Motion Detected') if int( sensor.last_read_value[7] ) == 1 else _('No Motion') else: sensor_result += _('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 += '{}'.format( get_level) + ' ' + _( 'cm') + ' (' + '{}'.format( get_perc) + ' %)' else: sensor_result += _('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 += _('Probe Error') else: sensor_result += sensor.name + ': ' + _( 'No response!') else: sensor_result += sensor.name + ': ' + _('Disabled') if sensors.count() > 1: sensor_result += ', ' result = ASCI_convert(sensor_result) else: result = ASCI_convert(_('No sensors available')) except Exception: result = ASCI_convert(_('Not Available')) else: result = None return result
def run(self): last_rain = False body = "" 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 += '<b>' + _('System') + '</b> ' + datetime_string() body += '<br><p style="color:red;">' + _('System was powered on.') + '</p>' if email_options["emllog"]: file_exists = os.path.exists(EVENT_FILE) if file_exists: self.try_mail(body, EVENT_FILE) else: body += '<br>' + _('Error - events.log file not exists!') #print body self.try_mail(body) else: self.try_mail(body) while not self._stop.is_set(): body = "" try: # Send E-amil if rain is detected if email_options["emlrain"]: if inputs.rain_sensed() and not last_rain: body += '<b>' + _('System') + '</b> ' + datetime_string() body += '<br><p style="color:red;">' + _('System detected rain.') + '</p>' 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: for run in finished[finished_count:]: duration = (run['end'] - run['start']).total_seconds() minutes, seconds = divmod(duration, 60) body += '<b>' + _('System') + '</b> ' + datetime_string() body += '<br><b>' + _('Finished run') + '</b>' 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' % (minutes, seconds) 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!') body += '<br><b>' + _('Water') + '</b>' body += '<br>' + _('Water level in tank') + ': %s \n' % (cm) except Exception: pass result = None try: from plugins import air_temp_humi body += '<br><b>' + _('Temperature DS1-DS6') + '</b>' for i in range(0, air_temp_humi.plugin_options['ds_used']): body += '<br>' + u'%s' % air_temp_humi.plugin_options['label_ds%d' % i] + ': ' + u'%.1f \u2103' % air_temp_humi.DS18B20_read_probe(i) + '\n' except Exception: pass self.try_mail(body) self._sleep(1) finished_count = len(finished) self._sleep(1) except Exception: log.error(NAME, _('E-mail plug-in') + ':\n' + traceback.format_exc()) self._sleep(60)