def GET(self): qdict = web.input() try: ddict = json.loads(qdict['data']) except: raise web.unauthorized() force_reboot = False info = {} for l in ['wlog', 'elog', 'slog', 'evlog']: if l in ddict and ddict[l]: end = ddict['end_date'] days = ddict['days_before'] start_after = ddict['start_after'] prefix = '' try: if l in ['slog', 'evlog']: prefix = 'sensors/' + gv.plugin_data['ld'][int(ddict['ldi'])]['name'] + '/logs/' except: pass records = read_log(prefix+l, end, days, start_after) info[l] = records for dlog in ['dlogirricloud', 'dlogirricloud_monitor', 'dlogirricloud_net_config']: if dlog in ddict and ddict[dlog]: records = [] try: with gv.log_lock: with open('./logs/' + dlog[4:] + '.out', 'r') as file: records = file.readlines() except: pass info[dlog] = [] for r in records: info[dlog].append(r) if 'lrun' in ddict and ddict['lrun']: info['lrun'] = gv.lrun if 'programs' in ddict: if ddict['programs'] == 1: info['programs'] = gv.pd else: gv.pd = ddict['programs'] jsave(gv.pd, 'programs') info['programs'] = 1 if 'ps' in ddict and ddict['ps']: info['ps'] = gv.ps if 'sbits' in ddict and ddict['sbits']: info['sbits'] = gv.sbits if 'srvals' in ddict and ddict['srvals']: info['srvals'] = gv.srvals if 'sensors' in ddict and 'ld' in gv.plugin_data: if ddict['sensors'] == 1: info['sensors'] = [] try: if 'ldi' not in ddict or int(ddict['ldi']) == -1: info['sensors'] = gv.plugin_data['ld'] else: info['sensors'] = gv.plugin_data['ld'][int(ddict['ldi'])] except: pass info['sensboards'] = i2c.get_vsb_boards().keys() info['remotesensboards'] = get_remote_sensor_boards() else: try: if 'ldi' not in ddict or int(ddict['ldi']) == -1: gv.plugin_data['ld'] = ddict['sensors'] else: gv.plugin_data['ld'][int(ddict['ldi'])] = ddict['sensors'] except: gv.plugin_data['ld'] = ddict['sensors'] jsave(gv.plugin_data['ld'], 'sensors') info['sensors'] = 1 if 'camera' in ddict and 'ca' in gv.plugin_data: if ddict['camera'] == 1: info['camera'] = gv.plugin_data['ca'] if 'cai' in ddict and ddict['cai']: info['cai'] = '' if gv.plugin_data['ca']['enable_camera'] == 'on': try: with open('./static/images/camera.jpg', mode='rb') as file: # b is important -> binary info['cai'] = base64.b64encode(file.read()) except: pass else: gv.plugin_data['ca'] = ddict['camera'] jsave(gv.plugin_data['ca'], 'camera') info['camera'] = 1 if 'sd' in ddict: if ddict['sd'] == 1: sd = gv.sd.copy() del sd['substation_network'] del sd['salt'] del sd['password'] del sd['pwd'] del sd['enable_upnp'] # stuff from base configuration stays as was del sd['subnet_only_substations'] del sd['external_proxy_port'] kill_keys = [] for k,v in sd.iteritems(): if len(k) > 2 and (k[0:2] == 'te' or k[0:2] == 'et'): kill_keys.append(k) for k in kill_keys: del sd[k] # dont send textemail or et_weather stuff info['sd'] = sd else: for field in ddict['sd']: gv.sd[field] = ddict['sd'][field] update_hostname(gv.sd['name']) update_tza(gv.sd['tza']) jsave(gv.sd, 'sd') info['sd'] = 1 force_reboot = True if 'snames' in ddict: if ddict['snames'] == 1: info['snames'] = gv.snames else: gv.snames = ddict['snames'] jsave(gv.snames, 'snames') info['snames'] = 1 if 'snotes' in ddict: if ddict['snotes'] == 1: info['snotes'] = gv.snotes else: gv.snotes = ddict['snotes'] jsave(gv.snotes, 'snotes') info['snotes'] = 1 if 'update_status' in ddict and ddict['update_status']: updatechecker.update_rev_data() info['update_status'] = updatechecker.status web.header('Content-Type', 'application/json') ret_str = json.dumps(info) if force_reboot: reboot(5) # give a few seconds to reply return ret_str
def timing_loop(): """ ***** Main timing algorithm. Runs in a separate thread.***** """ last_min = 0 zc = 0 supply_temp_readings = [] return_temp_readings = [] # last_mode = gv.sd['mode'] last_mode = 'Invalid Mode' # force intialization last_temp_log = 0 failed_temp_read = 0 last_dewpoint_adjust = 0 # Log the image and all the vsb board fw try: with open('data/version', 'r') as f: image_version = f.read() gv.logger.info('Image version: ' + image_version) except: pass boards = i2c.get_vsb_boards() for board, version in boards.items(): gv.logger.info('VSB Firmware for board: ' + str(board) + ' value: ' + hex(version)) for delay in range(15): time.sleep(1) # wait for ip addressing to settle but keep updating time gv.nowt = time.localtime() # Current time as time struct. Updated once per second. gv.now = timegm(gv.nowt) # Current time as timestamp based on local time from the Pi. Updated once per second. start_time = gv.now check_and_update_upnp() last_upnp_refresh = gv.now # one_way_cooling_adjustments tracks the direction of the last valve change and how many changes we have made # in that direction without going the other direction. This stops us from constantly moving the valve when the # heatpump is off or we cannot achieve our target. one_way_cooling_adjustments = 0 last_ave_supply_temp = None # force everything off set_output() while True: # infinite loop time.sleep(1) gv.nowt = time.localtime() # Current time as time struct. Updated once per second. gv.now = timegm(gv.nowt) # Current time as timestamp based on local time from the Pi. Updated once per second. # perform once per minute processing if gv.now // 60 != last_min: # only check programs once a minute last_min = gv.now // 60 update_radio_present() max_bd = -1 boards = i2c.get_vsb_boards() for bd, version in boards.items(): if bd not in gv.in_bootloader: try: max_bd = max(max_bd, bd) v = i2c_read(i2c.ADDRESS+bd, 0xc) # verify scratch value is as expected. Acts as a touch of the vsb too! if v != bd+16: gv.logger.critical('Main bad scratch value on board: ' + str(bd) + ' value: ' + str(v)) i2c_write(i2c.ADDRESS+bd, 0xc, bd+16) # write scratch register as keepalive except: gv.logger.critical('Cant access scratch register on board: ' + str(bd)) pass # read deadman debug register ignoring all errors. try: v = i2c_read(i2c.ADDRESS+bd, 0xd) if v != 0: gv.logger.critical('Deadman register triggered on board: ' + str(bd) + ' value: ' + str(v)) except: pass cur_bd = (gv.sd['nst']-gv.sd['radiost'])//8 if max_bd+1 > cur_bd: # ensure at nbrd captures all attached VSMs gv.logger.info('Changing nbrd based on attached boards: ' + str(max_bd+1)) adjust_gv_nbrd(max_bd+1) gv.sd['nst'] += 8*(max_bd+1-cur_bd) jsave(gv.sd, 'sd') cur_ip = get_ip() ext_ip_addr = get_external_ip() if gv.sd['master'] and (ext_ip_addr != gv.external_ip or cur_ip != gv.last_ip): gv.external_ip = ext_ip_addr try: # send email if ip addressing changed if gv.sd['teipchange'] and gv.plugin_data['te']['tesender']: subject = "Report from Irricloud" body = 'IP change. Local IP: ' + cur_ip if gv.sd['htp'] != 0 and gv.sd['htp'] != 80: body += ':' + str(gv.sd['htp']) body += ' External IP: ' + ext_ip_addr if gv.sd['external_htp'] != 0: body += ':' + str(gv.sd['external_htp']) gv.plugin_data['te']['tesender'].try_mail(subject, body) except: pass if cur_ip != gv.last_ip: gv.logger.info('IP changed from: ' + gv.last_ip + ' to: ' + cur_ip) gv.last_ip = cur_ip if cur_ip != "No IP Settings": # find router, set up upnp port mapping check_and_update_upnp(cur_ip) last_upnp_refresh = gv.now if gv.sd['upnp_refresh_rate'] > 0 and \ (gv.now-last_upnp_refresh)//60 >= gv.sd['upnp_refresh_rate']: check_and_update_upnp(cur_ip) last_upnp_refresh = gv.now process_actions() last_zc = zc zc = read_sensor_value('zone_call') if zc == None: zc = last_zc log_event('Failed to read zone_call') boiler_md = get_boiler_mode() heatpump_md = get_heatpump_mode() if gv.sd['mode'] != last_mode: # turn everything off log_event('change mode. Turn off boiler, heatpump, circ_pump') if boiler_md != 'none': set_boiler_mode('none') if heatpump_md != 'none': set_heatpump_mode('none') gv.srvals[circ_pump] = 0 set_output() last_zc = 0 # mark as was off last_mode = gv.sd['mode'] remove_action({'what':'set_valve_change'}) if gv.sd['mode'] in ['Heatpump Cooling'] or \ (gv.sd['mode'] in ['Boiler Only'] and not boiler_through_buffer_tank): # use only return water insert_action(gv.now, {'what':'set_valve_change', 'valve_change_percent':-100}) else: insert_action(gv.now, {'what':'set_valve_change', 'valve_change_percent':100}) try: temps = read_temps() failed_temp_read = 0 # rather than tracking serial # of thermistors, just assume higher readings are supply # and cooler readings are return (if heating) and vice versa if cooling min_temp = min(temps) max_temp = max(temps) if min_temp < 0 or max_temp < 0: log_event('Bad min/max temps. min: ' + str(min_temp) + ' max: ' + str(max_temp)) if gv.sd['mode'] == 'Heatpump Cooling': supply_temp_readings.append(min_temp) return_temp_readings.append(max_temp) else: supply_temp_readings.append(max_temp) return_temp_readings.append(min_temp) except: if gv.now - start_time > 120: # let things start up before capturing errors failed_temp_read += 1 if failed_temp_read < 300: if failed_temp_read % 10 == 1: # first exception should get cleared by reconnect and is normal log_event('cant read temperatures. Failcount: ' + str(failed_temp_read)) elif failed_temp_read == 300: log_event('TEMPERATURE SENSOR FAILURE') email('Heating', 'TEMPERATURE SENSOR FAILURE') elif failed_temp_read % 300 == 0: log_event('Ongoing temp failure. Failcount: ' + str(failed_temp_read)) if len(supply_temp_readings) > 5: supply_temp_readings.pop(0) if len(return_temp_readings) > 5: return_temp_readings.pop(0) try: ave_supply_temp = sum(supply_temp_readings)/float(len(supply_temp_readings)) if ave_supply_temp < 0: log_event('Bad ave_supply_temp: ' + str(ave_supply_temp)) except ZeroDivisionError: ave_supply_temp = -1 try: ave_return_temp = sum(return_temp_readings)/float(len(return_temp_readings)) if ave_return_temp < 0: log_event('Bad ave_return_temp: ' + str(ave_return_temp)) except ZeroDivisionError: ave_return_temp = -1 if gv.now - last_temp_log >= 600: last_temp_log = gv.now ast_c = ave_supply_temp ast_f = ast_c*1.8 + 32 art_c = ave_return_temp art_f = art_c*1.8 + 32 dew_f = dew*1.8 + 32 log_event('supply temp: ' + "{0:.2f}".format(ast_c) + 'C ' + "{0:.2f}".format(ast_f) + 'F' + '; ' + \ 'return temp: ' + "{0:.2f}".format(art_c) + 'C ' + "{0:.2f}".format(art_f) + 'F' + '; ' + \ 'dewpoint: ' + "{0:.2f}".format(dew) + 'C ' + "{0:.2f}".format(dew_f) + 'F') if zc != last_zc: # change in zone call if gv.sd['mode'] == 'None': zc = last_zc # dont do anything in terms of moving water elif last_zc == 0: # was off, now on? supply_temp_readings = [] return_temp_readings = [] last_ave_supply_temp = None log_event('zone call on; enable circ pump') gv.srvals[circ_pump] = 1 set_output() # for cooling or boiler operation start with only return water. For heating, only buffer tank water if gv.sd['mode'] in ['Heatpump Cooling']: remove_action({'what':'set_valve_change'}) insert_action(gv.now, {'what':'set_valve_change', 'valve_change_percent':-100}) one_way_cooling_adjustments = 0 elif not boiler_through_buffer_tank: remove_action({'what':'set_valve_change'}) if gv.sd['mode'] in ['Boiler Only']: insert_action(gv.now, {'what':'set_valve_change', 'valve_change_percent':-100}) else: insert_action(gv.now, {'what':'set_valve_change', 'valve_change_percent':100}) if gv.sd['mode'] in ['Boiler Only', 'Boiler and Heatpump']: log_event('zone call on; enable boiler') set_boiler_mode('heating') else: # was on, now off msg_start = 'zone call off; ' gv.srvals[circ_pump] = 0 set_output() if boiler_md == 'heating' and \ gv.sd['mode'] in ['Boiler Only', 'Boiler and Heatpump', 'Heatpump then Boiler']: msg_start += 'disable boiler; ' set_boiler_mode('none') if heatpump_md == 'heating' and \ gv.sd['mode'] in ['Boiler and Heatpump', 'Heatpump then Boiler', 'Heatpump Only']: msg_start += 'disable heatpump; ' set_heatpump_mode('none') if heatpump_md == 'cooling' and gv.sd['mode'] == 'Heatpump Cooling': msg_start += 'disable heatpump; ' set_heatpump_mode('none') log_event(msg_start + 'supply: ' + "{0:.2f}".format(ave_supply_temp) + ' return: ' + "{0:.2f}".format(ave_return_temp)) elif zc == 1: # still on? if len(supply_temp_readings) < 5 or len(return_temp_readings) < 5: continue if gv.sd['mode'] in ['Heatpump Only', 'Boiler and Heatpump', 'Heatpump then Boiler']: if ave_supply_temp < heatpump_setpoint_h-5: if heatpump_md == 'none' and gv.now-last_heatpump_off > 3*60: # log_event('reenable heatpump; supply: ' + str(ave_supply_temp)) set_heatpump_mode('heating') if ave_supply_temp > heatpump_setpoint_h-2: if heatpump_md == 'heating' and gv.now-last_heatpump_on > 3*60: # log_event('disable heatpump; supply: ' + str(ave_supply_temp)) set_heatpump_mode('none') if gv.sd['mode'] == 'Heatpump then Boiler': if ave_supply_temp < heatpump_setpoint_h-13 or ave_return_temp < 33: if boiler_md == 'none' and gv.now-last_boiler_off > 2*60 and \ gv.now-last_heatpump_on > 3*60: log_event('reenable boiler; supply: ' + "{0:.2f}".format(ave_supply_temp) + ' return: ' + "{0:.2f}".format(ave_return_temp)) # Use only boiler for a while if not boiler_through_buffer_tank: remove_action({'what':'set_valve_change'}) insert_action(gv.now, {'what':'set_valve_change', 'valve_change_percent':-100}) set_heatpump_mode('none') set_boiler_mode('heating') insert_action(gv.now+45*60, {'what':'set_boiler_mode', 'mode':'none'}) if gv.sd['mode'] == 'Heatpump Cooling' and gv.now-last_dewpoint_adjust >= 60: dewpoint_margin = 1.5 target = max(dew+dewpoint_margin+1, 10.) adjust = 0 if ave_supply_temp <= dew+dewpoint_margin and one_way_cooling_adjustments > min_cooling_adjustments: remove_action({'what':'set_valve_change'}) insert_action(gv.now, {'what':'set_valve_change', 'valve_change_percent':-100}) msg = 'Close valve; avoid condensation' log_event(msg + ': ' + "{0:.2f}".format(dew) + 'C target: ' + "{0:.2f}".format(target) + 'C supply: ' + "{0:.2f}".format(ave_supply_temp) + 'C') last_dewpoint_adjust = gv.now last_ave_supply_temp = None one_way_cooling_adjustments = min_cooling_adjustments elif target < ave_supply_temp - .1: remove_action({'what':'set_valve_change'}) adjust = 0 if one_way_cooling_adjustments < 0: one_way_cooling_adjustments = 0 adjust += 2 adjust += cooling_adjust_per_degree * (ave_supply_temp - target) if last_ave_supply_temp != None and last_ave_supply_temp - ave_supply_temp > 0 and \ gv.now-last_dewpoint_adjust <= 180: # already going down? Be patient new_adjust = adjust - 2*cooling_adjust_per_degree * (last_ave_supply_temp - ave_supply_temp) msg = 'already going down' gv.logger.debug(msg + ': ' + \ ' adjust: ' + "{0:.2f}".format(adjust) + \ ' new_adjust: ' + "{0:.2f}".format(new_adjust)) adjust = max(0, new_adjust) adjust = int(round(adjust)) msg = 'Ignoring request for more buffer tank water' if adjust > 0 and one_way_cooling_adjustments < max_cooling_adjustments: insert_action(gv.now, {'what':'set_valve_change', 'valve_change_percent':adjust}) last_dewpoint_adjust = gv.now last_ave_supply_temp = ave_supply_temp msg = 'More buffer tank water' elif target > ave_supply_temp + .1: remove_action({'what':'set_valve_change'}) adjust = 0 if one_way_cooling_adjustments > 0: one_way_cooling_adjustments = 0 adjust -= 2 adjust += cooling_adjust_per_degree * (ave_supply_temp - target) if last_ave_supply_temp != None and last_ave_supply_temp - ave_supply_temp < 0 and \ gv.now-last_dewpoint_adjust <= 180: # already going up? Be patient new_adjust = adjust - 2*cooling_adjust_per_degree * (last_ave_supply_temp - ave_supply_temp) msg = 'already going up' gv.logger.debug(msg + ': ' + \ ' adjust: ' + "{0:.2f}".format(adjust) + \ ' new_adjust: ' + "{0:.2f}".format(new_adjust)) adjust = min(0, new_adjust) adjust = int(round(adjust)) msg = 'Ignoring request for more return water' if adjust < 0 and one_way_cooling_adjustments > min_cooling_adjustments: insert_action(gv.now, {'what':'set_valve_change', 'valve_change_percent':adjust}) last_dewpoint_adjust = gv.now last_ave_supply_temp = ave_supply_temp msg = 'More return water' else: msg = 'Not changing valve' gv.logger.debug(msg + ': ' + str(one_way_cooling_adjustments) + \ ' dew: ' + "{0:.2f}".format(dew) + 'C' + \ ' target: ' + "{0:.2f}".format(target) + 'C' + \ ' supply: ' + "{0:.2f}".format(ave_supply_temp) + 'C') if (adjust > 0 and one_way_cooling_adjustments < max_cooling_adjustments) or \ (adjust < 0 and one_way_cooling_adjustments > min_cooling_adjustments): one_way_cooling_adjustments += adjust
def check_factory_reset(): """Check every five seconds to see if we need to do a factory reset""" while True: time.sleep(5) try: factory_reset = False i2c_reset = False try: # if factory_reset file is present or hw button is pushed. if pi.read(button_pin) == 0: factory_reset = True logger.warning('button factory reset') else: try: with open('data/factory_reset', 'r') as f: factory_reset = True logger.warning('file factory reset') if factory_reset: subprocess.call(['rm', 'data/factory_reset']) except: pass except Exception as ex: logger.warning('button read failure: ' + str(ex)) print 'checking factory reset', factory_reset if factory_reset: # Factory reset request logger.warning('factory reset') light_vsb_boards() try: type = '' usbs = subprocess.check_output(['lsusb']) usbl = usbs.split('\n') for usb in usbl: if 'Ralink Technology' in usb: type = 'CanaKit' if type == '' else 'Unknown' elif 'Realtek Semiconductor' in usb: type = 'TP' if type == '' else 'Unknown' if type != '' and type != 'Unknown': for file in [ '/etc/hostapd/hostapd.conf', '/usr/sbin/hostapd', '/etc/wpa_supplicant/functions.sh' ]: rc = subprocess.call( ['diff', file, file + '-' + type]) if rc == 0: logger.debug(file + ' and ' + file + '-' + type + ' match') else: logger.critical('Rebinding ' + file + ' to match ' + file + '-' + type) subprocess.call(['rm', file]) subprocess.call( ['ln', '-s', file + '-' + type, file]) else: logger.info( 'leaving hostapd and wpa_supplicant unchanged. type: ' + type) except Exception as ex: logger.exception( 'Could not validate/change hostapd and wpa_supplicant usage: ' + str(ex)) logger.info( 'restoring orig /etc/network/interfaces and /etc/wpa_supplicant/wpa_supplicant.conf' ) shutil.copy2('/etc/network/interfaces.orig', '/etc/network/interfaces') shutil.copy2('/etc/wpa_supplicant/wpa_supplicant.conf.orig', '/etc/wpa_supplicant/wpa_supplicant.conf') shutil.copy2('/etc/dhcpcd.conf.orig', '/etc/dhcpcd.conf') logger.info('starting sip_net_start') rc = subprocess.call( ['/usr/bin/python', '/home/pi/Irricloud/sip_net_start.py']) # leads to reboot logger.debug('sip_net_start rc: ' + str(rc)) time.sleep(10) else: try: with open('data/i2c_reset', 'r') as f: i2c_reset = True logger.warning('file i2c_reset') if i2c_reset: subprocess.call(['rm', 'data/i2c_reset']) except: pass print 'checking i2c reset', i2c_reset if i2c_reset: # Factory reset request boards = i2c.get_vsb_boards() for board, version in boards.items(): if board not in gv.in_bootloader: logger.warning('i2c reset board: ' + str(board + 1)) i2c.i2c_reset(i2c.ADDRESS + board) else: logger.critical('i2c reset aborted board: ' + str(board + 1) + ' in bootloader version: ' + hex(version)) logger.info('reboot.') reboot(0, True) except Exception as ex: logger.critical('check_factory_reset exception: ' + str(ex))
def timing_loop(): """ ***** Main timing algorithm. Runs in a separate thread.***** """ last_min = 0 zc = 0 supply_temp_readings = [] return_temp_readings = [] # last_mode = gv.sd['mode'] last_mode = 'Invalid Mode' # force intialization last_temp_log = 0 failed_temp_read = 0 failed_cold_supply = 0 last_dewpoint_adjust = 0 # Log the image and all the vsb board fw try: with open('data/version', 'r') as f: image_version = f.read() gv.logger.info('Image version: ' + image_version) except: pass boards = i2c.get_vsb_boards() for board, version in boards.items(): gv.logger.info('VSB Firmware for board: ' + str(board) + ' value: ' + hex(version)) for delay in range(15): time.sleep( 1) # wait for ip addressing to settle but keep updating time gv.nowt = time.localtime( ) # Current time as time struct. Updated once per second. gv.now = timegm( gv.nowt ) # Current time as timestamp based on local time from the Pi. Updated once per second. start_time = gv.now check_and_update_upnp() last_upnp_refresh = gv.now # one_way_cooling_adjustments tracks the direction of the last valve change and how many changes we have made # in that direction without going the other direction. This stops us from constantly moving the valve when the # heatpump is off or we cannot achieve our target. one_way_cooling_adjustments = 0 last_ave_supply_temp = None # force everything off set_output() while True: # infinite loop try: time.sleep(1) gv.nowt = time.localtime( ) # Current time as time struct. Updated once per second. gv.now = timegm( gv.nowt ) # Current time as timestamp based on local time from the Pi. Updated once per second. # perform once per minute processing if gv.now // 60 != last_min: # only check programs once a minute boiler_supply_crossover = gv.sd['boiler_supply_temp'] if gv.sd[ 'tu'] == 'C' else (gv.sd['boiler_supply_temp'] - 32) / 1.8 last_min = gv.now // 60 update_radio_present() max_bd = -1 boards = i2c.get_vsb_boards() for bd, version in boards.items(): if bd not in gv.in_bootloader: try: max_bd = max(max_bd, bd) v = i2c_read( i2c.ADDRESS + bd, 0xc ) # verify scratch value is as expected. Acts as a touch of the vsb too! if v != bd + 16: gv.logger.critical( 'Main bad scratch value on board: ' + str(bd) + ' value: ' + str(v)) i2c_write( i2c.ADDRESS + bd, 0xc, bd + 16) # write scratch register as keepalive except: gv.logger.critical( 'Cant access scratch register on board: ' + str(bd)) pass # read deadman debug register ignoring all errors. try: v = i2c_read(i2c.ADDRESS + bd, 0xd) if v != 0: gv.logger.critical( 'Deadman register triggered on board: ' + str(bd) + ' value: ' + str(v)) except: pass cur_bd = (gv.sd['nst'] - gv.sd['radiost']) // 8 if max_bd + 1 > cur_bd: # ensure at nbrd captures all attached VSMs gv.logger.info('Changing nbrd based on attached boards: ' + str(max_bd + 1)) adjust_gv_nbrd(max_bd + 1) gv.sd['nst'] += 8 * (max_bd + 1 - cur_bd) jsave(gv.sd, 'sd') cur_ip = get_ip() ext_ip_addr = get_external_ip() if gv.sd['master'] and (ext_ip_addr != gv.external_ip or cur_ip != gv.last_ip): gv.external_ip = ext_ip_addr try: # send email if ip addressing changed if gv.sd['teipchange'] and gv.plugin_data['te'][ 'tesender']: subject = "Report from Irricloud" body = 'IP change. Local IP: ' + cur_ip if gv.sd['htp'] != 0 and gv.sd['htp'] != 80: body += ':' + str(gv.sd['htp']) body += ' External IP: ' + ext_ip_addr if gv.sd['external_htp'] != 0: body += ':' + str(gv.sd['external_htp']) gv.plugin_data['te']['tesender'].try_mail( subject, body) except: log_event('ip change email send failed') if cur_ip != gv.last_ip: gv.logger.info('IP changed from: ' + gv.last_ip + ' to: ' + cur_ip) gv.last_ip = cur_ip if cur_ip != "No IP Settings": # find router, set up upnp port mapping check_and_update_upnp(cur_ip) last_upnp_refresh = gv.now if gv.sd['upnp_refresh_rate'] > 0 and \ (gv.now-last_upnp_refresh)//60 >= gv.sd['upnp_refresh_rate']: check_and_update_upnp(cur_ip) last_upnp_refresh = gv.now if gv.now % 60 == 0: gv.logger.info('timing_loop 0') process_actions() last_zc = zc zc = read_sensor_value('zone_call') if zc == None: zc = last_zc log_event('Failed to read zone_call') boiler_md = get_boiler_mode() heatpump_md = get_heatpump_mode() if gv.sd['mode'] != last_mode: # turn everything off log_event('change mode. Turn off boiler, heatpump, circ_pump') if boiler_md != 'none': set_boiler_mode('none') if heatpump_md != 'none': set_heatpump_mode('none') gv.srvals[circ_pump] = 0 set_output() last_zc = 0 # mark as was off last_mode = gv.sd['mode'] remove_action({'what': 'set_valve_change'}) if gv.sd['mode'] in ['Heatpump Cooling'] or \ (gv.sd['mode'] in ['Boiler Only'] and not boiler_through_buffer_tank): # use only return water insert_action(gv.now, { 'what': 'set_valve_change', 'valve_change_percent': -100 }) else: insert_action(gv.now, { 'what': 'set_valve_change', 'valve_change_percent': 100 }) try: temps = read_temps() failed_temp_read = 0 # rather than tracking serial # of thermistors, just assume higher readings are supply # and cooler readings are return (if heating) and vice versa if cooling min_temp = min(temps) max_temp = max(temps) if min_temp < 0 or max_temp < 0: log_event('Bad min/max temps. min: ' + str(min_temp) + ' max: ' + str(max_temp)) if gv.sd['mode'] == 'Heatpump Cooling': supply_temp_readings.append(min_temp) return_temp_readings.append(max_temp) else: supply_temp_readings.append(max_temp) return_temp_readings.append(min_temp) except: if gv.now - start_time > 120: # let things start up before capturing errors failed_temp_read += 1 if failed_temp_read < 300: if failed_temp_read % 10 == 1: # first exception should get cleared by reconnect and is normal log_event('cant read temperatures. Failcount: ' + str(failed_temp_read)) elif failed_temp_read == 300: log_event('TEMPERATURE SENSOR FAILURE') try: gv.plugin_data['te']['tesender'].try_mail( 'Heating', 'TEMPERATURE SENSOR FAILURE') except: log_event('temp sensor failure email send failed') elif failed_temp_read % 300 == 0: log_event('Ongoing temp failure. Failcount: ' + str(failed_temp_read)) if len(supply_temp_readings) > 5: supply_temp_readings.pop(0) if len(return_temp_readings) > 5: return_temp_readings.pop(0) try: ave_supply_temp = sum(supply_temp_readings) / float( len(supply_temp_readings)) if ave_supply_temp < 0: log_event('Bad ave_supply_temp: ' + str(ave_supply_temp)) except ZeroDivisionError: ave_supply_temp = -1 try: ave_return_temp = sum(return_temp_readings) / float( len(return_temp_readings)) if ave_return_temp < 0: log_event('Bad ave_return_temp: ' + str(ave_return_temp)) except ZeroDivisionError: ave_return_temp = -1 if gv.now - last_temp_log >= 600: last_temp_log = gv.now ast_c = ave_supply_temp ast_f = ast_c * 1.8 + 32 art_c = ave_return_temp art_f = art_c * 1.8 + 32 dew_f = dew * 1.8 + 32 log_event('supply temp: ' + "{0:.2f}".format(ast_c) + 'C ' + "{0:.2f}".format(ast_f) + 'F' + '; ' + \ 'return temp: ' + "{0:.2f}".format(art_c) + 'C ' + "{0:.2f}".format(art_f) + 'F' + '; ' + \ 'dewpoint: ' + "{0:.2f}".format(dew) + 'C ' + "{0:.2f}".format(dew_f) + 'F') if zc != last_zc: # change in zone call if gv.sd['mode'] == 'None': zc = last_zc # dont do anything in terms of moving water elif last_zc == 0: # was off, now on? supply_temp_readings = [] return_temp_readings = [] last_ave_supply_temp = None low_supply_count = 0 log_event('zone call on; enable circ pump') gv.srvals[circ_pump] = 1 set_output() # for cooling or boiler operation start with only return water. For heating, only buffer tank water if gv.sd['mode'] in ['Heatpump Cooling']: remove_action({'what': 'set_valve_change'}) insert_action( gv.now, { 'what': 'set_valve_change', 'valve_change_percent': -100 }) one_way_cooling_adjustments = 0 elif not boiler_through_buffer_tank: remove_action({'what': 'set_valve_change'}) if gv.sd['mode'] in ['Boiler Only']: insert_action( gv.now, { 'what': 'set_valve_change', 'valve_change_percent': -100 }) else: insert_action( gv.now, { 'what': 'set_valve_change', 'valve_change_percent': 100 }) if gv.sd['mode'] in ['Boiler Only', 'Boiler and Heatpump']: log_event('zone call on; enable boiler') set_boiler_mode('heating') else: # was on, now off msg_start = 'zone call off; ' gv.srvals[circ_pump] = 0 set_output() if boiler_md == 'heating' and \ gv.sd['mode'] in ['Boiler Only', 'Boiler and Heatpump', 'Heatpump then Boiler']: msg_start += 'disable boiler; ' set_boiler_mode('none') if heatpump_md == 'heating' and \ gv.sd['mode'] in ['Boiler and Heatpump', 'Heatpump then Boiler', 'Heatpump Only']: msg_start += 'disable heatpump; ' set_heatpump_mode('none') if heatpump_md == 'cooling' and gv.sd[ 'mode'] == 'Heatpump Cooling': msg_start += 'disable heatpump; ' set_heatpump_mode('none') log_event(msg_start + 'supply: ' + "{0:.2f}".format(ave_supply_temp) + ' return: ' + "{0:.2f}".format(ave_return_temp)) elif zc == 1: # still on? if len(supply_temp_readings) < 5 or len( return_temp_readings) < 5: continue if gv.sd['mode'] in [ 'Heatpump Only', 'Boiler and Heatpump', 'Heatpump then Boiler' ]: if ave_supply_temp < heatpump_setpoint_h - 8: if heatpump_md == 'none' and gv.now - last_heatpump_off > 3 * 60: # log_event('reenable heatpump; supply: ' + str(ave_supply_temp)) set_heatpump_mode('heating') if ave_supply_temp > heatpump_setpoint_h - 4: if heatpump_md == 'heating' and gv.now - last_heatpump_on > 3 * 60: # log_event('disable heatpump; supply: ' + str(ave_supply_temp)) set_heatpump_mode('none') if gv.sd['mode'] == 'Heatpump then Boiler': # if ave_supply_temp < heatpump_setpoint_h-13 or ave_return_temp < 32: if ave_supply_temp < boiler_supply_crossover: if low_supply_count <= 750: # about 12.5 mins. # Typically takes 300-450 seconds from low point to reach ok, and once starts trending up stays trending uo if low_supply_count % 150 == 0: log_event('low_supply: ' + str(low_supply_count) + ' supply: ' + "{0:.2f}".format(ave_supply_temp) + ' return: ' + "{0:.2f}".format(ave_return_temp)) low_supply_count += 1 elif boiler_md == 'none' and gv.now-last_boiler_off > 2*60 and \ gv.now-last_heatpump_on > 3*60: log_event('reenable boiler; supply: ' + "{0:.2f}".format(ave_supply_temp) + ' return: ' + "{0:.2f}".format(ave_return_temp)) # Use only boiler for a while if not boiler_through_buffer_tank: remove_action({'what': 'set_valve_change'}) insert_action( gv.now, { 'what': 'set_valve_change', 'valve_change_percent': -100 }) # try an hour to warm things up and allow defrost mode on hp to finish and rewarm tank last_on_min_gap = (gv.now - last_boiler_on) // 60 # gv.logger.info('last_on_min_gap: ' + str(last_on_min_gap) + ' now: ' + str(gv.now) + ' on: ' + str(last_boiler_on)) extra_min = 0 if last_on_min_gap >= 4 * 60 else 15 if last_on_min_gap >= 3 * 60 else 30 set_heatpump_mode('none') set_boiler_mode('heating') insert_action(gv.now + (extra_min + 59) * 60, { 'what': 'set_boiler_mode', 'mode': 'none' }) else: low_supply_count = 0 if gv.sd[ 'mode'] == 'Heatpump Cooling' and gv.now - last_dewpoint_adjust >= 60: dewpoint_margin = 1.5 target = max(dew + dewpoint_margin + 1, 10.) adjust = 0 if ave_supply_temp >= 20: failed_cold_supply += 1 if failed_cold_supply == 300: log_event('COLD SUPPLY WATER FAILURE') try: gv.plugin_data['te']['tesender'].try_mail( 'Cooling', 'COLD SUPPLY WATER FAILURE') except: log_event( 'cold supply water failure email send failed' ) elif failed_cold_supply % 300 == 0: log_event( 'Ongoing cold supply water failure. Failcount: ' + str(failed_cold_supply)) else: failed_cold_supply = 0 if ave_supply_temp <= dew + dewpoint_margin and one_way_cooling_adjustments > min_cooling_adjustments: remove_action({'what': 'set_valve_change'}) insert_action( gv.now, { 'what': 'set_valve_change', 'valve_change_percent': -100 }) msg = 'Close valve; avoid condensation' log_event(msg + ': ' + "{0:.2f}".format(dew) + 'C target: ' + "{0:.2f}".format(target) + 'C supply: ' + "{0:.2f}".format(ave_supply_temp) + 'C') last_dewpoint_adjust = gv.now last_ave_supply_temp = None one_way_cooling_adjustments = min_cooling_adjustments elif target < ave_supply_temp - .1: remove_action({'what': 'set_valve_change'}) adjust = 0 if one_way_cooling_adjustments < 0: one_way_cooling_adjustments = 0 adjust += 2 adjust += cooling_adjust_per_degree * ( ave_supply_temp - target) if last_ave_supply_temp != None and last_ave_supply_temp - ave_supply_temp > 0 and \ gv.now-last_dewpoint_adjust <= 180: # already going down? Be patient new_adjust = adjust - 2 * cooling_adjust_per_degree * ( last_ave_supply_temp - ave_supply_temp) msg = 'already going down' gv.logger.debug(msg + ': ' + \ ' adjust: ' + "{0:.2f}".format(adjust) + \ ' new_adjust: ' + "{0:.2f}".format(new_adjust)) adjust = max(0, new_adjust) adjust = int(round(adjust)) msg = 'Ignoring request for more buffer tank water' if adjust > 0 and one_way_cooling_adjustments < max_cooling_adjustments: insert_action( gv.now, { 'what': 'set_valve_change', 'valve_change_percent': adjust }) last_dewpoint_adjust = gv.now last_ave_supply_temp = ave_supply_temp msg = 'More buffer tank water' elif target > ave_supply_temp + .1: remove_action({'what': 'set_valve_change'}) adjust = 0 if one_way_cooling_adjustments > 0: one_way_cooling_adjustments = 0 adjust -= 2 adjust += cooling_adjust_per_degree * ( ave_supply_temp - target) if last_ave_supply_temp != None and last_ave_supply_temp - ave_supply_temp < 0 and \ gv.now-last_dewpoint_adjust <= 180: # already going up? Be patient new_adjust = adjust - 2 * cooling_adjust_per_degree * ( last_ave_supply_temp - ave_supply_temp) msg = 'already going up' gv.logger.debug(msg + ': ' + \ ' adjust: ' + "{0:.2f}".format(adjust) + \ ' new_adjust: ' + "{0:.2f}".format(new_adjust)) adjust = min(0, new_adjust) adjust = int(round(adjust)) msg = 'Ignoring request for more return water' if adjust < 0 and one_way_cooling_adjustments > min_cooling_adjustments: insert_action( gv.now, { 'what': 'set_valve_change', 'valve_change_percent': adjust }) last_dewpoint_adjust = gv.now last_ave_supply_temp = ave_supply_temp msg = 'More return water' else: msg = 'Not changing valve' gv.logger.debug(msg + ': ' + str(one_way_cooling_adjustments) + \ ' dew: ' + "{0:.2f}".format(dew) + 'C' + \ ' target: ' + "{0:.2f}".format(target) + 'C' + \ ' supply: ' + "{0:.2f}".format(ave_supply_temp) + 'C') if (adjust > 0 and one_way_cooling_adjustments < max_cooling_adjustments) or \ (adjust < 0 and one_way_cooling_adjustments > min_cooling_adjustments): one_way_cooling_adjustments += adjust except: gv.logger.exception('BUG')
def light_vsb_boards(delay=2): boards = i2c.get_vsb_boards() for board, version in boards.items(): if board not in gv.in_bootloader: address = i2c.ADDRESS + board blink_led(address, delay, 0)
def GET(self): qdict = web.input() try: ddict = json.loads(qdict['data']) except: raise web.unauthorized() force_reboot = False info = {} for l in ['wlog', 'elog', 'slog', 'evlog']: if l in ddict and ddict[l]: end = ddict['end_date'] days = ddict['days_before'] prefix = '' try: if l in ['slog', 'evlog']: prefix = 'sensors/' + gv.plugin_data['ld'][int(ddict['ldi'])]['name'] + '/logs/' except: pass records = read_log(prefix+l, end, days) info[l] = records for dlog in ['dlogirricloud', 'dlogirricloud_monitor', 'dlogirricloud_net_config']: if dlog in ddict and ddict[dlog]: records = [] try: with open('./logs/' + dlog[4:] + '.out', 'r') as file: records = file.readlines() except: pass info[dlog] = [] for r in records: info[dlog].append(r) if 'lrun' in ddict and ddict['lrun']: info['lrun'] = gv.lrun if 'programs' in ddict: if ddict['programs'] == 1: info['programs'] = gv.pd else: gv.pd = ddict['programs'] jsave(gv.pd, 'programs') info['programs'] = 1 if 'ps' in ddict and ddict['ps']: info['ps'] = gv.ps if 'sbits' in ddict and ddict['sbits']: info['sbits'] = gv.sbits if 'srvals' in ddict and ddict['srvals']: info['srvals'] = gv.srvals if 'sensors' in ddict and 'ld' in gv.plugin_data: if ddict['sensors'] == 1: info['sensors'] = [] try: if 'ldi' not in ddict or int(ddict['ldi']) == -1: info['sensors'] = gv.plugin_data['ld'] else: info['sensors'] = gv.plugin_data['ld'][int(ddict['ldi'])] except: pass info['sensboards'] = i2c.get_vsb_boards().keys() info['remotesensboards'] = get_remote_sensor_boards() else: try: if 'ldi' not in ddict or int(ddict['ldi']) == -1: gv.plugin_data['ld'] = ddict['sensors'] else: gv.plugin_data['ld'][int(ddict['ldi'])] = ddict['sensors'] except: gv.plugin_data['ld'] = ddict['sensors'] jsave(gv.plugin_data['ld'], 'sensors') info['sensors'] = 1 if 'camera' in ddict and 'ca' in gv.plugin_data: if ddict['camera'] == 1: info['camera'] = gv.plugin_data['ca'] if 'cai' in ddict and ddict['cai']: info['cai'] = '' if gv.plugin_data['ca']['enable_camera'] == 'on': try: with open('./static/images/camera.jpg', mode='rb') as file: # b is important -> binary info['cai'] = base64.b64encode(file.read()) except: pass else: gv.plugin_data['ca'] = ddict['camera'] jsave(gv.plugin_data['ca'], 'camera') info['camera'] = 1 if 'sd' in ddict: if ddict['sd'] == 1: sd = gv.sd.copy() del sd['substation_network'] del sd['salt'] del sd['password'] del sd['pwd'] del sd['enable_upnp'] # stuff from base configuration stays as was del sd['subnet_only_substations'] del sd['external_proxy_port'] kill_keys = [] for k,v in sd.iteritems(): if len(k) > 2 and (k[0:2] == 'te' or k[0:2] == 'et'): kill_keys.append(k) for k in kill_keys: del sd[k] # dont send textemail or et_weather stuff info['sd'] = sd else: for field in ddict['sd']: gv.sd[field] = ddict['sd'][field] update_hostname(gv.sd['name']) update_tza(gv.sd['tza']) jsave(gv.sd, 'sd') info['sd'] = 1 force_reboot = True if 'snames' in ddict: if ddict['snames'] == 1: info['snames'] = gv.snames else: gv.snames = ddict['snames'] jsave(gv.snames, 'snames') info['snames'] = 1 if 'snotes' in ddict: if ddict['snotes'] == 1: info['snotes'] = gv.snotes else: gv.snotes = ddict['snotes'] jsave(gv.snotes, 'snotes') info['snotes'] = 1 if 'update_status' in ddict and ddict['update_status']: updatechecker.update_rev_data() info['update_status'] = updatechecker.status web.header('Content-Type', 'application/json') ret_str = json.dumps(info) if force_reboot: reboot(5) # give a few seconds to reply return ret_str
def timing_loop(): """ ***** Main timing algorithm. Runs in a separate thread.***** """ last_min = 0 last_day = 0 last_master_station_running = 0 master_turn_on = 0 mton_delay = 0 gv.sd['bsy'] = 0 check_and_update_upnp() last_upnp_refresh = gv.now # Log the image and all the vsb board fw try: with open('data/version', 'r') as f: image_version = f.read() gv.logger.info('Image version: ' + image_version) except: pass boards = i2c.get_vsb_boards() for board, version in boards.items(): gv.logger.info('VSB Firmware for board: ' + str(board) + ' value: ' + hex(version)) for delay in range(15): time.sleep(1) # wait for ip addressing to settle but keep updating time gv.nowt = time.localtime() # Current time as time struct. Updated once per second. gv.now = timegm(gv.nowt) # Current time as timestamp based on local time from the Pi. Updated once per second. for i, p in enumerate(gv.pd): # get future instances of programs that recur but started earlier in the day if prog_match(p, True): schedule_recurring_instances(i) ip_countdown = 2 while True: # infinite loop gv.nowt = time.localtime() # Current time as time struct. Updated once per second. gv.now = timegm(gv.nowt) # Current time as timestamp based on local time from the Pi. Updated once per second. if gv.sd['en'] and not gv.sd['mm']: if gv.now // 60 != last_min: # only check programs once a minute last_min = gv.now // 60 update_radio_present() max_bd = -1 boards = i2c.get_vsb_boards() for bd, version in boards.items(): if bd not in gv.in_bootloader: try: max_bd = max(max_bd, bd) v = i2c_read(i2c.ADDRESS+bd, 0xc) # verify scratch value is as expected. Acts as a touch of the vsb too! if v != bd+16: gv.logger.critical('Main bad scratch value on board: ' + str(bd) + ' value: ' + str(v)) i2c_write(i2c.ADDRESS+bd, 0xc, bd+16) # write scratch register as keepalive except: gv.logger.critical('Cant access scratch register on board: ' + str(bd)) pass # read deadman debug register ignoring all errors. try: v = i2c_read(i2c.ADDRESS+bd, 0xd) if v != 0: gv.logger.critical('Deadman register triggered on board: ' + str(bd) + ' value: ' + str(v)) except: pass cur_bd = (gv.sd['nst']-gv.sd['radiost'])//8 if max_bd+1 > cur_bd: # ensure at nbrd captures all attached VSMs gv.logger.info('Changing nbrd based on attached boards: ' + str(max_bd+1)) adjust_gv_nbrd(max_bd+1) gv.sd['nst'] += 8*(max_bd+1-cur_bd) jsave(gv.sd, 'sd') cur_ip = get_ip() ext_ip_addr = get_external_ip() ip_countdown = 2 if cur_ip == gv.last_ip else ip_countdown-1 # when establishing bridge, wlan0 appears to drop then come back....just take a couple of iterations to # be sure we have an ip change. if ip_countdown == 0: ip_countdown = 2 # reset if gv.sd['master'] and (ext_ip_addr != gv.external_ip or cur_ip != gv.last_ip): gv.external_ip = ext_ip_addr try: # send email if ip addressing changed if gv.sd['teipchange'] and gv.plugin_data['te']['tesender']: subject = "Report from Irricloud" body = 'IP change. Local IP: ' + cur_ip if gv.sd['htp'] != 0 and gv.sd['htp'] != 80: body += ':' + str(gv.sd['htp']) body += ' External IP: ' + ext_ip_addr if gv.sd['external_htp'] != 0: body += ':' + str(gv.sd['external_htp']) gv.plugin_data['te']['tesender'].try_mail(subject, body) except: pass if cur_ip != gv.last_ip: gv.logger.info('IP changed from: ' + gv.last_ip + ' to: ' + cur_ip) gv.last_ip = cur_ip if cur_ip != "No IP Settings": # find router, set up upnp port mapping check_and_update_upnp(cur_ip) last_upnp_refresh = gv.now if gv.sd['upnp_refresh_rate'] > 0 and \ (gv.now-last_upnp_refresh)//60 >= gv.sd['upnp_refresh_rate']: check_and_update_upnp(cur_ip) last_upnp_refresh = gv.now cur_day = gv.now//86400 if cur_day != last_day: for i in range(gv.sd['nst']): for j in range(len(gv.rs[i])-1,0,-1): if gv.rs[i][j]['rs_stop_sec'] < gv.now: gv.logger.critical('Left over data on gv.rs['+str(i)+']') del gv.rs[i][j] elif gv.rs[i][j]['rs_stop_sec'] > gv.now + 86400: # indefinite program? gv.rs[i][j]['rs_stop_sec'] += 86400 # keep extending update_rs_order(i) last_day = cur_day for i, p in enumerate(gv.pd): # get both index and prog item if prog_match(p): gv.logger.debug('prog_match for program: ' + p[-1]) run_program(i) schedule_recurring_instances(i) for e in gv.recur[:]: # sorted by start times of recurring programs to run now_min = (gv.now%86400)//60 if e[0] <= now_min: try: gv.logger.debug('recur match for program: ' + gv.pd[e[1]][-1]) except: pass run_program(e[1]) # pid del gv.recur[0] # always deleting what is (now) first in list else: break if gv.sd['bsy']: with gv.rs_lock: program_running = False masid = gv.sd['mas']-1 for sid in range(gv.sd['nst']): # Check each station once a second b = sid >> 3 s = sid % 8 prog_id = gv.rs[sid][len(gv.rs[sid])-1]['rs_program_id'] p = None if prog_id >= len(gv.pd) else gv.pd[prog_id-1] if gv.now >= gv.rs[sid][len(gv.rs[sid])-1]['rs_stop_sec'] and len(gv.rs[sid]) > 1: # check if time is up stop_station(sid, **{'stop_only_current':1}) elif gv.rs[sid][len(gv.rs[sid])-1]['rs_start_sec'] <= gv.now < gv.rs[sid][len(gv.rs[sid])-1]['rs_stop_sec']: if (gv.sbits[b] & (1<<s)) == 0: # not yet displayed? duration = gv.rs[sid][len(gv.rs[sid])-1]['rs_stop_sec'] - gv.now gv.logger.info('start station display sid: ' + str(sid+1) + ' for: ' + to_relative_time(duration)) gv.sbits[b] |= 1 << s # Set display to on gv.ps[sid][0] = prog_id if gv.ps[sid][1] != duration: gv.logger.info('sip_loop ps_set: change ps sid: ' + str(sid+1) + ' from: ' + to_relative_time(gv.ps[sid][1])+ ' to: ' + to_relative_time(duration)) gv.ps[sid][1] = duration if p is None or (p[gv.p_flags]&2) == 0: # if not ban program gv.logger.info('turn on sid: ' + str(sid+1) + ' prog: ' + str(prog_id) + ' (ps set) dur: ' + to_relative_time(duration)) gv.srvals[sid] = 1 set_output() else: # already stopped what was running in schedule_stations gv.logger.info('turn on ban sid: ' + str(sid+1) + ' prog: ' + str(prog_id) + ' (ps set) dur: ' + to_relative_time(duration)) if sid == gv.sd['mas']-1: # when we turn on master, start mton countdown mton_delay = -gv.sd['mton'] elif gv.rs[sid][len(gv.rs[sid])-1]['rs_start_sec'] > gv.now and gv.ps[sid][0] == 0: duration = gv.rs[sid][len(gv.rs[sid])-1]['rs_stop_sec'] - gv.rs[sid][len(gv.rs[sid])-1]['rs_start_sec'] gv.ps[sid][0] = prog_id if gv.ps[sid][1] != duration: gv.logger.info('sip_loop ps_change: change ps sid: ' + str(sid+1) + ' from: ' + to_relative_time(gv.ps[sid][1]) + ' to: ' + to_relative_time(duration)) gv.ps[sid][1] = duration gv.logger.info('future: ' + to_relative_time(gv.rs[sid][len(gv.rs[sid])-1]['rs_start_sec']) + ' turn on sid: ' + str(sid+1) + ' prog: ' + str(prog_id) + ' (ps set) dur: ' + to_relative_time(duration)) if masid >= 0 and sid != masid and gv.srvals[sid] and gv.sd['mo'][b]&(1<<s): # Master settings last_master_station_running = gv.now if len(gv.rs[sid]) > 1: # if any station is scheduled or on program_running = True # delays may have screwed up master scheduling. # If a station requiring master is running and master is not started, start it. if masid >= 0 and gv.srvals[masid] == 0: if master_turn_on == 0 and last_master_station_running == gv.now: master_turn_on = last_master_station_running + gv.sd['mton'] if master_turn_on != 0 and master_turn_on <= gv.now: gv.logger.info('turn on master without prescheduling mton: ' + str(master_turn_on)) gv.sbits[masid>>3] |= 1 << (masid%8) # Set display to on gv.ps[masid][0] = 98 gv.ps[masid][1] = 0 gv.srvals[masid] = 1 set_output() master_turn_on = 0 # If no station requiring master is running and master is not stopped, stop it. if masid >= 0 and gv.srvals[masid] == 1: if mton_delay >= 0: mton_delay -= 1 if (mton_delay < 0 and # allow for windown where master starts early last_master_station_running < gv.now and ((gv.sd['mtoff'] <= 0 and last_master_station_running >= gv.now + gv.sd['mtoff']) or \ (gv.sd['mtoff'] > 0 and last_master_station_running + gv.sd['mtoff'] <= gv.now))): gv.logger.info('turn off master without prescheduling') stop_station(masid, **{'stop_active':1}) mton_delay = 0 else: program_running = True # give time for master to shut down if program_running: # todo check stop_onrain for ban programs if gv.sd['urs'] and gv.sd['rs']: # Stop stations if use rain sensor and rain detected. stop_onrain() # Clear schedule for stations that do not ignore rain. for sid in range(len(gv.rs)): # loop through program schedule (gv.ps) if (gv.sbits[sid>>3] & (1<<(sid%8))) != 0: # If station is on, decrement time remaining display if gv.ps[sid][1] < 86400: # dont reduce indefinite program if gv.ps[sid][1] == -1: gv.logger.critical('sip negative ps decrement sid: ' + str(sid+1) + ' to: ' + to_relative_time(gv.ps[sid][1]) + \ ' start: ' + to_relative_time(gv.rs[sid][len(gv.rs[sid])-1]['rs_start_sec']) + \ ' stop: ' + to_relative_time(gv.rs[sid][len(gv.rs[sid])-1]['rs_stop_sec'])) gv.ps[sid][1] -= 1 else: gv.logger.debug('sip_ indefinite program sid: ' + str(sid+1) + ' to: ' + to_relative_time(gv.ps[sid][1]) + \ ' start: ' + to_relative_time(gv.rs[sid][len(gv.rs[sid])-1]['rs_start_sec']) + \ ' stop: ' + to_relative_time(gv.rs[sid][len(gv.rs[sid])-1]['rs_stop_sec'])) pass if not program_running: gv.sd['bsy'] = 0 for sid in range(gv.sd['nst']): b = sid >> 3 s = sid % 8 if gv.srvals[sid]: gv.logger.critical('srval set with no program running sid: ' + str(sid+1)) if gv.sbits[b]&(1<<s): gv.logger.critical('sbits set with no program running sid: ' + str(sid+1)) if gv.ps[sid][0]: gv.logger.critical('ps[0] set with no program running sid: ' + str(sid+1)) if gv.ps[sid][1]: gv.logger.critical('ps[1] set with no program running sid: ' + str(sid+1)) if gv.sd['urs']: check_rain() # in helpers.py if gv.sd['rd'] and gv.now >= gv.sd['rdst']: # Check of rain delay time is up gv.sd['rd'] = 0 gv.sd['rdst'] = 0 # Rain delay stop time jsave(gv.sd, 'sd') new_now = timegm(time.localtime()) if new_now - gv.now == 0: # try to avoid drift time.sleep(1)
def check_factory_reset(): """Check every five seconds to see if we need to do a factory reset""" global config_mode while True: time.sleep(5) try: factory_reset = False i2c_reset = False try: # if factory_reset file is present or hw button is pushed. if pi.read(button_pin) == 0: factory_reset = True logger.warning('button factory reset') else: try: with open('data/factory_reset','r') as f: factory_reset = True logger.warning('file factory reset') if factory_reset: subprocess.call(['rm', 'data/factory_reset']) except: pass except Exception as ex: logger.warning('button read failure: ' + str(ex)) if factory_reset: # Factory reset request logger.warning('factory reset') light_vsb_boards() try: type = '' usbs = subprocess.check_output(['lsusb']) usbl = usbs.split('\n') for usb in usbl: if 'Ralink Technology' in usb: type = 'CanaKit' if type == '' else 'Unknown' elif 'Realtek Semiconductor' in usb: type = 'TP' if type == '' else 'Unknown' if type != '' and type != 'Unknown': for file in ['/etc/hostapd/hostapd.conf', '/usr/sbin/hostapd', '/etc/wpa_supplicant/functions.sh']: rc = subprocess.call(['diff', file, file+'-'+type]) if rc == 0: logger.debug(file + ' and ' + file+'-'+type + ' match') else: logger.critical('Rebinding ' + file + ' to match ' + file+'-'+type) subprocess.call(['rm', file]) subprocess.call(['ln', '-s', file+'-'+type, file]) else: logger.info('leaving hostapd and wpa_supplicant unchanged. type: ' + type) except Exception as ex: logger.exception('Could not validate/change hostapd and wpa_supplicant usage: ' + str(ex)) logger.info('restoring orig /etc/network/interfaces and /etc/wpa_supplicant/wpa_supplicant.conf') config_mode = True shutil.copy2('/etc/network/interfaces.orig', '/etc/network/interfaces') shutil.copy2('/etc/wpa_supplicant/wpa_supplicant.conf.orig', '/etc/wpa_supplicant/wpa_supplicant.conf') shutil.copy2('/etc/dhcpcd.conf.orig', '/etc/dhcpcd.conf') logger.info('starting sip_net_start') rc = subprocess.call(['/usr/bin/python', '/home/pi/Irricloud/sip_net_start.py']) # leads to reboot logger.debug( 'sip_net_start rc: ' + str(rc)) time.sleep(10) else: try: with open('data/i2c_reset','r') as f: i2c_reset = True logger.warning('file i2c_reset') if i2c_reset: subprocess.call(['rm', 'data/i2c_reset']) except: pass if i2c_reset: # Factory reset request boards = i2c.get_vsb_boards() for board, version in boards.items(): if board not in gv.in_bootloader: logger.warning('i2c reset board: ' + str(board+1)) i2c.i2c_reset(i2c.ADDRESS+board) else: logger.critical('i2c reset aborted board: ' + str(board+1) + ' in bootloader version: ' + hex(version)) logger.info('reboot.') reboot(0, True) except Exception as ex: logger.critical('check_factory_reset exception: ' + str(ex))